g++未定义对typeinfo的引用

275

我刚遇到了以下错误:

(.gnu.linkonce.[stuff]):未定义的 参考 [method] [object file]:(.gnu.linkonce.[stuff]): 未定义对于`[classname]`的typeinfo的引用

为什么会出现这种“未定义对于typeinfo的引用”的链接器错误?
有人能解释一下背后发生了什么吗?


53
我知道这是一个旧帖子,但我今天遇到了同样的问题,解决方法很简单,只需在基类中将我的虚函数定义为virtual abc() {},而不是virtual abc(); 这样会导致错误。 - Nav
31
如果基类版本从不被调用,最好使用 virtual void abc() =0; - dhardy
3
如果你像这样定义 abc() 函数,你很容易忘记在派生类中重新定义 abc(),并认为一切都很好,因为你仍然可以无任何问题地调用该函数。一个实现纯虚函数的好做法可以在这篇文章中找到,即让函数打印出 "Pure virtual function called" 然后崩溃程序。 - HelloGoodbye
1
我曾经遇到过同样的错误。我发现改变“lib”引用的顺序可能会有所帮助。我只是将问题库从列表开头移到了末尾,这解决了问题。 - Oleg Vazhnev
3
这至少是我第二次精准地跳转到该页面,阅读@dhardy的评论并自言自语“呃”。刚刚花了45分钟追踪一些疯狂的行为,而我所需要的只是= 0; - dwanderson
1
在我的情况下,我将基础析构函数从 "= 0;" 改为 "{};"。然后就没问题了。如果你将析构函数定义为虚函数,你仍然需要在基类中定义它。因为派生类的析构函数在销毁时会调用基类的析构函数。如果找不到定义,就会出现问题。 - Raghav Navada
18个回答

283

可能的一个原因是你声明了一个虚函数但没有定义它。

当你在同一编译单元中声明但未定义虚函数时,你表明它在其他地方被定义了 - 这意味着链接器将尝试在其他编译单元(或库)中找到它。

定义虚函数的一个例子是:

virtual void fn() { /* insert code here */ }
在这种情况下,您正在将定义附加到声明,这意味着链接器不需要稍后解析它。
该行
virtual void fn();

这段代码声明了fn(),但没有定义它,会导致你所询问的错误信息。

与以下代码非常相似:

extern int i;
int *pi = &i;

这说明整数变量i是在另一个编译单元中声明的,必须在链接时解析(否则pi无法设置为它的地址)。


35
virtual void fn() = 0 是一个定义是不正确的。它只是一种 声明。链接器没有试图解析它的唯一原因是相应的虚函数表(VMT)条目不会引用函数体(很可能包含空指针)。然而,你可以以非虚拟方式调用这个纯虚函数,即使用全限定名。在这种情况下,链接器将寻找函数体,你需要定义该函数。而且,你确实可以为纯虚函数定义函数体。 - AnT stands with Russia
3
编译器(g++)会告诉你哪个符号丢失了。注意:在动态库链接的情况下,你可能会得到一个已编码名称。使用c++filt <mangledNameVariable>将其转换为可读形式。对于类名的typeinfo错误是因为某个基类缺少虚析构函数实现。 - chmike
2
问题特别提到缺少typeinfo,这与rtti有关。请参见https://dev59.com/1mct5IYBdhLWcg3wqvTL中Damon的评论。 - user2088639
2
我之所以出现这个错误,是因为-fno-rtti被指定为编译器选项,而不是因为虚函数未定义。我认为这个答案的引言声明"This particular error is caused by..."有点误导人,更好的说法应该是"One possible reason is because..."。 - gbmhunter
1
@gbmhunter,好的。已经做出了更改。 - paxdiablo
显示剩余4条评论

190
这种情况也可能发生在混合使用-fno-rtti-frtti代码时。这时,您需要确保在-frtti代码中访问type_info的任何类都使用-frtti编译其关键方法。当您创建该类的对象、使用dynamic_cast等时,就会出现这种访问情况。
[source]

28
非常感谢。在搜寻了5个小时后,你的解决方案解决了我的问题。 - steipete
1
源链接已失效,它肯定与http://permalink.gmane.org/gmane.comp.gcc.help/32475相同。 - math
1
感谢您指出这一点。原始页面仍然可以在此处找到:http://web.archive.org/web/20100503172629/http://www.pubbs.net/201004/gcc/25970-linker-error-undefined-reference-to-typeinfo-for-a-with-no-rtti-option.html - Sergiy Belozorov
3
StackOverflow.com再次拯救了我!我希望我能多次点赞。在我苦思冥想了一个小时后,你的答案正是我所需要的。 - spartygw
2
n+1个生命已被拯救,而且还在继续增加 :) - Gabriel
显示剩余5条评论

71

当声明了(非纯)虚函数但未提供函数主体时,会出现此问题。在类定义中,可能存在以下代码:

virtual void foo();

应该被定义(内联或在链接的源文件中):

virtual void foo() {}

或者声明为纯虚函数:

virtual void foo() = 0;

31
引用自gcc手册
对于多态类(具有虚函数的类),在vtable之后会写入type_info对象[...] 对于所有其他类型,只有当应用`typeid`到一个表达式、抛出对象或在catch子句或异常规范中引用类型时使用时,才会写出type_info对象。
在同一页的稍早之处也有这样的说明:
如果该类声明了任何非内联、非纯虚函数,则第一个函数被选择为该类的“关键方法”,并且vtable仅在定义关键方法的翻译单元中发出。
因此,正如其他答案已经提到的那样,当“关键方法”缺失其定义时,就会发生这个错误。

