我有一个抽象基类Foo
,它有一个名为bar
的抽象方法。
我正在从bar
的构造函数中调用Foo
。我希望子类覆盖bar
(毕竟它是抽象的),然后Foo
在每个实例上调用重写的bar
:
class Foo
{
public:
Foo() { bar(); }
virtual void bar() = 0;
}
然而,我收到一个错误:
foo.obj:-1: error: LNK2019: unresolved external symbol "protected: virtual void __cdecl Foo::bar(void)" (?bar@Foo@@MEAAXXZ) referenced in function "public: __cdecl Foo::Foo(void)" (??0Foo@@QEAA@XZ)
这种链接器错误通常意味着我已经定义了一些东西,但没有声明它(或者反过来说),这似乎又是这种情况。
如果我像bar
这样添加一个定义,那么一切正常:
void Foo::bar() {}
这是预期的错误,还是链接器中的错误? 如果是故意的,为什么呢? 我不明白为什么我不能从构造函数中调用抽象方法? 我想到的唯一原因是基类'构造函数在子类'方法被定义之前被调用,但我仍然认为我不应该得到这个错误?
我正在使用Qt Creator 2.7.0 - Based on Qt 5.0.2 (32 bit)
答案 0 :(得分:4)
在构造函数中,动态类型*this
为Foo
,并且没有已定义的函数Foo::bar
。不要在构造函数中调用虚函数。 (除非你知道你在做什么,在这种情况下你会知道如何提供一个定义。)
答案 1 :(得分:2)
你的问题很简单但很难:Foo
的构造函数在派生类的构造函数之前运行。因此,虚函数设置为Foo
设置的任何值 - 未实现。
您必须等到构造完成后才能以您期望的方式使用虚拟功能,并且所有vtable条目都设置为其派生程度最高的版本。
虽然可以将构造分解为三个部分(首先vtable由每个构造函数更新,而不是所有初始化程序,最后执行所有构造函数),C ++标准委员会已经决定不采用这种方式。
除了编译器构建器的简单性之外,还有一些优点:如果您可以访问更多派生类成员(通过调用重写的virtual
函数),它们将不会被初始化(或者甚至是构造的)所以更多派生类的任何函数都不应该触及任何非平凡的数据成员,这使得所有内容复杂化,而不仅仅是说你不应该在构造期间调用虚函数。