2010-08-16 8 views
10

Gibt es eine (praktische) Möglichkeit, die normale Aufrufreihenfolge des virtuellen Konstruktors zu umgehen?Aufruf des überladenen Konstruktors einer virtuellen Basisklasse

Beispiel:

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; 
} 

Output:

Aufruf A()
Aufruf B (int)
Aufruf C (int)
Aufruf D (int)

Was ich haben will, ist so etwas wie:

A (int) Aufruf
Aufruf B (int)
Aufruf C (int)
Aufruf D (int)


Wie Sie sehen, ist eine virtuelle Vererbung beteiligt, die dazu führt, dass der Konstruktor von D zuerst den Konstruktor von A aufruft, aber da kein Parameter angegeben ist, ruft er A() auf. Es gibt die const Int i, die Initialisierung benötigt, also habe ich ein Problem.

Was ich tun möchte ist, die Vererbung Details von C zu verstecken, deshalb suche ich nach einer Möglichkeit zu vermeiden, A (i) in D's (und jedem abgeleiteten) Konstruktor Initialisierungsliste zu nennen. [edit] In diesem speziellen Fall kann ich annehmen, dass es nur nicht-virtuelle Einzelvererbungs-Kindklassen von C gibt (wie D ist eins). [/ Edit]

[Bearbeiten]

Virtuelle Basisklassen initialisiert werden, bevor irgendwelche nicht-virtuellen Basisklassen initialisiert werden, so dass nur die abgeleitete Klasse virtuelle Basisklassen initialisieren. - James McNellis

Das ist genau der Punkt, ich nicht wollen die abgeleitete Klasse die virtuelle Basisklasse Konstruktor aufzurufen.

A 
/\ 
B0 B1 
\/
    C 
    | 
    D 

Ich verstehe, warum C die Ctor von A (Mehrdeutigkeit), wenn Sie anrufen hat: [/ edit]

Betrachten Sie die folgende Situation (nicht im Codebeispiel oben dargestellt) instanziieren C, aber warum muss D es nennen, wenn es D instanziiert?

+1

Ich glaube nicht, dass Ihr Codebeispiel mit der von Ihnen bereitgestellten Ausgabe übereinstimmt. Sind Sie sicher, dass Sie d mit der Anweisung "D d" instanziiert haben? ? –

+0

sry, ich habe den Parameter vergessen .... es ist D d (42) jetzt. Vielen Dank. – dyp

+0

Ok, das scheint fairer zu sein :-) Darf ich fragen, warum Sie die "Dreaded Diamond" -Architektur verwenden wollen? Können Sie Ihren Code nicht auf andere Weise reorganisieren? –

Antwort

5

Leider müssen Sie den Konstruktor der virtuellen Basisklasse immer von der am weitesten abgeleiteten Klasse aufrufen.

Dies ist, weil Sie sagen, dass die virtuelle Basis zwischen allen Klassen geteilt wird, die davon für die Instanz des Objekts abgeleitet werden. Da ein Konstruktor nur einmal für eine gegebene Instanz eines Objekts aufgerufen werden kann, müssen Sie den Konstruktor explizit in der abgeleiteten Klasse aufrufen, da der Compiler nicht weiß, wie viele Klassen sich die virtuelle Basis (paraphrasiert (wahrscheinlich schlecht) von The) teilen C++ Programmiersprache 3. Ausgabe, Abschnitt 15.2.4.1). Dies liegt daran, dass der Compiler vom Konstruktor der Basisklasse startet und auf die am weitesten abgeleitete Klasse zugreift. Klassen, die direkt von einer virtuellen Basisklasse erben, werden standardmäßig nicht den Konstruktor ihrer virtuellen Basisklassen aufrufen, daher müssen sie explizit aufgerufen werden.

+0

