刘卫东
刘卫东

Reputation: 325

When is the vptr initialized for derived classes?

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

Answers (3)

MSalters
MSalters

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

ixSci
ixSci

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

Andrew Kashpur
Andrew Kashpur

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:

http://cpp.sh/2gyz5

// 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

Related Questions