最近,我一直在阅读“在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?
答案 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%确信此行为是标准命令,但您最好检查一下。示例:
// 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