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 :

conversion entre les classes et héritage.


Sujet :

C++

  1. #1
    Débutant  
    Inscrit en
    Novembre 2006
    Messages
    1 073
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 073
    Points : 217
    Points
    217
    Par défaut conversion entre les classes et héritage.
    Bonjour

    Je ne comprends pas pourquoi il n'est pas possible de convertir un objet d'une classe dérivée secondaire en une classe de base, lorsque cette classe dérivée secondaire hérite d'une classe dérivée primaire (qui elle-même hérite directement de la classe de base).

    Je m'explique avec un exemple:
    On a une classe B qui hérite de manière privée de A, et une classe C qui hérite de manière public de B.

    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
     
    class A{
    private:
    	int o;
    public:
    	A(int u):o(u){}
    	int g(){
    		return 1;
    	}
    };
     
    class B:private A{
    private:
    	int i;
    public:
    	B(int f,int k):A(k),i(f){}
    	int f(int o){
    		this->g();
    	};
    };
     
    class C:public B{
    private:
    	int i;
    public:	 
            //ctor pas défini ici.
    	int f(int o){
    	(this)->g();
    	};
    };
    On voit qu'il n'y a pas d'erreur avec la première fonction f(), mais il y a une erreur avec la fonction f de la classe C.
    Pourtant il est parfaitement possible de convertir un objet de la classe dérivée B en un objet de la classe dérivée A bien que l'héritage soit privé.
    Il est possible de convertir un objet de la classe dérivée C en un objet de la classe dérivée B, l'héritage étant public.

    Mais il n'y a pas de transitivité? Il n'est pas possible de convertir un objet C en un objet A.

    Que pourrait faire un dynamic_cast?


    De plus, j'ai cru comprendre que l'héritage privé signifiait qu'un objet d'une classe dérivée n'était pas du tout du type de la classe de base, mais ne pouvait profiter uniquement de l'implémentation.

    Or, la conversion n'est-elle pas contradictoire avec ce qui vient d'être dit, car l'objet converti sera en quelque sorte un objet de la classe de base.




    Je vous remercie
    Pierre

  2. #2
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    J'utilise peu l'héritage privé, mais il me semble que ce n'est pas vraiment à utiliser dans une hiérarchie d'héritage. Si B hérite de 1, B n'est *pas* une spécialisation de A. Ils utilisent A.

    Je trouve donc étrange que tu puisses convertir un objet de la classe B en un objet de la classe A. Car cela signifierait que des méthodes publiques devenues privées par l'héritage privé redeviendrait publiques.

  3. #3
    Débutant  
    Inscrit en
    Novembre 2006
    Messages
    1 073
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 073
    Points : 217
    Points
    217
    Par défaut
    Je trouve donc étrange que tu puisses convertir un objet de la classe B en un objet de la classe A. Car cela signifierait que des méthodes publiques devenues privées par l'héritage privé redeviendrait publiques.
    je trouve cela étrange aussi. Mais regarde ici:

    http://msdn.microsoft.com/en-us/library/5bw4yh4d.aspx

    Conversion to a private base class type is acceptable only for pointers to immediate derived classes. Therefore, pointers of type Derived1 * can be converted to type Base *.

  4. #4
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 632
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 632
    Points : 30 711
    Points
    30 711
    Par défaut
    Salut,

    Le fait est que l'héritage privé n'a rien d'un héritage polymorphe (ad-hoc, de surcharge ou de coercition):

    L'héritage privé n'a pas la pas la sémantique d'une relation EST-UN que peut avoir l'héritage public, mais bien la sémantique "est implémenté en terme de", et se rapproche donc bien plus d'un phénomène d'aggrégation (cf: cette entrée de la FAQ).

    En effet, il faut bien te dire que tout ce qui est public dans la classe de base passe dans la visibilité... privée de la classe qui hérite de manière privée de la classe de base.

    De ce fait, les différents éléments de la classe de base ne sont accessibles, comme tout élément déclaré avec accessibilité privée, qu'au départ... d'une des fonctions membre de la classe dérivée.

    Enfin, il faut te dire que, si tu décides d'hériter de manière privée d'une classe, c'est, très certainement parce que... tu souhaites "cacher" la relation qui existe entre la classe dérivée et la classe de base.

    Il n'y a donc, a priori, aucune raison valable de vouloir utiliser une instance de la classe dérivée en la faisant passer pour une instance de la classe de base, vu que... tu n'est même pas sensé savoir qu'il y a une relation entre les deux

  5. #5
    Débutant  
    Inscrit en
    Novembre 2006
    Messages
    1 073
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 073
    Points : 217
    Points
    217
    Par défaut
    Il n'y a donc, a priori, aucune raison valable de vouloir utiliser une instance de la classe dérivée en la faisant passer pour une instance de la classe de base, vu que... tu n'est même pas sensé savoir qu'il y a une relation entre les deux
    Ok, mais le cas donné dans la MSDN est un cas d'école qui montre que l'on peut convertir un pointeur d'un classe dérivée dans une classe de base. Et la question que je me pose est: comment est-ce possible de faire cela, (même si théoriquement, on n'a pas le droit de le faire comme tu viens de le dire).

  6. #6
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 632
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 632
    Points : 30 711
    Points
    30 711
    Par défaut
    Citation Envoyé par deubelte Voir le message
    Ok, mais le cas donné dans la MSDN est un cas d'école qui montre que l'on peut convertir un pointeur d'un classe dérivée dans une classe de base. Et la question que je me pose est: comment est-ce possible de faire cela, (même si théoriquement, on n'a pas le droit de le faire comme tu viens de le dire).
    Tu donnes toi même la réponse: si en théorie, on n'a pas le droit de le faire, il vaut mieux veiller à... ne pas le faire en pratique

    Maintenant, mais je ne pourrai jamais insister assez sur le fait que c'est particulièrement dangereux, le seul endroit où tu pourra envisager la conversion sera... une fonction membre dans laquelle this est un pointeur sur la classe directement dérivée.

    Autrement dit, dans une fonction membre non redéfinie, non virtuelle de la classe qui hérite de manière privée de la classe de base:

    using namespace std;
    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
    class Base
    {
        public:
            void foo()
            {
                cout <<"base::foo()";
            }
    };
    class Derivee : private Base
    {
        public:
            void bar()
            {
                Base* ptr=static_cast<Base*>(this);
                ptr->foo();
            }
            Base* toBase()
            {
                return static_cast<Base*>(this);
            }
    };
    class D2 : public Derivee
    {
        public:
            void doSomething()
            {
                bar();
            }
    };
    int main()
    {
        D2 obj;
        obj.doSomething();
        Base* ptr=obj.toBase();
        ptr->foo();
    }

  7. #7
    Débutant  
    Inscrit en
    Novembre 2006
    Messages
    1 073
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 073
    Points : 217
    Points
    217
    Par défaut
    Dans la fonction foo, pourquoi mettre un static_cast?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
      Base* ptr=static_cast<Base*>(this);
                ptr->foo()
    En effet, étant donné que l'héritage est privé, cela revient à dire que this est implémenté en terme de "Base". Ici, le type statique de ptr est Base et son type dynamique est Derivee, mais comme la classe Dérivée ne redéfinit par
    la fonction foo() et que celle-ci n'est pas virtuelle, il n'y a pas de risque d'ambiguité, non?

  8. #8
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 632
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 632
    Points : 30 711
    Points
    30 711
    Par défaut
    Citation Envoyé par deubelte Voir le message
    Dans la fonction foo, pourquoi mettre un static_cast?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
      Base* ptr=static_cast<Base*>(this);
                ptr->foo()
    En effet, étant donné que l'héritage est privé, cela revient à dire que this est implémenté en terme de "Base". Ici, le type statique de ptr est Base et son type dynamique est Derivee, mais comme la classe Dérivée ne redéfinit par
    la fonction foo() et que celle-ci n'est pas virtuelle, il n'y a pas de risque d'ambiguité, non?
    Cela n'a aucun intérêt, à part celui de te montrer que tu peux le faire, et d'utiliser les transtypages C++

    Si l'héritage n'était pas pris en compte, tu aurais une erreur au plus tard à l'exécution, et sans doute à la compilation si tu essayais d'effectuer un transtypage non autorisé (vers un type différent du type de base, par exemple )

  9. #9
    Débutant  
    Inscrit en
    Novembre 2006
    Messages
    1 073
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 073
    Points : 217
    Points
    217
    Par défaut
    J'ai une autre question concernant la déclaration d'héritage

    Sur ce lien http://msdn.microsoft.com/en-us/libr...5x(VS.80).aspx

    Il est écrit

    The base class from which each class is derived is declared before the declaration of the derived class. It is not sufficient to provide a forward-referencing declaration for a base class; it must be a complete declaration.
    Pouvez vous me donner l'exemple d'une déclaration forward? je ne vois pas trop ce que c'est. Par exemple, ce code semble fonctionner:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class A;
    class B:public A{
     
    };
    class A{...};
    Dois-je en déduire qu'il ne s'agit pas de déclaration forward, mais d'une vraie déclaration?

    merci

  10. #10
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 632
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 632
    Points : 30 711
    Points
    30 711
    Par défaut
    Une forward declaration, c'est ce que l'on appelle en français une déclaration anticipée.

    A quelques exceptions près, elles ne peuvent être utilisées que lorsque tu souhaites simplement déclarer un pointeur ou une référence sur un objet du type déclaré de manière anticipée sans essayer d'accéder à l'objet

    Elles ne servent, en très gros, qu'à prévenir le compilateur que "il existe un type nommé 'le type déclaré anticipativement' ".

    Le revers de la médaille, c'est que le compilateur sait donc que le type existe, mais qu'il n'en sait absolument rien de plus tant qu'il n'en aura pas rencontré la définition.

    Il est donc, en attendant de croiser la définition du type, dans l'impossibilité d'en déterminer les différents membres ou... sa taille.

    Or l'héritage est une relation très forte qui nécessite de connaitre l'ensemble de la classe de base, et, du coup, le compilateur se plaint du fait qu'il sait effectivement que le type existe, mais qu'il est dans l'incapacité d'en déterminer le contenu

    [EDIT]Effectivement, le premier class A; est une déclaration anticipée, et justement, il ne connait que le nom de A lorsqu'il rencontre class B : public A... et il n'est pas content

  11. #11
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par deubelte Voir le message
    je trouve cela étrange aussi. Mais regarde ici:

    http://msdn.microsoft.com/en-us/library/5bw4yh4d.aspx
    L'exemple du MSDN est un peu subtil :
    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
    // access_control.cpp
    class Base
    {
    public:
        int Print();             // Nonstatic member.
        static int CountOf();    // Static member.
    };
     
    // Derived1 declares Base as a private base class.
    class Derived1 : private Base
    {
    };
    // Derived2 declares Derived1 as a public base class.
    class Derived2 : public Derived1
    {
        int ShowCount();    // Nonstatic member.
    };
    // Define ShowCount function for Derived2.
    int Derived2::ShowCount()
    {
       // Call static member function CountOf explicitly.
        int cCount = Base::CountOf();     // OK.
     
       // Call static member function CountOf using pointer.
        cCount = this->CountOf();  // C2247. Conversion of
                                   //  Derived2 * to Base * not
                                   //  permitted.
        return cCount;
    }
     
    int main()
    {
        return 0;
    }
    Base::CountOf est une fonction statique et par conséquent ne nécessite pas d'objet pour être appelée. Etant publique elle peut être appelée par n'importe quel objet qui dérive ou non de base.
    La subtilité vient que dans le second appel (mais j'ai le même problème avec le premier appel sur gcc et visual express 2008) que l'objet this est utilisé pour rechercher la fonction. Or celle trouvée est celle de base. Elle nécessite donc une conversion de this vers Base. Or cette conversion est impossible car l'héritage est privée. Ce qui provoque l'erreur. A ce stade, je ne suis pas capable de te dire si l'erreur vient de la norme pour le premier appel qui utilise implicitement this pour résoudre Base::CountOf ou d'une erreur d'implémentation des 2 compilateurs que j'ai essayé. Pour le second appel, this->CountOf, l'erreur liée à la conversion ne me choque pas car on demande explicitement à rechercher une fonction CountOf applicable sur un objet Derivated2.


    Citation Envoyé par deubelte Voir le message
    J'ai une autre question concernant la déclaration d'héritage

    Sur ce lien http://msdn.microsoft.com/en-us/libr...5x(VS.80).aspx

    Il est écrit



    Pouvez vous me donner l'exemple d'une déclaration forward? je ne vois pas trop ce que c'est. Par exemple, ce code semble fonctionner:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    class A;
    class B:public A{
     
    };
    class A{...};
    Dois-je en déduire qu'il ne s'agit pas de déclaration forward, mais d'une vraie déclaration?

    merci
    Une déclaration anticipé (ou déclaration tout court) permet de dire au compilateur ce qu'il relève exactement d'un symbole sans pour autant le définir, c'est à dire lui donner corps :
    Elle permet l'utilisation de ce type dans des circonstances où on n'a pas besoin de le connaître complètement. L'exemple typique est l'utilisation de pointeur ou de référence dans un passage d'argument :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    class A;
    void une_fonction_sur_un_A(A const &); // la déclaration anticipée suffit
    Cependant au moment de son utilisation, il faut l'avoir complètement définie :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class A;
    void une_fonction_sur_un_A(A const &); // la déclaration anticipée suffit
     
    class A
    {
    public :
    void une_fonction()const;
    };
    void une_fonction_sur_un_A(A const &a_)
    {
       a_.une_fonction(); // ici A doit être définie pour pouvoir être utilisée.
    }
    Pour l'héritage, la classe doit être définie entièrement. Une déclaration anticipée ne suffit pas.
    Si tu testes le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class A;// déclaration anticipée
    class B:public A{ // -> error C2504: 'A'*: classe de base non définie
     
    };
    Il faut bien une déclaration complète :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class A{};// déclaration complète
    class B:public A{ // l'héritage est possible. Il n'y a plus d'erreur.
     
    };

  12. #12
    Membre du Club
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 32
    Points : 41
    Points
    41
    Par défaut
    A ce stade, je ne suis pas capable de te dire si l'erreur vient de la norme pour le premier appel qui utilise implicitement this pour résoudre Base::CountOf ou d'une erreur d'implémentation des 2 compilateurs que j'ai essayé.
    Pas de problème au niveau de l'implémentation des compilateurs, le comportement est bien celui attendu (B::countOf() OK, mais this->countOf() KO). On retrouve d'ailleurs un tel exemple dans la norme.

  13. #13
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    A ce stade, je ne suis pas capable de te dire si l'erreur vient de la norme pour le premier appel qui utilise implicitement this pour résoudre Base::CountOf ou d'une erreur d'implémentation des 2 compilateurs que j'ai essayé.
    Citation Envoyé par 3DArchi Voir le message
    Pour le second appel, this->CountOf, l'erreur liée à la conversion ne me choque pas car on demande explicitement à rechercher une fonction CountOf applicable sur un objet Derivated2.
    Citation Envoyé par Idwakest Voir le message
    Pas de problème au niveau de l'implémentation des compilateurs, le comportement est bien celui attendu (B::countOf() OK, mais this->countOf() KO). On retrouve d'ailleurs un tel exemple dans la norme.
    Je sais que j'ai du mal à m'exprimer, mais mon doute est bien sur B::countOf() qui ne compile pas sur gcc ou sur visual express. Et c'est bien parce que j'ai essayé de compiler l'exemple que je me rend compte que B::countOf() KO et this->countOf() KO et non pas B::countOf() OK, mais this->countOf() KO

  14. #14
    Membre du Club
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 32
    Points : 41
    Points
    41
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Je sais que j'ai du mal à m'exprimer, mais mon doute est bien sur B::countOf() qui ne compile pas sur gcc ou sur visual express. Et c'est bien parce que j'ai essayé de compiler l'exemple que je me rend compte que B::countOf() KO et this->countOf() KO et non pas B::countOf() OK, mais this->countOf() KO
    Autant pour moi j'avais compris l'inverse.
    Ce n'est pas un bug des compilos, c'est juste que l'exemple de MSDN est faux !!

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
        int cCount = Base::CountOf();     // KO !!!!!!
    mais


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
        int cCount = ::Base::CountOf();     // OK

  15. #15
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par Idwakest Voir le message
    Autant pour moi j'avais compris l'inverse.
    Ce n'est pas un bug des compilos, c'est juste que l'exemple de MSDN est faux !!

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
        int cCount = Base::CountOf();     // KO !!!!!!
    mais


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
        int cCount = ::Base::CountOf();     // OK
    Je m'en étais rendu compte mais j'avoue que de prime abord, on aurait pu attendre que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
        int cCount = Base::CountOf();     // KO !!!!!!
    soit OK. Car après tout CountOf est une fonction statique et Base::CountOf() l'appel qu'on a envie de faire. Il doit y avoir une subtilité qui fait primer l'appel Base::CountOf comme implicitement équivalent à this->Base::CountOf plutôt qu'à ::Base::CountOf().
    En toute franchise, si je n'avais pas essayé de compiler l'exemple du MSDN, j'aurais eu le réflexe de le croire à sa seule lecture.
    Le C++ reste toujours surprenant dans ses détails et je comprend qu'il puisse être parfois déroutant lorsqu'on débute.

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

Discussions similaires

  1. Héritage et relation entre les classes filles
    Par h_ayadi dans le forum JPA
    Réponses: 3
    Dernier message: 02/02/2012, 21h02
  2. Réponses: 2
    Dernier message: 13/03/2006, 18h25
  3. [PHP5][MYSQL]Préserver une connexion entre les classes
    Par nesbla dans le forum SQL Procédural
    Réponses: 3
    Dernier message: 02/02/2006, 12h51
  4. Réponses: 3
    Dernier message: 22/11/2005, 12h12
  5. Comparaison entre les classes et les fonctions
    Par Ashgenesis dans le forum Langages de programmation
    Réponses: 6
    Dernier message: 08/09/2005, 20h09

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