Virtuelle Funktionsaufrufe im Konstruktor



  • Ist der Aufruf einer virtuellen Funktion im Konstruktor oder Destruktor undefiniertes Verhalten, oder ist garantiert, dass die Funktion der aktuellen Klasse aufgerufen wird?

    Im Standard konnte ich nur eine Aussage bezüglich pur virtuellen Funktionen finden:

    n3337 §10.4/6 schrieb:

    Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a
    virtual call (10.3) to a pure virtual function directly or indirectly for the object being created (or destroyed)
    from such a constructor (or destructor) is undefined.



  • Dürfte Designtechnisch schon ein Fehler sein. C# meckert sowas an, Java auch.



  • Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2).
    When a virtual function is called directly or indirectly from a constructor or from a destructor, including
    during the construction or destruction of the class’s non-static data members, and the object to which the
    call applies is the object (call it x) under construction or destruction, the function called is the final overrider
    in the constructor’s or destructor’s class and not one overriding it in a more-derived class. If the virtual
    function call uses an explicit class member access (5.2.5) and the object expression refers to the complete
    object of x or one of that object’s base class subobjects but not x or one of its base class subobjects, the
    behavior is undefined. [ Example:

    struct V {
      virtual void f();
      virtual void g();
    };
    struct A : virtual V {
      virtual void f();
    };
    struct B : virtual V {
      virtual void g();
      B(V*, A*);
    };
    struct D : A, B {
      virtual void f();
      virtual void g();
      D() : B((A*)this, this) { }
    };
    B::B(V* v, A* a) {
      f();      // calls V::f, not A::f
      g();      // calls B::g, not D::g
      v->g();   // v is base of B, the call is well-defined, calls B::g
      a->f();   // undefined behavior, a’s type not a base of B
    }
    

    — end example ]



  • (§ 12.7/1)

    Sie können einen Beitrag nicht so schnell nach Ihrem letzten absenden, bitte warten Sie einen Augenblick.


  • Mod

    Der dynamische Typ des Objekts ist derjenige, dessen Konstruktor gerade läuft. Du rufst also garantiert die zu dem Zeitpunkt am weitesten spezialisierte Version der virtuellen Funktion auf. Also die der aktuellen Klasse oder von einer ihrer Basisklassen, auf keinen Fall eine der abgeleiteten Klassen.

    Im Destruktor wird bekanntlich alles in umgekehrter Reihenfolge zerstört wie es erstellt wurde, da funktioniert das also auch.



  • Was in dem Zusammenhang ein Fehler ist, ist, wenn man auf diese Art eine Funktion aufruft die noch gar nicht definiert wurde.

    Und natürlich der allseits beliebte "ich starte einen Thread im Konstruktor meiner Thread Basisklasse der virtual this->run() aufruft" Fehler. Aber darum geht's hier ja nicht. 🤡



  • Vielen Dank, besonders für den Standardauszug! 🙂

    ψ



  • Kurze weitere Frage:

    class Base
    {
    public:
        ...
        void f() { v(); }
        virtual void v();
    };
    
    class Derived : public Base
    {
    public:
        Derived() { f(); } // hier
        virtual void v();
    };
    

    Ist hier garantiert, dass Derived::v aufgerufen wird? Wird also beim virtuellen Aufruf von Base::f aus erkannt, dass die meist-abgeleitete Klasse Derived ist, obwohl ihr Konstruktor erst gerade läuft?


  • Mod

    Ja.



  • Mal ne doofe Frage zu dem Thema...

    Wie sagt man da?
    Ich meine während Derived::Derived ist das Objekt ja noch nicht "fully constructed" -- merkt man auch schon daran dass Derived::~Derived nicht laufen würde wenn man hier ne Exception schmeisst.

    Wie nennt man den Zustand in dem sich der Derived Teil des Objekts zwischen Beginn und erfolgreicher Beendigung von Derived::Derived befindet? Ist das "partially constructed"?
    Also speziell im Gegensatz zu BEVOR Derived::Derived anfängt zu laufen.



  • das heißt dann wohl "object under construction"

    12.6.2 Initializing bases and members
    13 Member functions (including virtual member functions, 10.3) can be called for an object under construction. Similarly, an object under construction can be the operand of the typeid operator (5.2.8) or of a dynamic_cast (5.2.7).


Anmelden zum Antworten