In-class initialization reihenfolge



  • In einer Vererbungshierarchie mit einer Basis Klasse und einer Subklasse, ruft der Ctor der Subklasse ja u.U. den Ctor der Basisklasse auf, um das Basisklassen Subobjekt ordnungsgemäß zu initialisieren, sofern die Basisklasse keinen default ctor hat und dieser auch aufgerufen werden soll. Also wird bei

    Base *obj = new Derived(5);
    

    zunächst der Ctor von Derived aufgerufen, der dann zum Ctor der basisklasse weiterleitet. Dort werden dann laut stack overflow alle datamember initialisiert, zB durch die Contructor initializer list und erst danach der Rumpf der ctors von Base bearbeitet, seh ich das richtig?

    Ist das auch der Grund, warum man Constructor Initializer Lists Zuweisungen im Ctor Rumpf vorziehen soll, da die member sonst eventuell zuerst default initialisiert würden, nur um sie anschließend mit einem anderen Wert zu assignen?

    Mich interessiert jetzt, wie in dieses Gefüge die In-Class Initialization reinpasst. An welcher Stelle wird diese abgearbeitet? Nach oder vor der Ctor Initialization List?


  • Mod

    Rekursiv gilt folgende Reihenfolge:
    * Virtuelle Basisklassen, in einer komplizierten Reihenfolge, deren genaue Beschreibung ich mir mal spare, da man so gut wie nie virutelle Basisklassen hat
    * Basisklassen, in der Reihenfolge ihrer Angabe von vorne nach hinten
    * (Nicht-statische) Member, in der Reihenfolge ihrer Angabe von vorne nach hinten
    * Ausführung des Codekörpers des Konstruktors

    Der Codekörper des Konstruktors der aktuellen Klasse wird also nach den ganzen Initialisierungen ausgeführt. Alle Initialisierungen sind zu der Zeit schon lange abgeschlossen! Der Körper des Konstruktors führt gegebenenfalls noch Aufgaben aus, die über die Initialisierung hinaus gehen. Mit der Initialisierung selber hat er überhaupt nichts zu tun. Wenn dir jemand etwas anderes erzählt hat, wurdest du belogen.

    Der Codekörper des Konstruktors einer eventuellen Basisklasse wird nach obigem Schema aber irgendwann mitten im Gesamtprozess ausgeführt, nämlich nachdem alle Untermember und Basisklassen der Basisklasse initialisiert wurden und bevor die nächste Basisklasse oder der erste Member der abgeleiteten Klasse dran kommt.

    In-Class Initialization hat überhaupt gar nichts mit der Reihenfolge der Initialisierung zu tun hat; genausowenig wie die Reihenfolge der Angaben in der Initialisierungsliste einen Einfluss auf die oben angegebene Reihenfolge hat. Die Reihenfolge wird allein durch obige Regeln festgelegt. Die Initialisierungsliste und in-class Initialisierung legen die Parameter fest, mit der die Initialisierung erfolgt, nicht die Reihenfolge. Dabei werden eventuell vorhandene Parameter in der Initialisierungsliste der in-class Initialisierung vorgezogen.



  • Sewing schrieb:

    Also wird bei

    Base *obj = new Derived(5);
    

    zunächst der Ctor von Derived aufgerufen, der dann zum Ctor der basisklasse weiterleitet.

    Nein. Andersrum! Also erst wird die Basisklasse fertig konstruiert. Wenn der Derived-Constructor läuft, ist das Objekt schon als Base-Objekt fertig initialisiert.
    Edit: ok, vielleicht meintest du das auch so.

    Dort werden dann laut stack overflow alle datamember initialisiert, zB durch die Contructor initializer list und erst danach der Rumpf der ctors von Base bearbeitet, seh ich das richtig?

    Die Reihenfolge ist: base initializer list, base Rumpf, derived initializer list, derived Rumpf.

    Vor dem Rumpf werden ALLE Variablen initialisiert.
    Wenn du im Rumpf a = b schreibst, wurde a vorher default-initialisiert.

    Kannst du doch auch einfach testen!

    #include <iostream>
    
    using namespace std;
    
    struct S {
        S(const char *s) { cout << "S::S(" << s << ")\n"; }
        S() { cout << "S::S\n"; }
        S& operator=(const S &) { cout << "copy S\n"; return *this; }
        S(const S&) { cout << "copy S\n"; }
    };
    
    struct B {
        B() : s1("x1") {
            s2 = s1;
        }
        S s1;
        S s2;
        virtual ~B() { cout << "~B()\n"; }
    };
    
    struct D : public B {
        D() : B::B(), s3("x2") {
            s4 = s2;
        }
        ~D() { cout << "~D()\n"; }
        S s3;
        S s4;
    };
    
    int main() {
        D d;
    }
    

    (Edit 2: Destruktor hinzugefügt)



  • wob schrieb:

    Sewing schrieb:

    Also wird bei

    Base *obj = new Derived(5);
    

    zunächst der Ctor von Derived aufgerufen, der dann zum Ctor der basisklasse weiterleitet.

    Nein. Andersrum!

    Danke. Damit meinte ich allerdings, das der Derived Ctor zunächst nichst anderes macht, als den Base Class ctor aufzurufen, den der braucht ja eventuell Argumente oder?



  • Ja, so meinte ich das. Hatte eben parallel zu deiner Frage meinen Post noch editiert, weil mir aufgefallen war, dass ich dich falsch verstanden haben könnte 🙂

    Klar, wenn da Base::Base(whatever) steht, wird das natürlich weitergeleitet. Aber sobald die erste "eigene" Variable initialisiert wird, muss das Base-Objekt schon fertig initialisiert sein.



  • vielen Dank


Log in to reply