3
在我的情况下,我有一个基类声明了但未定义不是纯虚拟的虚拟方法。一旦我将它们变成纯虚拟的,也就是我的意思,链接器错误消失了。 - Tatiana Racheva
@TatianaRacheva 谢谢!链接器的错误报告不太有用,对于一个大接口来说,很容易忽略纯虚函数缺少“=0;”的问题! - rholmes

27
如果你正在将一个 .so 文件链接到另一个 .so 文件中,那么还有一种可能性是在 gcc 或 g++ 中使用 "-fvisibility=hidden" 进行编译。如果两个 .so 文件都使用了 "-fvisibility=hidden" 进行构建,并且关键方法不在与虚函数实现的另一个 .so 中,那么后者将看不到前者的虚表或类型信息。对于链接器来说,这看起来就像一个未实现的虚函数(如 paxdiablo 和 cdleary 的答案中所示)。
在这种情况下,你必须为基类的可见性做出例外。
__attribute__ ((visibility("default")))
在类声明中。例如,
class __attribute__ ((visibility("default"))) boom{
    virtual void stick();
}

另一种解决方案,当然是不使用“-fvisibility=hidden”。这会让编译器和链接器变得更复杂,可能会影响代码性能。


2
如果基类是抽象的或者未使用,你不需要导出(取消隐藏)它,只需要导出非虚函数,通常只有构造函数。另一方面,如果派生类被使用了,那么它们必须被导出。 - Chris Huang-Leaver
感觉有点像黑科技,但它确实解决了我的问题。谢谢! - malat

18

之前的回答是正确的,但这个错误也可能是由于尝试在没有虚函数的类对象上使用typeid而引起的。C++ RTTI需要一个vtable,因此您希望执行类型识别的类需要至少一个虚函数。

如果你想让某个类的类型信息工作,但又不想真正使用任何虚函数,请将析构函数声明为virtual。


2
因为我认为这更有可能是导致那个特定错误消息的原因(而不是未定义方法的更一般情况),所以我已经进行了升级。 - Alastair
4
在使用SO时,我需要适应的一件事是不能参考“上面”的答案,因为它们的排列顺序可能会根据投票而改变。现在我通常不会参考任何其他答案,因为它们也可能会被删除。我认为答案应该是独立的。然而,我仍然会提及用户名以便归属。 - paxdiablo
你可以在没有虚函数表的情况下使用typeid;请参考我的答案,其中包含来自gcc手册的引用。 - CesarB

17

我刚刚花了几个小时来解决这个错误,虽然这里其他答案帮助我理解了情况,但并没有解决我的具体问题。

我正在开发一个使用clang++g++编译的项目。使用clang++时没有任何链接问题,但是使用g++时遇到了undefined reference to 'typeinfo for的错误。

重要提示:在使用g++进行链接时,链接顺序很重要。如果你以不正确的顺序列出要连接的库,就会出现typeinfo错误。

有关使用gcc/g++链接顺序的更多详细信息,请参见此 SO 问题


谢谢!!!我花了一整天的时间试图找出为什么我会得到这个错误,直到看到了你的回复和你提供的链接,才让问题得以解决。非常感谢!! - Irene
实际上,在使用clang时链接顺序也很重要,因此这个建议是普适的,谢谢。 - Fedor
是的,最终修复链接顺序解决了我的问题。 "undefined reference to typeinfo" 错误是指在链接类中使用非虚拟类时出现的错误,形式如下: somelibrary.a(somefile.o):(.gcc_except_table+0x23c): undefined reference to `typeinfo for NS:CLASSNAME'在这种情况下,NS: CLASSNAME 在另一个库otherlib.a中实现,需要将其移至somelibrary.a之下。我还有几个与库顺序相关的错误,但这是唯一表现为 typeinfo 错误的错误。 - Daemon42

12

处理 RTTI 和非 RTTI 库的代码可能的解决方案:

a)重新编译所有内容,要么使用 -frtti ,要么使用 -fno-rtti
b)如果 a)对您不可行,请尝试以下方法:

假设 libfoo 是没有使用 RTTI 构建的。 您的代码使用 libfoo 并使用 RTTI 进行编译。 如果您在 libfoo 中使用具有虚函数的类(Foo),则可能会遇到链接时错误,指出:缺少类 Foo 的类型信息。

定义另一个类(例如 FooAdapter),它没有虚函数并将调用转发到您使用的 Foo。

在一个小的静态库中编译 FooAdapter,该库不使用 RTTI 且仅依赖于 libfoo 符号。为其提供一个头文件,并在您的代码中(使用 RTTI)使用该头文件而不是 libfoo。由于 FooAdapter 没有任何虚函数,因此它不会有任何类型信息,您将能够链接您的二进制文件。如果您从 libfoo 使用了许多不同的类,则此解决方案可能不方便,但这是一个开始。


这就是我所做的,链接到具有不同RTTI设置的库。 - marsh

9

在我的情况下,出现了一个在接口类中没有被定义为纯虚函数的虚函数。

class IInterface
{
public:
  virtual void Foo() = 0;
}

我忘记了= 0这一位。


8
在基类(一个抽象基类)中,您声明了一个虚拟析构函数,由于您无法将析构函数声明为纯虚函数,因此您必须在此处定义它,就像虚拟~base() {}这样的虚拟函数,或者在任何派生类中定义。
如果您没有这样做,您将在链接时遇到“未定义符号”的问题。由于VMT具有所有纯虚函数的条目,并且具有匹配的NULL,因此它根据派生类中的实现更新表格。但对于非纯虚但虚拟函数,它需要在链接时进行定义,以便它可以更新VMT表。
使用c++filt来解开符号。例如$c++filt _ZTIN10storageapi8BaseHostE将输出类似于“storageapi :: BaseHost的类型信息”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接