Invoking a virtual function inside a constructor using an object expression

the code:

#include <iostream> using std::cout; using std::endl; struct A { virtual void foo() { cout << "A" << endl; } A(){ } }; struct B : A { B(); virtual void foo() { cout << "B" << endl; } }; B b; B::B() { b.foo(); foo(); } struct C : B { virtual void foo() { cout << "C" << endl; } C() : B(){ } }; C c; int main(){ } 

Demo

When a virtual function is called directly or indirectly from the constructor or destructor, including during the time of construction or destruction of the classes of non-static data classes and the object to which the call is to an object (call it x) under construction or destruction of, the function is called final overrider in class constructors or destructors, not one overriding it in a more derived class. If the call to the virtual function uses an explicit member of the class (5.2.5) and the expression object refers to the full object x or one of the object base classes, but not x or one of its base class subobjects, the behavior is undefined.

I'm trying to get UB for

If the call to a virtual function uses explicit access to a class member (5.2.5), and the expression of the object refers to the full object x [...]

It is not clear what the full object x means, where x is the object. Is this the same as a complete object of type x ?

+5
source share
2 answers

§1.8 [intro.object] / p2-3:

Objects may contain other objects called subobjects. A subobject can be a member subobject (9.2), a base class subobject (clause 10), or an array element. An object that is not a subobject of any object, another object is called a complete object.

For each object x there is an object called the complete object x , defined as follows:

  • If x is a complete object, then x is a complete object x .
  • Otherwise, the complete object x is the complete object (unique) object containing x .

In essence, the suggestion you made does the execution of static_cast<C*>(this)->foo(); in the B constructor, undefined behavior in your code, even if the constructed complete object is C The standard actually gives a pretty good 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 } 

In fact, you can already see the undefined behavior in this example if you run it : The Ideone compiler (GCC) actually calls V::f() on the line a->f(); Although the pointer refers to a fully built subobject A .

+6
source

This is quite complicated, and I had to edit the message several times (thanks to the guys who helped me), I will try to make it simple and refer to the N3690:

§12.7.4 conditions

Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2).

and this is what you did in constructor B

 B::B() { b.foo(); // virtual foo(); // virtual } 

This is completely legal. This pointer (implicitly used in the second function call) always points to the constructed object.

Then the standard also says:

When a virtual function is called directly or indirectly from the constructor and the object to which the call is applied, if the object (call it x) is under construction or destruction, the called function is the final redirector in the class of constructors or destructors and does not redefine it in a more derived class (such ignore more derived versions of the function)

so the vtable didn’t completely go through, as you might think, but settled on the version of the virtual function of the constructor class (see http://www.parashift.com/c%2B%2B-faq-lite/calling-virtuals-from- ctors.html ).

Still legal.

Finally, to your point:

If calling a virtual function uses explicit access to a class member, for example. (object.vfunction () or object-> vfunction ()), and the expression of the object refers to the full object x or one of the subobjects of the base class of objects, but not to x or one of its subobjects of the base class (i.e. not to the object , construction or one of its subobjects of the base class), undefined behavior.

To understand this sentence, we first need to understand what the full object x means:

§1.8.2

Objects may contain other objects called subobjects. A subobject can be a member subobject (9.2), a base subclass of a class object (section 10), or an array element. An object that is not a subobject of any other object is called a complete object.

For each object x, there is some object called the complete object x, defined as follows:

- If x is a complete object, then x is a complete object x.

- Otherwise, the complete object x is the complete object of the (unique) object that contains x

if you combine the previous previous pass, you will get that you cannot call a virtual function that refers to the "full type" of the base class (that is, a derived object that is not yet constructed) or the object that owns this element of the element or array.

If you must explicitly reference the C in B constructor:

 B::B() { static_cast<C*>(this)->foo(); // Refers to the complete object of B, ie C } struct C : B { C() : B(){ } } 

then you would have undefined behavior.

The intuitive (more or less) reason is that

  • A virtual or member function call is allowed in the constructor, and in the case of virtual functions, it “stops the virtual hierarchy” before this object and calls its version of the function (see http://www.parashift.com/c%2B%2B-faq-lite /calling-virtuals-from-ctors.html )

  • In any case, if you do this from a subobject referencing the full object of that subobject (re-read the standard pass), then it has undefined behavior

Practical use: Do not call virtual functions in your constructors / destructors unless you are sure you can .

If I have something wrong, please let me know in the comments below and I will correct the message. Thanks!

+2
source

Source: https://habr.com/ru/post/1201381/


All Articles