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

C++ 为什么只有声明的虚拟函数会导致编译器错误?,c++,C++,我有这门课 class Base { public: void foo(); }; int main() { Base b; } main将编译而不出错,尽管foo()没有定义。但是b.foo()将导致编译错误 此外,即使对于构造函数和操作符=,我也可以在不定义它们的情况下声明它们,只要我不触发它们,它就会编译 问题: 同样,我在Base中添加了一个虚拟函数 class Base { public: void foo();

我有这门课

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

int main()
{
    Base b;
}
main
将编译而不出错,尽管
foo()
没有定义。但是
b.foo()将导致编译错误

此外,即使对于
构造函数
操作符=
,我也可以在不定义它们的情况下声明它们,只要我不触发它们,它就会编译

问题: 同样,我在
Base
中添加了一个虚拟函数

class Base {
    public:
        void foo();
        virtual void bar();  // no defition is gonna be provided.
};
现在,
main
无法编译,而是出现了一个错误:

undefined reference to vtable for Base 未定义对vtable的基本引用 这让我有点困惑,因为以前,
main
可以编译,只要不调用
foo()
,但现在我添加了
bar()
,它就根本不被调用


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

第一个版本可以编译,因为链接器不需要搜索对象
foo
。它哪儿也没用过


但是,当您创建虚拟函数时,(用于动态分派)的构造需要函数的地址
Base::bar
(它引用它),因此链接器需要找到它的实现。

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

10.3虚拟函数[类虚拟] 9应定义或声明类中声明的
虚拟
函数 在该类别中为纯(10.4),或两者兼而有之;但是不需要诊断 (3.2). [我的重点]

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

每个翻译单元生成一个对象文件,每个对象文件带有导出(导出的符号)和导入(需要的符号)

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


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

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

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

如果未实现任何虚拟函数,且该函数不是纯虚拟函数,则由于虚拟表不完整,某些编译器会给出编译器错误


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

是否不提供定义允许链接器针对不同的实现进行链接?至于虚函数,我希望纯虚函数(virtual void bar()=0)编译没有问题。+1:非常有趣的问题!:)这不是编译器的错误,而是链接器的错误(所以不是编译错误,而是链接错误)。@elmo,很高兴知道,:-)你的答案肯定比我的好+1.我这么做的原因是,我只是好奇如果一个函数没有定义就被声明会发生什么,:-)@Alcott好吧,好奇的荣誉:)你能得到一个链接吗?
10.3虚拟函数[class.Virtual]
来自@Luchian Grigore,不管怎么说!因此,在C++中,只要不使用<代码>函数< /> >或<代码>变量< /C>,我可以在不定义它们的情况下声明它们,对吗?我刚才只是尝试从问题中编译源代码,而将Bar()定义为纯虚函数。因为类是抽象基,所以不需要实现,所以不创建vtable?代码编译。。