2010-08-16 5 views
10

Existe-t-il un moyen (pratique) de contourner l'ordre d'appel du constructeur normal (virtuel)?Appel du constructeur surchargé d'une classe de base virtuelle

Exemple:

class A 
{ 
    const int i; 

public: 
    A() 
     : i(0) 
    { cout << "calling A()" << endl; } 

    A(int p) 
     : i(p) 
    { cout << "calling A(int)" << endl; } 
}; 

class B 
    : public virtual A 
{ 
public: 
    B(int i) 
     : A(i) 
    { cout << "calling B(int)" << endl; } 
}; 

class C 
    : public B 
{ 
public: 
    C(int i) 
     : A(i), B(i) 
    { cout << "calling C(int)" << endl; } 
}; 

class D 
    : public C 
{ 
public: 
    D(int i) 
     : /*A(i), */ C(i) 
    { cout << "calling D(int)" << endl; } 
}; 


int main() 
{ 
    D d(42); 
    return 0; 
} 

sortie:

appelant A()
appelant B (int)
appelant C (int)
appelant D (int)

Ce que je veux avoir est quelque chose comme:

appelant A (int)
appelant B (int)
appelant C (int)
appelant D (int)


Comme vous le voyez, il y a un héritage virtuel impliqué, ce qui amène le constructeur de D à appeler le constructeur de A en premier, mais comme aucun paramètre n'est fourni, il appelle A(). Il y a le const int qui a besoin d'initialisation, donc j'ai un problème. Ce que je voudrais faire est de cacher les détails d'héritage de C, c'est pourquoi je cherche un moyen d'éviter d'appeler A (i) dans la liste d'initialisation du constructeur de D (et de chaque dérivé). [edit] Dans ce cas précis, je peux supposer qu'il n'y a que des classes enfants non-virtuelles à héritage unique de C (comme D en est une). [/ Edit]

[modifier]

classes de base virtuelles sont initialisés avant toutes les classes de base non virtuelles sont initialisés, alors que la classe la plus dérivée peut initialiser les classes de base virtuelles. - James McNellis

C'est exactement le point, je ne le font pas veulent la classe la plus dérivée pour appeler le constructeur de la classe de base virtuelle. [/ modifier]

la situation suivante (pas représenté dans l'exemple de code ci-dessus):

A 
/\ 
B0 B1 
\/
    C 
    | 
    D 

Je comprends pourquoi C doit appeler le cteur de A (ambiguïté) lorsque vous instancier C, mais pourquoi D doit-il l'appeler lors de l'instanciation de D?

+1

Je ne crois pas que votre exemple de code corresponde à la sortie que vous fournissez. Êtes-vous sûr d'instancier avec l'instruction "D d"? ? –

+0

sry, j'ai oublié le paramètre .... c'est D d (42) maintenant. Merci. – dyp

+0

Ok, cela semble plus juste :-) Puis-je vous demander pourquoi vous voulez utiliser l'architecture "diamant redouté"? Tu ne peux pas réorganiser ton code d'une autre manière? –

Répondre

5

Malheureusement, vous devrez toujours appeler le constructeur de classes de base virtuelles à partir de la classe la plus dérivée. C'est parce que vous dites que la base virtuelle est partagée entre toutes les classes qui en dérivent pour l'instance de l'objet. Comme un constructeur ne peut être appelé qu'une seule fois pour une instanciation donnée d'un objet, vous devez explicitement appeler le constructeur dans la classe la plus dérivée car le compilateur ne sait pas combien de classes partagent la base virtuelle (paraphrasée (probablement mal)). Langage de programmation C++ 3ème édition, section 15.2.4.1). C'est parce que le compilateur commencera à partir du constructeur de la classe la plus basique et travaillera à la classe la plus dérivée. Les classes qui héritent directement d'une classe de base virtuelle, n'appelleront pas, par la norme, leur constructeur de classes de base virtuelles, elles doivent donc être appelées explicitement.

+0

> Les classes qui héritent directement d'une classe de base virtuelle, n'appelleront pas, par la norme, le constructeur de classes de base virtuelles, elles doivent donc être appelées explicitement. C'est juste pour la classe B, et ensuite, la classe C pourrait l'appeler. Mais puisque C n'est pas la classe la plus dérivée, D doit l'appeler. Un moyen de laisser C l'appeler? D est directement dérivé de C avec un héritage non virtuel unique. – dyp

+0

@DyP: Supposons que j'ajoute une classe E dérivée de C puis que D se multiplie à partir de C et E (même si je ne rends pas C virtuel) alors C appelle le constructeur pour A? – diverscuba23

+0

sry pour cela, j'ai édité la question. Je peux supposer qu'il n'y a pas d'héritage multiple de C. – dyp

-1

Sur parashift C++ - faq-lite ce numéro is outlined.

+0

Je crois que l'auteur demandait pourquoi vous avez toujours dû appeler le constructeur de classes de base virtuelles de la classe la plus dérivée, et pas un problème particulier qu'il avait avec l'exemple qu'il a donné. L'exemple était juste pour illettre sa question clairement. – diverscuba23

+0

@ diverscuba23: Il m'a fallu quelques recherches pour comprendre même quel était le problème. Maintenant, je comprends, mais je ne comprends pas la situation qui mènerait à cela. – Shamster

2

Je comprends pourquoi C doit appeler le cteur de A (ambiguïté) lorsque vous instancie C, mais pourquoi D doit appeler quand instanciation D?

Pour la même raison que C doit l'appeler. Ce n'est pas un problème d'ambiguïté, c'est le fait que le constructior de A ne doit être appelé qu'une seule fois (puisqu'il s'agit d'une base virtuelle).

Si vous espériez que C pourrait initialiser le constructeur de A, que se passerait-il si la classe D héritait de C et qu'une autre classe héritait finalement de A?

+0

> [...] que se passerait-il si la classe D héritait de deux C [...]? Avais-je alors dérivé virtuellement de C? – dyp

+0

@DyP: Oui, juste au sujet de deux C. J'ai édité cela hors de ma réponse. Le point reste cependant bien sûr. – Troubadour

+0

@DyP: Pas nécessairement, si C peut supporter plusieurs copies de lui-même dans un objet, il n'a pas besoin d'être virtuel. D'accord, c'est vraiment rare que vous voudriez jamais une telle situation, mais il est possible de faire avec la langue. – diverscuba23

0

Telles sont les règles. Il existe des règles pour remplacer les fonctions virtuelles et des règles pour construire des sous-objets de base virtuels. Bien que les deux soient très similaires conceptuellement, ils suivent des règles complètement différentes, pour une raison: le remplacement d'une fonction virtuelle est explicite. L'appel d'un constructeur est implicite pour le constructeur par défaut.

Les fonctions virtuelles dans les classes de base virtuelles ne doivent contenir qu'un seul overrider final, un overrider qui écrase tous les autres overriders. (Les fonctions virtuelles dans les classes de base non virtuelles ne peuvent pas avoir deux overriders de telle sorte que l'une ne remplace pas l'autre.)

Mais les constructeurs de classe de base virtuelle sont toujours appelés à partir de la classe la plus dérivée, et généralement sous la forme implicite de non Il est inutile de mentionner la classe de base virtuelle dans la liste ctor-init-list, car la plupart des classes conçues pour être utilisées comme classes de base virtuelles sont des "interfaces pures" sans membre de données et sans initialisation d'utilisateur.

Questions connexes