虽然还有其他关于堆栈溢出的问题,这些问题处理"对vtable的未定义引用"错误消息。以下代码编译或不编译取决于无参数构造函数C()是否在内联中实现。我知道成员函数m()应该是纯虚拟的,这将是为了解决问题而进行的正确更改。让我困惑的是,它可以通过一个明显不相关的更改进行编译。
以下代码不使用g++(在ubuntu 64位上为4.6.3)进行编译,并产生预期的"C的vtable的未定义引用"消息(记录在案,考虑到m()的问题,这仍然是一个可怕的错误消息)
收割台.h
#ifndef HEADER_H
#define HEADER_H
class C
{
public:
C();
virtual void m();
};
#endif
实施.cpp
#include "Header.h"
C::C() {}
主要.cpp
#include "Header.h"
int main()
{
return 0;
}
以下不相关的更改允许编译:
- 从implementation.cpp中删除C::C()的非内联实现
- 将C()的琐碎内联实现添加到Header.h中的类中
为什么这允许编译?这是编译器错误、优化器问题还是标准升级的黑暗角落?
这是编译器错误、优化器问题还是标准升级的黑暗角落?
以上都没有。这不是一个错误,与优化无关,而且这个特定问题不在标准范围内,它包含在相关的ABI中(这只是一个事实上的标准)
C::m
是关键函数,您在任何地方都没有定义它,这意味着编译器不会发出vtable。
使用这些更改编译代码有很好的原因(如果复杂的话):
- 从implementation.cpp中删除C::C()的非内联实现
由于ABI文件2.6中描述的一些复杂原因,在施工期间需要vtables。因此,构造函数的定义创建了对vtable的引用,链接器告诉您在链接时缺少该引用。如果删除构造函数的定义,那么就没有对vtable的引用。
- 将C()的琐碎内联实现添加到Header.h中的类中
未在给定翻译单元中调用的内联函数不会在对象文件中发出,因此使函数内联意味着构造函数不在对象文件内,因此对象文件不引用vtable,链接器也不需要在链接时查找它。
如果您更改程序以便实际使用内联构造函数(例如,通过在main
中创建C
),那么您将再次得到相同的链接器错误,因为现在内联构造函数将在Main.o
中定义,因此需要vtable。
class C
{
public:
C() { } // inline
virtual void m();
};
int main()
{
C c;
}