Understanding base class initialization

69 views Asked by At

Consider the program:

#include<iostream>
#include<vector>

struct A
{
    int a;
    A(int) { }
    virtual int foo(){ std::cout << "base" << std::endl; return 5; }
};

struct B : A
{
    int b;
    B(): b(9), A(foo()) { }
    virtual int foo(){ std::cout << "derived" << std::endl; return 6; }
};

B b; //prints derived

int main(){ }

DEMO

What Scott Meyers in his Effective C++ said about that was :

During base class construction of a derived class object, the type of the object is that of the base class.

So, I expected base to be printed instead, because we was under the base class class construction while invoking the foo function. What did I miss? Maybe it's UB? If so, please point me out to the relevant section.

3

There are 3 answers

3
ixSci On BEST ANSWER

Scott means that while you are in the base class constructor usual rules for virtual function don't work. So if you are in the base class constructor then any virtual functions(of that base class) called inside the ctor will be called on the object which is currently under construction in this very ctor.

So your code prints correct result: foo() is called in the B's ctor not in the parent constructor. If you called foo inside the A ctor you would have base printed.

Still the behavior is deemed undefined according to the standard:

[12.6.2/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). However, if these operations are performed in a ctor-initializer (or in a function called directly or indirectly from a ctor-initializer) before all the mem-initializers for base classes have completed, the resultof the operation is undefined.

But you should understand that "undefined" here means that you might use some internal state in the called function. Since you don't the behavior will be consistent but standard still deems it undefined. The "undefined" part have nothing to do with what is printed but rather with what might be accessed in the member function.

0
Steephen On

What said by Scott Mayers is right but your program is wrong.

 B(): b(9), A(foo()) { } 

This statement is absolutely wrong:

In derived class constructor initialization list you should first call base class constructor before initializing derived class member object. You cant invoke non static member function before instantiating the object.

#include<iostream>
#include<vector>

struct A
{
    int a;
    A(int) { std::cout<<"base constructed\n"; }
    virtual int foo(){ std::cout << "base" << std::endl; return 5; }
};

struct B : A
{
    int b;
    B():  A(6), b(9) { std::cout<<"derived constructed"; }
    virtual int foo(){ std::cout << "derived" << std::endl; return 6; }
};



int main(){
    B b; //prints derived
    }

O/P

base constructed

derived constructed
2
Ed Heal On

Just do not use virtual functions in constructors - item 9