> Klassen, die direkt von einer virtuellen Basisklasse erben, nennen im Standard ihren virtuellen Basisklassenkonstruktor nicht und müssen daher explizit aufgerufen werden. Das ist richtig für Klasse B, und dann könnte Klasse C es nennen. Aber da C nicht die am weitesten abgeleitete Klasse ist, muss D sie nennen. Kann C es nennen? D wird direkt von C mit einer einzelnen nicht virtuellen Vererbung abgeleitet. – dyp

+0

@DyP: Angenommen, ich füge eine Klasse E hinzu, die von C ableitet und dann D multipliziert von C und E ableitet (auch wenn ich C nicht virtuell mache), welches C ruft dann den Konstruktor für A auf? – diverscuba23

+0

sry dafür, ich habe die Frage bearbeitet. Ich kann annehmen, es gibt keine Mehrfachvererbung von C. – dyp

-1

Auf Paraschift C++ - FAQ-Lite diese Ausgabe is outlined.

+0

Ich glaube, der Autor fragte, warum man den Konstruktor der virtuellen Basisklasse immer von der am weitesten abgeleiteten Klasse aufrufen musste, und nicht ein bestimmtes Problem, das er mit dem Beispiel hatte, das er gab. Das Beispiel diente lediglich dazu, seine Frage klar zu stellen. – diverscuba23

+0

@ diverscuba23: Ich brauchte ein wenig Suche, um zu verstehen, was das Problem war. Jetzt verstehe ich es, verstehe aber nicht die Situation, die dazu führen würde. – Shamster

2

Ich verstehe, warum C die Ctor von A (Mehrdeutigkeit), wenn Sie Instanciate C nennen, aber warum D müssen es nennen, wenn D Instanziierung?

Aus dem gleichen Grund, dass C es nennen muss. Es ist keine Frage der Mehrdeutigkeit, es ist die Tatsache, dass A's Konstruktor nur einmal aufgerufen werden muss (da es eine virtuelle Basis ist).

Wenn Sie gehofft haben, dass C den Konstruktor von A initialisieren könnte, was wäre dann, wenn Klasse D C und eine andere Klasse erben würde, die schließlich A erbt?

+0

> [...] Was wäre, wenn Klasse D zwei Cs erben würde? Musste ich dann nicht virtuell von C ableiten? – dyp

+0

@DyP: Ja, fairer Punkt über zwei C's. Ich habe das aus meiner Antwort heraus bearbeitet. Der Punkt steht natürlich noch. – Troubadour

+0

@DyP: Wenn C nicht mehrere Kopien von sich selbst in einem Objekt unterstützen kann, muss es nicht virtuell sein. Zugegeben, es ist wirklich selten, dass Sie jemals eine solche Situation wollen, aber es ist möglich, mit der Sprache zu tun. – diverscuba23

0

Das sind die Regeln. Es gibt Regeln zum Überschreiben von virtuellen Funktionen und Regeln zum Konstruieren von virtuellen Basisunterobjekten. Obwohl beide Begriffe konzeptionell sehr ähnlich sind, folgen sie aus einem ganz anderen Grund völlig anderen Regeln: Das Überschreiben einer virtuellen Funktion ist explizit. Das Aufrufen eines Konstruktors ist für den Standardkonstruktor implizit.

Virtuelle Funktionen in virtuellen Basisklassen müssen nur einen letzten Overrider haben, einen Overrider, der alle anderen Overriders außer Kraft setzt. (Virtuelle Funktionen in nicht virtuellen Basisklassen können möglicherweise nicht zwei Überschreibungen haben, so dass der eine den anderen nicht übersteuert.)

Aber virtuelle Basisklassenkonstruktoren werden immer von der am weitesten abgeleiteten Klasse und normalerweise in der impliziten Form von not aufgerufen Die virtuelle Basisklasse in der ctor-init-Liste zu erwähnen, da die meisten Klassen, die als virtuelle Basisklassen verwendet werden sollen, "reine Schnittstellen" ohne Datenelemente und keine Benutzerinitialisierung sind.