为什么只有声明的虚函数会导致编译错误?

时间:2013-01-14 10:14:45

标签: c++

我有这门课,

class Base {
    public:
        void foo();
};

int main()
{
    Base b;
}
尽管main没有定义,但

foo()将编译时没有任何错误。但b.foo();将导致编译错误。

此外,即使对于constructoroperator=,我也可以在不定义它们的情况下声明它们,只要我不触发它们就会编译它。

问题

同样,我将虚拟函数添加到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>

为什么不在这种情况下编译?

4 个答案:

答案 0 :(得分:15)

这完全取决于编译器。两种情况都不需要诊断。

10.3虚函数[class.virtual]

  

9应定义或声明在类中声明的virtual函数   纯粹(10.4)在该类中,或两者兼而有之;但无需诊断   (3.2)。 [强调我的]

要理解为什么会发生这种情况,让我们来看看它是如何运作的。

每个翻译单元生成一个目标文件,每个目标文件都有导出(导出的符号)和导入(它想要的符号)。

第一个例子很简单 - 导入时只需使用foo。链接器没有理由寻找符号,所以它没有。

第二个使用virtual方法,有点复杂。大多数编译器(如果不是全部)都需要有效的虚函数表。这意味着在链接时,声明非纯虚方法的所有类都必须导出这些方法。这比非virtual情况更严格,因为实现实际上并不知道函数是否被调用(它可以被称为多态)。

答案 1 :(得分:5)

第一个版本可以编译,因为链接器不需要搜索对象foo。它无处使用。

但是当你创建一个虚函数时,vtable(用于动态调度)的构造需要一个函数Base::bar的地址(它引用它),因此链接器需要找到它的实现。

答案 2 :(得分:3)

当创建多态对象(具有至少一个虚函数的类的实例)时,它必须具有指向虚拟表的虚拟表指针。每个多态类都有一个构造一次的虚拟表。 由编译器实现以允许虚拟表不完整。此表基于虚函数定义进行填充。

如果没有实现任何虚函数且函数不是纯虚函数,那么一些编译器会因虚拟表不完整而给出编译器错误。

对于简单(非虚拟)函数,如果从未使用函数声明,则将忽略该函数声明。即使使用它,我们也会收到链接器错误而不是编译器错误。

答案 3 :(得分:1)

在类中声明的虚函数应该在该类中定义或声明为纯,或者两者都是(C ++ 03标准)。