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 :

Problème avec classes virtuelles


Sujet :

C++

  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Novembre 2005
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2005
    Messages : 7
    Points : 6
    Points
    6
    Par défaut Problème avec classes virtuelles
    Bonjour à tous,

    Je suis face à un problème dont je n'arrive pas à trouver la solution.
    J'ai le code suivant:

    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
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
     
    #include <iostream>
     
    using namespace std;
     
     
    ////////////////////////////////////////////////////////
     
    class A
    {
     
    public:
     
    	A() { std::cout << "A BAD_0!" << std::endl; }
     
    	A( unsigned int val )  { std::cout << "A BAD_1!" << std::endl; }
     
    	A( float val_1, unsigned int val_2, unsigned int val_3 )  { std::cout << "A GOOD!" << std::endl; }
     
    };
     
    ////////////////////////////////////////////////////////
     
    class B : public A
    {
    public:
     
    	B( unsigned int val = 0 ) : A( val ) 
    	{ std::cout << "B BAD!" << std::endl; }
     
    	B( float val_1, unsigned int val_2, unsigned int val_3 = 0) 
    		: A( val_1, val_2, val_3 )
    	{ std::cout << "B GOOD!" << std::endl; }
    };
     
    ////////////////////////////////////////////////////////
     
    //class C : public B // VERSION WITH NO PROBLEM
    class C : public virtual B // VERSION WITH PROBLEM
    {
    public:
     
    	C( unsigned int val = 0) : B( val ) 
    	{ std::cout << "C BAD!" << std::endl; }
     
     
    	C( float val_1,	unsigned int val_2, unsigned int val_3 = 0 ) 
    		: B( val_1, val_2, val_3 )
    	{ std::cout << "C GOOD!" << std::endl; }
     
    };
     
    ////////////////////////////////////////////////////////
     
    class D :  public virtual C
    {
    public:
     
     
    	D( unsigned int val = 0) : C( val ) 
    	{ std::cout << "D BAD!" << std::endl; }
     
    	D( float val_1, unsigned int val_2, unsigned int val_3) 
    		: C(val_1, val_2, val_3 )
    	{ std::cout << "D GOOD!" << std::endl; }
    };
     
    ////////////////////////////////////////////////////////
     
     
    int main( int argc, char ** argv)
    {
    	D* d_instance = new D(1, 2, 3);
     
    	std::cout << " ----" << std::endl;
     
    	C* c_intance = new C(1, 2, 3 );
     
    	getchar();
     
    	return EXIT_SUCCESS;
    }
    Quand je l'exécute(sous linux avec g++ et sous windows avec visual C++) j'ai cette trace:

    A BAD_1!
    B BAD!
    C GOOD!
    D GOOD!
    ----
    A GOOD!
    B GOOD!
    C GOOD!

    Je ne comprends pas pourquoi pour D* d_instance = new D(1, 2, 3); il ne passe pas par les bons contructeurs.

    Par contre si je remplace class C : public virtual B // VERSION WITH PROBLEM par class C : public B // VERSION WITH NO PROBLEM l'exécution est bonne. Il passe par les bons constructeurs.
    voici la trace:
    A GOOD!
    B GOOD!
    C GOOD!
    D GOOD!
    ----
    A GOOD!
    B GOOD!
    C GOOD!

    Donc c'est le fait de déclarer la classe B comme virtuelle qui pose problème. Mais je ne vois pas en quoi !

    J'ai bien trouvé cela http://www.greyc.ensicaen.fr/ensicaen/CPP/x2951.html Mais je ne me trouve pas dans ce cas de figure.

    Si quelqu'un a une explication ! Merci

  2. #2
    Membre chevronné
    Avatar de poukill
    Profil pro
    Inscrit en
    Février 2006
    Messages
    2 155
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 2 155
    Points : 2 107
    Points
    2 107
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    class C : public virtual B
    Qu'est ce que c'est que ça ? Ca compile ?
    C'est du jamais vu pour moi.....

    Ah ça je connais !

  3. #3
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Points : 4 732
    Points
    4 732
    Par défaut
    poukill >> C'est l'héritage virtuel. Ca sert dans les cas d'héritage en diamand ( une classe D hérite de C et B qui toutes deux héritent d'une classe A. Sans héritage virtuel, il y a deux fois les membres de A dans D).

    Par contre, j'ai plus l'habitude de mettre le mot clé virtual avant, bien qu'il est possible que cela ne change rien, je ne suis pas assez calé en théorie des langages pour comprendre la grammaire de la norme.

    Edit: je viens de comprendre que c'est en tant que constructeur par défaut que B est appelé (car si tu retire la valeure par défaut dans le constructeur, la compilation plante). Reste à trouver pourquoi.
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

  4. #4
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Juste pour être sûr, j'imagine que c'est un exemple qui reproduit le problème, et que tu as réellement besoin d'un héritage virtuel à cause d'un héritage multiple ?

    Parce que héritage virtuel et fonctions virtuelles ne sont pas liés.

    Ensuite, par rapport au lien que tu donnes, il y a l'explication dedans :

    Deuxièmement, chaque classe dérivée directement ou indirectement d'une classe virtuelle doit en appeler le constructeur explicitement dans son constructeur si celui-ci prend des paramètres. En effet, elle ne peut pas se fier au fait qu'une autre de ses classes de base, elle-même dérivée de la classe de base virtuelle, appelle un constructeur spécifique, car il est possible que plusieurs classes de base cherchent à initialiser différemment chacune un objet commun hérité de la classe virtuelle.
    C'est exactement le cas.

    Suppose, par exemple, que tu aies, dans le cas d'un héritage en diamand :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class B : public virtual A
    {
      B() : A(10) {}; 
    }
     
    class C : public virtual A
    {
      C() : A(15);
    }
     
    class D: public B, public C
    {
      D() : C(), B() {};
    }
    Le compilateur ne sait pas quel constructeur de A il doit appeler. Donc tu dois préciser lequel en écrivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    D() : C(), B(), A(10) {};

  5. #5
    Membre chevronné
    Avatar de poukill
    Profil pro
    Inscrit en
    Février 2006
    Messages
    2 155
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 2 155
    Points : 2 107
    Points
    2 107
    Par défaut
    Citation Envoyé par Davidbrcz Voir le message
    poukill >> C'est l'héritage virtuel. Ca sert dans les cas d'héritage en diamand ( une classe D hérite de C et B qui toutes deux héritent d'une classe A. Sans héritage virtuel, il y a deux fois les membres de A dans D)..
    Au temps pour moi, je ne savais pas.

  6. #6
    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
    Bonjour,
    Pour rappel, l'ordre de construction des objets est le suivant :
    1/ Pour l'objet le plus bas de la hiérarchie, les classes virtuelles dans l'ordre de leur apparition (gauche->droite) depuis la plus profonde (celle le plus haut dans la hierarchie).
    2/ les classes de bases dans l'ordre où elles apparaissent dans la définition,
    3/ Les membres statiques dans l'ordre de leur définition.
    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
     
    class A {};
     
    class B {};
     
    class C :public virtual A, public  B {};
     
    class D {};
     
    class E {};
     
    class F :virtual public D, public C
    {
       E e;
    };
    Ordre des constructeurs : D, A, B, C, E, F.
    D car c'est la première classe virtuelle,
    puis A car c'est la classe virtuelle la plus profonde en remontant la branche par C
    Puis B, C, les classes de bases non virtuels,
    Puis E pour le membre e
    Enfin, F.

    Ensuite, si le constructeur de la classe la plus basse ne spécifie pas de constructeur pour la classe virtuelle, alors c'est le constructeur par défaut qui est pris :
    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
     
    class A
    {
    public:
       A(){std::cout<<"A"<<std::endl;}
       A(int){std::cout<<"A - 1"<<std::endl;}
    };
     
    class B : public virtual A
    {
    public:
       B():A(1){std::cout<<"B"<<std::endl;}
    };
     
    class C : public B
    {
    public:
       C(){std::cout<<"C"<<std::endl;}
    };
     
    int main()
    {
       std::cout<<"Construction de B"<<std::endl;
       B b;
       std::cout<<"Construction de C"<<std::endl;
       C c;
       return 0;
    }
    On a :
    Construction de B
    A - 1
    B
    Construction de C
    A
    B
    C
    Par abus de langage, on peut dire qu'avec une classe virtuelle on n'hérite pas des appels de constructeurs des classes de base. Il faut le re-spécifier à chaque niveau si on ne veut pas l'appel implicite.

  7. #7
    Futur Membre du Club
    Profil pro
    Inscrit en
    Novembre 2005
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2005
    Messages : 7
    Points : 6
    Points
    6
    Par défaut
    Depuis vos réponses la solution a été trouvée de mon côté.
    Il faut appeler le constructeur de base pour ne pas avoir ce problème.

    Merci

  8. #8
    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 lokida Voir le message
    Depuis vos réponses la solution a été trouvée de mon côté.
    Il faut appeler le constructeur de base pour ne pas avoir ce problème.

    Merci
    Tu verras aussi dans les différentes réponses pourquoi...

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

Discussions similaires

  1. problème avec class vector
    Par abdelmajid_daosabah dans le forum Collection et Stream
    Réponses: 2
    Dernier message: 12/11/2007, 22h40
  2. Problème avec Class.forName
    Par steftanguy dans le forum JDBC
    Réponses: 4
    Dernier message: 14/08/2007, 16h24
  3. Réponses: 8
    Dernier message: 18/06/2007, 15h06
  4. problème avec classe interface
    Par bandit_debutant dans le forum Langage
    Réponses: 6
    Dernier message: 05/11/2006, 20h54
  5. Problème avec classe MySQL
    Par shadeoner dans le forum Requêtes
    Réponses: 1
    Dernier message: 10/07/2006, 11h25

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