Understanding Base Class Initialization
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(){ }
What Scott Meyers said in his Effective C++
about it:
At the time of constructing the base class of an object of a derived class, the type of object is the base class.
So, I expected what base
would be printed instead, because we were under constructing a base class by calling a function foo
. What am I missing? Could it be UB? If so, please point me to the appropriate section.
source to share
Scott means that although you are in the base class constructor, the normal rules for a virtual function do not work. So if you are in the constructor of the base class, then any virtual functions (of that base class) called inside the ctor will be called on the object that is currently being built in that very ctor.
This way your code prints the correct result: foo()
called in B
ctor not in parent constructor. If you called foo
inside the A
ctor, you would print base
.
The behavior is still undefined by the standard:
[12.6.2 / 13] Member functions (including virtual member functions, 10.3) can be called on an object under construction. Likewise, an object under construction can be the operand of a typeid operator (5.2.8) or dynamic_casting (5.2.7). However, if these operations are performed in the ctor initializer (or in a function called directly or indirectly from the ctor initializer) before all mem initializers for base classes have completed, the result of the operation is undefined.
But you should understand that "undefined" here means that you can use some internal state in the called function. Since you don't know, the behavior will be consistent, but the standard still considers it undefined. The "undefined" part has nothing to do with what is printed, but rather what can be accessed in the member function.
source to share
What Scott Myers says, but your program is wrong.
B(): b(9), A(foo()) { }
This statement is completely wrong:
In the initialization list of the derived class constructor, you must first call the base class constructor before you initialize the class member object. You cannot use a non-stationary member function before creating an 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
source to share