我有这门课,
class Base {
public:
void foo();
};
int main()
{
Base b;
}
尽管main
没有定义,但 foo()
将编译时没有任何错误。但b.foo();
将导致编译错误。
此外,即使对于constructor
和operator=
,我也可以在不定义它们的情况下声明它们,只要我不触发它们就会编译它。
同样,我将虚拟函数添加到Base
,
class Base {
public:
void foo();
virtual void bar(); // no defition is gonna be provided.
};
现在,main
无法编译,而是出现错误:
undefined reference to vtable for Base
这让我感到困惑,因为之前,main
只要foo()
没有被调用就可以编译,但是现在我添加了bar()
并且根本没有被调用。< / p>
为什么不在这种情况下编译?
答案 0 :(得分:15)
这完全取决于编译器。两种情况都不需要诊断。
9应定义或声明在类中声明的
virtual
函数 纯粹(10.4)在该类中,或两者兼而有之;但无需诊断 (3.2)。 [强调我的]
要理解为什么会发生这种情况,让我们来看看它是如何运作的。
每个翻译单元生成一个目标文件,每个目标文件都有导出(导出的符号)和导入(它想要的符号)。
第一个例子很简单 - 导入时只需使用foo
。链接器没有理由寻找符号,所以它没有。
第二个使用virtual
方法,有点复杂。大多数编译器(如果不是全部)都需要有效的虚函数表。这意味着在链接时,声明非纯虚方法的所有类都必须导出这些方法。这比非virtual
情况更严格,因为实现实际上并不知道函数是否被调用(它可以被称为多态)。
答案 1 :(得分:5)
第一个版本可以编译,因为链接器不需要搜索对象foo
。它无处使用。
但是当你创建一个虚函数时,vtable(用于动态调度)的构造需要一个函数Base::bar
的地址(它引用它),因此链接器需要找到它的实现。
答案 2 :(得分:3)
当创建多态对象(具有至少一个虚函数的类的实例)时,它必须具有指向虚拟表的虚拟表指针。每个多态类都有一个构造一次的虚拟表。 由编译器实现以允许虚拟表不完整。此表基于虚函数定义进行填充。
如果没有实现任何虚函数且函数不是纯虚函数,那么一些编译器会因虚拟表不完整而给出编译器错误。
对于简单(非虚拟)函数,如果从未使用函数声明,则将忽略该函数声明。即使使用它,我们也会收到链接器错误而不是编译器错误。
答案 3 :(得分:1)
在类中声明的虚函数应该在该类中定义或声明为纯,或者两者都是(C ++ 03标准)。