2

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?

刘卫东
  • 325
  • 1
  • 7
  • Possible duplicate of [Calling virtual functions inside constructors](https://stackoverflow.com/questions/962132/calling-virtual-functions-inside-constructors) – hellow Oct 16 '18 at 08:29
  • 2
    You don't have any call through vptr in your code. `cs` is a normal "method" of your B class. – ixSci Oct 16 '18 at 08:29
  • Sorry, I made a mistake. cs() is actually vfunc(). I've already edited the question. – 刘卫东 Oct 16 '18 at 08:44
  • You also made your `A` class constructor private – Rhathin Oct 16 '18 at 08:44
  • Sorry, another mistake I've made. – 刘卫东 Oct 16 '18 at 08:55
  • That actually doesn't matter, if you have a call to a virtual function in the constructor it won't use vptr to call it, it will use the one it can find w/o any vptr magic. Thus you have a simple direct call to `B::vfunc` which is UB, btw. Because you are not allowed to call member functions until its parents' ctors are completed. – ixSci Oct 16 '18 at 08:58

3 Answers3

3

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.

ixSci
  • 13,100
  • 5
  • 45
  • 79
2

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)

MSalters
  • 173,980
  • 10
  • 155
  • 350
0

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

Andrew Kashpur
  • 736
  • 5
  • 13