Reputation: 325
Recently,I've been reading "inside the c++ object model". It says that the vptr initializes after calling the base class's constructor. So I ran a test:
class A {
public:
A(int i) {
std::cout << i << std::endl;
}
virtual int vfunc() {
return 1;
}
};
class B : public A {
public:
B() : A(vfunc()) {
}
virtual int vfunc() {
return 2;
}
};
int main() {
B b;
}
Here is the result:
2
My question is, does class B set its vptr first before calling the base class A's constructor?
Upvotes: 2
Views: 324
Reputation: 179981
The C++ Standard says nothing about vptr
or setting it.
However, the Standard does say that virtual
calls depend on the type of the object at the moment of the call. In the constructor of A
, the type is A
, in the constructor of B
the type is B
. But that's inside the body of constructor. Since the initializer list also includes initialization of the base classes, at the time the initializer list of B::B()
begins to execute the object does not have any type yet.
Formally:
§ 12.6.2.13: Member functions (including virtual member functions, 10.3) can be called for an object under construction. [...] 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 result of the operation is undefined.
(Emphasis mine)
Upvotes: 2
Reputation: 13708
The answer to your question is no. If vptr
would be set before the parent constructor gets executed it would mean that that constructor would overwrite it.
As to the behavior you see in your code: any call to the virtual function for the object under construction inside its constructor is resolved w/o vptr
. So your code is actually an equivalent to:
B() : A(B::vfunc()) {
}
There is no virtual call. Relevant standard wording([class.cdtor]p3):
Member functions, including virtual functions (13.3), can be called during construction or destruction (15.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.
Upvotes: 3
Reputation: 746
during class construction/destruction any virtual call would be resolved as if the class' override is final override. I'm almost 100% sure this behavior is standard mandated, but you better check this out. Example:
// Example program
#include <iostream>
#include <string>
class A{
public:
virtual void f1(){
std::cout<<"1A"<<std::endl;
}
virtual void f2(){
std::cout<<"2A"<<std::endl;
}
A(){
f1();
}
virtual ~A(){
f1();
}
};
class B : public A{
public:
virtual void f1(){
std::cout<<"1B"<<std::endl;
}
virtual void f2(){
std::cout<<"2B"<<std::endl;
}
B():A(){
f2();
}
~B(){
f2();
}
};
class C: public B{
public:
virtual void f1(){
std::cout<<"1C"<<std::endl;
}
virtual void f2(){
std::cout<<"2C"<<std::endl;
}
C():B(){
f2();
}
~C(){}
};
int main(){
C c;
return 0;
}
it will produce output
1A
2B
2C
2B
1A
because call sequence would be C::C B::B A::A A::f1 B::f2 C::f2 C::~C B::~B B::~f2 A::~A A::f1
Upvotes: 0