虽然堆栈溢出还有其他问题,这些问题涉及'未定义的vtable引用'错误消息。以下代码编译或不编译,具体取决于no-args构造函数C()是否在线实现。我知道成员函数m()应该是纯虚拟的,并且这是正确的更改,以便解决问题。令我感到困惑的是,它可以通过明显不相关的变化进行编译。
以下代码不能用g ++编译(在ubuntu 64位上为4.6.3)并产生预期的'未定义的对vtable for C'消息的引用(对于记录来说仍然是一个可怕的错误消息,考虑到问题是m ())
Header.h
#ifndef HEADER_H
#define HEADER_H
class C
{
public:
C();
virtual void m();
};
#endif
Implementation.cpp
#include "Header.h"
C::C() {}
Main.cpp的
#include "Header.h"
int main()
{
return 0;
}
以下不相关的更改允许编译:
为什么这允许编译?这是编译器错误,优化器问题还是标准难题的黑暗角落?
答案 0 :(得分:7)
这是编译器错误,优化器问题还是标准难题的黑暗角落?
以上都不是。这不是一个错误,它与优化无关,而且这个特殊问题超出了标准的范围,它由相关的ABI(这只是事实上的标准)所涵盖。
C::m
是key function,你没有在任何地方定义它,这意味着编译器不会发出vtable。
有很好的(如果复杂的)原因,为什么代码会编译这些更改:
- 从Implementation.cpp
中删除C :: C()的非内联实现
由于ABI文档中2.6中描述的一些复杂原因,在构建期间需要vtable。因此构造函数的定义创建了对vtable的引用,链接器告诉您在链接时缺少该引用。如果删除构造函数的定义,则不会引用vtable。
- 为Header.h中的类添加C()的简单内联实现
在给定的转换单元中未调用的内联函数将不会在目标文件中发出,因此使函数内联意味着构造函数不在目标文件中,因此目标文件不引用vtable,链接器不需要在链接时查找它。
如果您更改程序以便实际使用内联构造函数(例如,通过在C
中创建main
),那么您将再次获得相同的链接器错误,因为现在内联构造函数将在Main.o
中定义,因此需要vtable。
class C
{
public:
C() { } // inline
virtual void m();
};
int main()
{
C c;
}