何时为派生类初始化vptr?

时间:2018-10-16 08:26:04

标签: c++

最近,我一直在阅读“在c ++对象模型内部”。它说vptr在调用基类的构造函数之后初始化。所以我进行了测试:

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;
}

这是结果:

2

我的问题是,在调用基类A的构造函数之前,类B是否首先设置其vptr?

3 个答案:

答案 0 :(得分:2)

您问题的答案为。如果在父构造函数执行之前设置vptr,则意味着该构造函数将覆盖它。

关于您在代码中看到的行为:对构造函数内部正在构造的对象的虚拟函数的任何调用都无需vptr来解决。因此,您的代码实际上等效于:

B() : A(B::vfunc()) {
    }

没有虚拟电话。相关标准措辞([class.cdtor] p3):

  

可以调用成员函数,包括虚拟函数(13.3)   在建造或破坏期间(15.6.2)。当虚函数   直接从构造函数或间接调用   破坏者,包括在建造或销毁   类的非静态数据成员以及调用的对象   适用于正在建造或销毁的物体(称为x),   所调用的函数是构造函数或   析构函数的类,而不是在派生更高的类中覆盖它的。

答案 1 :(得分:2)

C ++标准对vptr或设置它一无所知。

但是,标准确实说virtual调用取决于对象调用时的类型。在A的构造函数中,类型为A,在B的构造函数中,类型为B。但这在构造函数的内部。由于初始化器列表还包括基类的初始化,因此B::B()的初始化器列表开始执行时,该对象还没有 any 类型。

正式:

  

§12.6.2.13:成员函数(包括虚拟成员函数,   10.3)可以针对正在构造的对象进行调用。 [...]但是,如果这些操作在ctor初始化程序(或函数)中执行   在所有之前,直接或间接从ctor初始化程序调用)   基本类的mem初始化程序已经完成,   操作未定义

(强调我的)

答案 2 :(得分:0)

在类构造/破坏期间,任何虚拟调用都将被解析,就好像该类的替代是最终替代。我几乎100%确信此行为是标准命令,但您最好检查一下。示例:

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;
}

它将产生输出

1A
2B
2C
2B
1A

因为调用顺序是     C :: C B :: B A :: A A :: f1 B :: f2 C :: f2 C ::〜C B ::〜B B ::〜f2 A ::〜A A :: f1