我发现这很奇怪。在Sample_Base的ctor中,我调用bar(),它在内部调用fun(),这是一个纯虚函数。我得到错误“纯虚函数”调用。哪个好。现在,如果我直接从Sample_Base的ctor调用fun(),我就不会得到那个错误。我在VC ++ 2010 Beta 2和Ubuntu 9.10上的g ++ 4.4.1上尝试过它。我同意,除了纯虚拟析构函数之外,为纯虚函数提供实现是没有意义的。但是,我对这种行为感到有些惊讶。
class Sample_Base
{
public:
Sample_Base()
{
bar();
// fun();
}
/* This is code does not throw any error.
Sample_Base()
{
fun();
}
*/
void bar()
{
fun();
}
virtual void fun() = 0;
virtual ~Sample_Base();
};
Sample_Base::~Sample_Base()
{
}
void Sample_Base::fun()
{
std::cout << "Sample_Base::fun\n";
}
class Sample_Derived : public Sample_Base
{
public:
Sample_Derived() : Sample_Base()
{
fun();
}
void fun()
{
std::cout << "Sample_Derived::fun\n";
}
~Sample_Derived()
{
}
};
答案 0 :(得分:6)
当你直接调用函数时,由于你在构造函数中,编译器会解析对象的静态类型(Sample_Base
)并直接调用Sample_Base::fun()
。由于您为它提供了一个实现,编译器会找到该函数并且它可以正常工作。
当您通过bar()
间接调用它时,编译器必须使用动态类型,因此它会执行在运行时解析的虚拟调用。它失败了,因为它调用了一个纯虚函数。
所以区别在于它将函数绑定到调用。
答案 1 :(得分:4)
为纯虚函数提供定义不一定是没有意义的。将虚函数标记为纯意味着封闭类是抽象的,并且从中派生的任何类都是抽象的,除非该函数的最终重写不是纯虚函数。仍然可以通过显式非虚拟调用来调用纯虚函数。
在基类构造函数的主体中(但不是来自 ctor-initializer ),通过虚拟调用调用的虚函数的版本是在类本身或其基础之一中定义的版本而不是任何超越它的类(尚未构建)。这在12.7 [class.cdtor] / 3中明确指定。
在构造函数体中显式调用纯虚函数是合法的(即使用显式类限定符) - 尽管这需要函数定义一个体 - 但是通过以下方式调用纯虚函数是未定义的行为虚拟调用,只能从抽象类的构造函数或析构函数中进行。这在10.4 [class.abstract] / 6中明确指定。
答案 2 :(得分:1)
答案 3 :(得分:1)
在构造时,当调用Sample_Base
构造函数时,该对象尚未完全构造。具体来说,属于Sample_Derived
的部分尚未创建对Sample_Derived
覆盖的虚拟函数的调用,不会调用Sample_Derived
中的实现,而是Sample_Base
中定义的版本。由于该函数没有实现,因此会出现错误。
有关更多信息和可能的解决方法,请参阅this entry in the C++ FAQ Lite。
答案 4 :(得分:1)
此行为未定义,已明确定义:虚函数在构造函数和析构函数中不是虚函数。他们调用函数的静态版本。如果该函数是纯虚函数,则会导致VC中出现着名的“纯虚拟调用”错误。
我在多线程程序中看到了这种有趣的变化:一个对象在线程A上被破坏,而线程B正试图调用一个虚函数。构造函数或析构函数中没有虚函数调用,但我们仍然遇到纯虚拟调用错误。