可能重复:
C++ virtual function from constructor
Calling virtual functions inside constructors
这个问题在采访中被问到了。
我想我已经正确回答了第一部分但不确定第二部分。事实上,我对第二部分毫无头绪。
当我尝试使用virtual void foo() = 0;
在我的编译器上运行相同的问题时,它会抛出
错误“未定义引用`A :: Foo()'”
#include <iostream>
using namespace std;
class A
{
public:
A()
{
this->Foo();
}
virtual void Foo()
{
cout << "A::Foo()" << endl;
}
};
class B : public A
{
public:
B()
{
this->Foo();
}
virtual void Foo()
{
cout << "B::Foo()" << endl;
}
};
int main(int, char**)
{
B objectB;
return 0;
}
答案 0 :(得分:8)
实例化B
对象时,会发生以下情况:
B
的构造函数被调用。
首先,B
的构造函数调用基础构造函数A()
。
在A
的构造函数中,函数调用被调度到A::foo()
,因为this
具有静态和动态类型{{1} (如果你想的话,别无其他意义);现在A*
子对象已完成。
现在A
的构造函数体已运行。这里函数调用被分派到B
。现在整个B::foo()
对象已完成。
如果B
是纯虚拟的,则步骤(3)会导致未定义的行为;比照标准中为10.6 / 4.
(在您的情况下可能表现为链接器错误,因为编译器会优化以静态解析调用,并且找不到符号A::foo()
。)
答案 1 :(得分:4)
在第二种情况下,你有未定义的行为(在类T构造函数中调用类T的纯虚拟),因此输出可以是任何东西 - 如果它甚至编译。
要理解的主要是在C ++中,当对象的T构造函数执行时,对象的动态类型为T.
这使得从C ++构造函数调用虚函数变得安全。您没有调用未初始化的派生类子对象。相比之下,在Java和C#(以及类似语言)中,您可以轻松获得这种错误,这很常见。
干杯&amp;第h。,
答案 2 :(得分:1)
构造函数中的方法被调度为类的动态类型。 A的构造函数用动态类型A调用Foo。(参见AlfP.Steinbach对正确定义的评论)
如果A是抽象基础,那么错误是因为它试图调用纯虚方法。
A()
{
this->Foo(); // call A::Foo
}
来自Scott Meyers的Effective C ++:
这种看似违反直觉的行为有充分的理由。因为基类构造函数在派生类构造函数之前执行,所以在基类构造函数运行时尚未初始化派生类数据成员。如果在基类构造期间调用的虚函数归结为派生类,派生类函数几乎肯定会引用本地数据成员,但这些数据成员尚未初始化。这将是未定义行为和深夜调试会话的直接票。调用尚未初始化的对象的某些部分本质上是危险的,因此C ++让你无法做到。
答案 3 :(得分:1)
将调用构造函数,以便首先构造父类,以便不会对未定义的对象产生任何依赖。因此它是A :: A,然后是B :: B.编辑:B的构造函数也可能直接调用A,如Kerrek SB says - 最终效果是相同的。
在第一种情况下,输出将是“A :: Foo()”,后跟“B :: Foo()”。在A的构造时B还不存在,其虚函数还不是对象的一部分。
在第二种情况下,您将调用纯虚函数A :: Foo,它将生成错误或拒绝完全编译。