为什么这个编译,而不是链接
Why does this compile, but not link?
#include <iostream>
using namespace std;
class C
{
public:
virtual void a();
};
class D : public C
{
public:
void a() { cout<<"D::an"; }
void b() { cout<<"D::bn"; }
};
int main()
{
D a;
a.b();
return 0;
}
我得到一个关于undefined reference to 'vtable for C'
的链接错误。这意味着什么?为什么会这样?
我知道问题显然是基类有一个从未定义的非纯虚函数,但如果我从不调用它,为什么这会打扰链接器?为什么它和其他函数不同,我声明了,没有定义,如果我不调用它,我就没事了?
我我对基本细节感兴趣。
大多数c++编译器的实现都会为每个类生成一个虚函数表,它是虚函数的函数指针表。与任何其他数据项一样,虚值表只能有一个定义。一些c++编译器在编译类型中第一个声明的虚函数的实现时生成这个虚函数表(这保证了虚函数表只有一个定义)。如果你没有提供第一个虚函数的实现,编译器就不会生成虚函数表,并且链接器会报错虚函数表缺失,并给出链接错误。
可以看到,具体细节取决于所选择的编译器和链接器的实现。并不是所有的工具链都是一样的
问:我知道问题显然是基类有一个从未定义的非纯虚函数
A:这就是你问题的答案了。问:为什么它与我声明而不定义的任何其他函数不同,如果我从不调用它,我就没事了?
A:因为它不仅仅是一个"功能"。这是一个虚拟类方法。建议:
声明三个不同的类:
一个简单的方法
2)虚方法(如上面的"C")
3)抽象虚方法(= 0)
生成装配输出(例如:
比较三种情况。如果创建了"构造函数",请仔细注意:)
如果你想要C
是一个纯虚拟基类,你需要告诉编译器"将没有a()
的实现",你通过使用... a() = 0;
来声明类来做到这一点。编译器试图找到一个基类虚函数表,但是没有一个!
在几种情况下可能需要实际的函数定义。您已经命名了这样一种情况:如果调用,则必须定义该函数。然而,这还不是全部。考虑这段代码
void foo();
int main() {
void (*p)() = &foo;
}
此代码从不调用foo
。但是,如果您尝试编译它,典型的编译器会抱怨在链接阶段缺少foo
的定义。因此,取函数的地址(即使你从不调用它)恰好是必须定义函数的情况之一。
这就是虚函数问题的由来。虚函数通常通过虚表实现:虚表包含指向虚函数定义的指针。这个表是由编译器事先无条件地形成和初始化的,而不管您是否实际调用了这些函数。编译器实际上会隐式地获取程序中每个非纯虚函数的地址,并将其放入相应的表中。因此,即使不调用每个非纯虚函数,也需要定义。
确切的虚拟机制是一个实现细节,因此您可能会根据您的情况(例如缺少函数定义或缺少虚拟表本身)而最终出现不同的特定错误,但这些错误的根本原因是我上面描述的。
- 我可以将一个用clang c++11编译的对象与另一个用c++17编译的对象链接起来吗
- 生成一个生成文件,该生成文件使用Automake在一个步骤中编译和链接所有源文件
- Red Hat:使用<atomic>编译很好,但链接器找不到__atomic_store_16;什么库?
- 在发布模式下启动使用库的静态链接编译的应用程序时出错
- 编译时出错 - 链接.cpp和头文件
- 仅为从某种语言编译的对象添加链接库?
- C++ 为什么在定义的编译和链接之前引用外部实例的程序
- Visual Studio 2017 ARM 交叉编译链接器崩溃
- 在调试配置中编译工作正常,但发布会给出链接错误
- 可视化工作室项目中C++预编译标头未正确链接
- std::to_chars在MacOS/clang上编译但不链接
- 使用共享库编译可执行文件时仅链接所需的符号
- 编译时出现Boost.python链接错误
- 微控制器的首次 gcc 链接器脚本编译但不运行
- CUDA 和C++链接/编译,程序在 cudaMalloc 上崩溃
- 链接/编译时间有关静态模板库
- 链接/编译使用 boost/filesystem.hpp 的程序
- 我们可以动态链接编译的静态库吗?
- 如何在我的项目中使用CMake链接编译的cmph库
- 在msvc++ 2010中,在调试配置中用静态链接编译DLL时,链接器错误