15

我有这堂课,

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

int main()
{
    Base b;
}

main将编译没有任何错误,虽然foo()没有定义。但是b.foo();会导致编译错误。

此外,即使对于constructorand operator=,我也可以只声明它们而不定义它们,只要我不触发它们,它就会编译。

问题

再次,我将一个虚函数添加到Base,

class Base {
    public:
        void foo();
        virtual void bar();  // no defition is gonna be provided.
};

现在,main无法编译,而是出现错误:

未定义对 Base 的 vtable 的引用

好吧,这让我有点困惑,因为以前,main只要foo()不调用就可以编译,但现在我添加bar()了它,根本不调用它。

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

4

4 回答 4

16

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

10.3 虚函数 [class.virtual]

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

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

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

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

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

于 2013-01-14T10:21:57.913 回答
5

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

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

于 2013-01-14T10:19:22.353 回答
3

创建多态对象(具有至少一个虚函数的类的实例)时,它必须有一个指向虚表的虚表指针。每个多态类都有一个构造一次的虚拟表。允许虚拟表不完整取决于编译器的实现。该表是根据虚函数定义填充的。

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

在简单(非虚拟)函数的情况下,如果从未使用过函数声明,它将被忽略。即使使用它,我们也会得到链接器错误而不是编译器错误。

于 2013-01-14T10:25:00.293 回答
1

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

于 2013-01-14T10:24:04.637 回答