IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

Passer de l'heritage a la composition


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Homme Profil pro
    Helsinki
    Inscrit en
    Avril 2012
    Messages
    39
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Finlande

    Informations professionnelles :
    Activité : Helsinki
    Secteur : Enseignement

    Informations forums :
    Inscription : Avril 2012
    Messages : 39
    Par défaut Passer de l'heritage a la composition
    Bonjour,
    Je suis un débutant en C++ et je rencontre un probleme concernant le passage de l'héritage à la combinaison. Voilà mon probleme: j'ai les classes suivantes (Cube, Carre, Point) et pour chacune des figure, je dois efffectuer le calcul de sa surface et de son volume et notant que la Classe Cube aura une surface nulle et la classe Carre aura un volume nulle. Ce programme en bas qui est une partie de mon programme global illustre l'héritage que j'ai implémenter et qui marche selon mes attentes. Par contre, quand je veux changer l'héritage en combinaison en créant un objet de classe Carre dans le prototype de la classe Cube et en redéfinissant mon constructeur dans dans Cube.cpp, mon programme bloque (je suis dans dev C++) sans mesage d'erreur, car la console s'affiche et disparait presque automatiquement. Merci d'avance pour ceux qui vondront bien m'expliquer le passage de l'héritage à la combinaison que je voudrai faire.

    - Cube hérite de Carre
    - Carre hérite de Point
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     
    // Définition de la classe Point
    #ifndef CARRE_H
    #define CARRE_H
    #include "Point.h"
    #include <iostream>
     
    using namespace std;
     
    class Carre : public Point {
     
        friend std::ostream &operator<<(ostream &, const Carre &);
     
      public: 
        // Constructeur par défaut
        Carre(float cote = 0.0);
        virtual float aire() const;
        float getcote() const { return cote; }
     
        virtual void afficher() const; 
        virtual void afficherNomFigures() const {cout << "Carre: "; } 
     
      protected:
        float cote;
     
      };
    #endif

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // Définition des fonctions membres de la classe Carre
    #include <iostream>
    #include <iomanip>
    #include "Carre.h"
     
    using namespace std;
     
    Carre::Carre(float a) 
        {cote=a;} 
    // Appel du constructeur de la classe de base 
      float Carre::aire() const { return cote*cote; }
      void Carre::afficher() const 
      {
      cout << "Cote=" << cote;
      }
      std::ostream &operator<<(ostream &output, const Carre &r)
      { 
        r.afficher();
      return output;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
     
    // Définition de la classe Cube
    #ifndef CUBE_H
    #define CUBE_H
    #include "Carre.h"
    #include <iostream>
    using namespace std;
     
     
    class Cube : public Carre
    { 
        friend ostream &operator<<(ostream &, const Cube &); 
      public: 
        // Constructeur par défaut
        Cube(float x = 0.0);
        void setCote(float);
        //virtual float cote() const;
        virtual float aire() const;
        virtual float volume() const;
        virtual void afficherNomFigures() const { cout << "Cube: "; 
    }
    virtual void afficher() const; 
      private:  
      float cote1;
      };
    #endif [/QUOTE]
     
    [QUOTE]
    // Définition des fonctions membres et amies de la classe Cube
    #include <iostream> 
    #include <iomanip>
    #include "Cube.h"
    #include <iostream>
     
     
    Cube::Cube(float x) 
        : Carre(x) // Appel du constructeur de la classe de base 
        { setCote(x) ; }
        void Cube::setCote(float x) {cote = x;}
        float Cube::aire() const
        { 
        return Carre::aire() ;  // Aire de Cube
        }
        float Cube::volume() const { return Carre::aire() * cote ; }
        void Cube::afficher() const
        {
        cout << "Cote=" << cote;
        }
        std::ostream &operator<<(ostream &output, const Cube& c)
        {  
        c.afficher(); 
    return output; 
     
      }

  2. #2
    Membre émérite
    Homme Profil pro
    R&D imagerie 3D / prog embarquée
    Inscrit en
    Mars 2007
    Messages
    419
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : R&D imagerie 3D / prog embarquée
    Secteur : Santé

    Informations forums :
    Inscription : Mars 2007
    Messages : 419
    Par défaut
    Salut,

    Si je comprend bien, ton cube ne doit plus étendre carré, mais être composé d'un carré qui représente sa base.
    Nous parlons ici de composition et non d’agrégation. Donc, si on détruit ton cube, on détruit sa base.

    Voici un bout de code qui correspond à ces contraintes:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    // Définition de la classe Cube
    #ifndef CUBE_H
    #define CUBE_H
    #include "Carre.h"
    #include <iostream>
    using namespace std;
     
     
    class Cube
    { 
        friend ostream &operator<<(ostream &, const Cube &); 
     
        Carre m_cBase;
     
      public: 
        // Constructeur par défaut
        Cube(float x = 0.0);
        void setCote(float);
        //virtual float cote() const;
        virtual float aire() const;
        virtual float volume() const;
        virtual void afficherNomFigures() const { cout << "Cube: "}; 
    }
    virtual void afficher() const; 
      private:  
      float cote1;
      };
    #endif 
     
    // Définition des fonctions membres et amies de la classe Cube
    #include <iostream> 
    #include <iomanip>
    #include "Cube.h"
    #include <iostream>
     
     
    Cube::Cube(float x) 
        : m_cBase(x) // Appel du constructeur de la classe de base 
        {}
     
        void Cube::setCote(float x) {m_cBase.setCote(x);} // Carre::setCote() est à écrire, ou tu peux rendre Cube friend de Carre
        float Cube::aire() const
        { 
          return m_cBase.aire() ;  // Aire de Cube
        }
        float Cube::volume() const { return m_cBase.aire() * m_cBase.getcote(); }
        void Cube::afficher() const
        {
         cout << "Cote=" << m_cBase.getcote();
        }
        std::ostream &operator<<(ostream &output, const Cube& c)
        {  
        c.afficher(); 
    return output; 
     
      }
    Note que je n'ai pas corrigé les erreurs déjà présentes dans ton code (accolade etc...), mais je pense que ce bout de code devrait t'aider à comprendre comment ça marche.

    D'une façon générale, quand tu poste du code sur le forum essais de faire en sorte qu'il soit le plus propre possible, ça aide à te répondre. Dans ce cas ci, tu peux juste faire attention à ce qu'il soit compilable et correctement indenté.


    A+

  3. #3
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Conceptuellement parlant, il était déjà totalement aberrant de considérer qu'un carré puisse etre un point et qu'un cube puisse être un carré...

    On pourrait dire qu'un point est ton "unité de base", car c'est ce qui te permet de représenter des coordonnées.

    A ce titre, ta classe point a sémantique de valeur et ressemble plus ou moins à quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Point
    {
        public:
            Point(double x, double y, double z):x_(x),y_(y),z_(z){}
            double x() const { return x_; }
            double y() const { return y_; }
            double z() const { return z_; }
        private:
            double x_;
            double y_;
            double z_;
    };
    Éventuellement on pourrait envisager de dire que Carre et Cube sont tous deux des ... Formes, mais

    1. En tant que classe parent, une classe éventuelle Forme ne peut recouvrir que ce qui est commun à l'ensemble des formes géométriques. (*)
    2. Il faut sans doute s'intéresser à la relation rectangle <--> carré(**)
    3. Il faut sans doute s'intéresser à la relation parallélipipède rectangle <--> cube(**)
    4. On peut se demander s'il est vraiment intéressant de pouvoir mélanger des volumes et des surfaces, et, surtout, de ne pouvoir manipuler les volumes qu'avec une interface réduite au point de ne pouvoir les manipuler, au mieux que comme des surfaces.

    (*) En vertu du principe de substitution de Liskov, toute propriété valide (comprend: tout ce qui fait partie de l'interface publique) d'une classe de base doit être une propriété valide pour la classe dérivée.

    Or, il n'y a, purement et simplement, aucun sens de considérer que la propriété "volume" d'une classe "figure plate" (comme le carré) soit une propriété valide.

    Au mieux, pourrait-on envisager d'avoir une classe intercalaire entre "Figure" et Carré (respectivement Cube) qui serait de l'ordre de "Surface" (respectivement "Volume)

    (**) Je vais peut etre beaucoup trop loin dans la réflexion car tu n'as peut etre à travailler qu'avec des carrés et des cubes, mais, bien que l'on t'apprenne dés la primaire qu'un carré est un rectangle spécial en cela que la hauteur est égale à la largeur, on ne peut décemment pas envisager de faire hériter une classe "carré" d'une classe "rectangle"...

    J'ai expliquer pourquoi dans cette discussion, et j'y ai même proposer deux solutions alternatives

    Bien que cette discussion portait sur la relation rectangle<--> carré, il est bien évident que la même réflexion doit être suivie pour la relation cube<-->parallélépipède

    Enfin, il ne faut pas oublier que, sémantiquement parlant, il est vraiment difficile de soutenir qu'un cube EST-UN carré...

    On ne peut donc pas envisager de faire hériter Cube de Carre, bien que tout ce qui est possible avec un carré le soit également avec un cube, et que toute l'interface publique d'un carré puisse se retrouver dans l'interface publique d'un cube

    Mais, bon, réfléchissons un peu à ta classe Carre, en considérant que nous pourrions la faire hériter d'une classe "Surface" qui fournit une interface permettant (des fonctions publiques virtuelles pures) d'obtenir le périmètre et la superficie. (cf plus haut pour les raisons qui font que l'on ne peut considérer que ces deux fonctions comme propriété valide)

    Un carré est, typiquement, représenté par ses quatre coins, mais il est tout à fait possible de ne le représenter qu'avec deux : le coin supérieur gauche et le coin inférieur droit.

    Dans le pire des cas, il peut suffire de "normaliser" ces deux points, mais il faudra aussi (car cela fait partie des invariants relatifs à un carré) veiller à ce que la distance verticale entre les deux points (la "hauteur") soit égale à la distance horizontale entre ces deux points (la "largeur").

    Les deux point "manquant" pourront, sans grosse difficulté, être déduits de ces deux point en prenant pour le point supérieur droit, la coordonnée X du point inférieur droit et la coordonnée Y de point supérieur gauche, et pour le coin inférieur gauche, la coordonnée X du coin supérieur gauche et la coordonnée Y du coin supérieur droit.


    Note, au passage, que cette manière d'envisager les choses peut s'appliquer à d'autres formes aussi

    Nous pourrions donc en arriver à quelque chose proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    class Surface
    {
        public:
            virtual double surface() const = 0;
            virtual double perimetre() const = 0;
            virtual ~Surface();
    };
    class Carre : public Surface
    {
        public:
            Carre(Point const & first, Point const & Second):
                topLeft_(first),bottomRight_(second)
            {
                normalize();
               assert( sideSize() == 
                       bottomRight_.y() - topLeft_.y() );
            }
            double sideSize() const
            {
                 return bottomRight_.x() - topLeft_.x();
            }
            virtual double surface() const
            {
                return sizeSize() * sideSize();
            }
            virtual double perimetre() const
            {
                return sizeSize() * 4;
            }
            virtual ~Carre(){}
        private:
            normalize()
            {
                double minX = std::min(topLeft_.x(), bottomRight_.x());
                double minY = std::min(topLeft_.y(), bottomRight_.y());
                double minZ = std::min(topLeft_.z(), bottomRight_.z());
                double maxX = std::max(topLeft_.x(), bottomRight_.x());
                double maxY = std::max(topLeft_.y(), bottomRight_.y());
                double maxZ = std::max(topLeft_.z(), bottomRight_.z());
     
                 topLeft_ = Point(minX, minY, minZ);
                 bottomRight_ = Point(maxX, maxY, maxZ);
            }
            Point topLeft_;
            Point bottomRight_;
    };
    Ta classe Cube ressemblerait sans doute très fort à la classe Carré, à ceci près qu'elle présenterait (en plus) une fonction volume(), qu'elle hériterait plutot d'une classe "Volume", et qu'elle prendrait, en outre, la coordonnée "Z" en compte.

    Mais, et j'insiste encore une fois là dessus, la classe Cube ne doit ni ne peut en aucun cas hériter de la classe carré
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  4. #4
    Membre éclairé
    Homme Profil pro
    Helsinki
    Inscrit en
    Avril 2012
    Messages
    39
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Finlande

    Informations professionnelles :
    Activité : Helsinki
    Secteur : Enseignement

    Informations forums :
    Inscription : Avril 2012
    Messages : 39
    Par défaut Résolu
    Merci djudju, ton aide a été précieuse et cela répond à ma question et du coup à résolu mon problème. Je suis conscient comme tu l'as souligné que mon code n'est pas si propre que cela, mais bon comme ça marque et comme je suis débutant, j'apprendai à me parfaire avec le temps.

    Pour Koala01, j’ai aussi compris tes explications techniques, ça m’a aussi aidé dans la réflexion. Je débute en C++ et les petites notions ne sont toujours pas évidentes pour moi.
    Merci à vous deux. S’il n’y a pas d’autres commentaires je reviendrai pour marquer résolu.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. composition vs heritage qu'en est-il ?
    Par kaljerhom dans le forum Scala
    Réponses: 1
    Dernier message: 22/06/2011, 18h24
  2. Réponses: 2
    Dernier message: 27/11/2009, 10h37
  3. combinaison de la composition et l'heritage
    Par ammah dans le forum Diagrammes de Classes
    Réponses: 2
    Dernier message: 13/04/2008, 21h29
  4. Heritage et composition
    Par foufa007 dans le forum Diagrammes de Classes
    Réponses: 5
    Dernier message: 06/04/2007, 19h17
  5. heritage et lien de composition
    Par slim dans le forum C++
    Réponses: 8
    Dernier message: 23/03/2005, 11h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo