g ++ undefined对typeinfo的引用

cdl*_*ary 195 c++ linker g++

我只是遇到了以下错误(并在线找到了解决方案,但它在Stack Overflow中不存在):

(.gnu.linkonce.[stuff]):对[方法] [目标文件]的未定义引用:(.gnu.linkonce.[stuff]):对[classname]的`typeinfo的未定义引用

为什么可能会得到这些"未定义的typeinfo引用"链接器错误之一?

(如果你能解释幕后发生的事情,可以给予奖励.)

pax*_*blo 208

一个可能的原因是因为您在没有定义虚拟函数的情况下声明它.

如果在没有在同一个编译单元中定义它的情况下声明它,则表明它已在其他地方定义 - 这意味着链接器阶段将尝试在其他编译单元(或库)中找到它.

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

virtual void fn() { /* insert code here */ }
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您将一个定义附加到声明,这意味着链接器以后不需要解析它.

这条线

virtual void fn();
Run Code Online (Sandbox Code Playgroud)

声明fn()而不定义它,并将导致您询问的错误消息.

它与代码非常相似:

extern int i;
int *pi = &i;
Run Code Online (Sandbox Code Playgroud)

表明整数i是在另一个编译单元中声明的,必须在链接时解析(否则pi不能设置为它的地址).

  • 说'virtual void fn()= 0`是一个定义是不正确的.它不是一个定义,而只是一个*声明*.链接器没有尝试解决它的唯一原因是相应的VMT条目不会引用函数体(最有可能包含空指针).但是,没有人禁止您以非虚拟方式调用此纯虚函数,即使用完全限定名称.在这种情况下,链接器*将*查找正文,您将必须定义该函数.是的,你可以*为纯虚函数定义一个体. (28认同)
  • 编译器(g ++)将告诉你缺少的符号是什么.注意:如果是动态库链接,您可能会收到错误的名称.使用c ++ filt <mangledNameVariable>以可读的形式获取它.由于某些基类中缺少虚拟析构函数实现,因此带有类名的typeinfo错误就在我的情况下. (3认同)
  • 该问题特别提到缺少typeinfo,这与rtti有关。见达蒙评论 http://stackoverflow.com/questions/11904519/c-what-are-the-causes-of-undefined-reference-to-typeinfo-for-class-name (2认同)
  • 我收到此错误是因为“-fno-rtti”被指定为编译器选项,而不是因为未定义虚拟函数。我认为这个答案的介绍性陈述是“这个特定的错误是由......引起的”有点误导,而它应该是“一个可能的原因是因为......”。 (2认同)

Ser*_*rov 142

混合-fno-rtti-frtti编码时也会发生这种情况.然后,您需要确保type_info-frtti代码中访问的任何类都使用其密钥方法进行编译-frtti.当您创建类的对象,使用dynamic_cast等时,可能会发生此类访问.

[ 来源 ]

  • 非常感谢.这在5小时搜索后解决了我的问题. (19认同)
  • StackOverflow.com再次抢救!我希望我能不止一次地投票.在键盘上敲了一个小时后,你的答案就是我所需要的. (3认同)
  • 拯救了 n+1 条生命,而且还在继续计数 :) (2认同)

cdl*_*ary 49

当声明的(非纯)虚函数缺少实体时会发生这种情况.在您的类定义中,类似于:

virtual void foo();
Run Code Online (Sandbox Code Playgroud)

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

virtual void foo() {}
Run Code Online (Sandbox Code Playgroud)

或者声明纯虚拟:

virtual void foo() = 0;
Run Code Online (Sandbox Code Playgroud)


Ces*_*arB 26

引用gcc手册:

对于多态类(具有虚函数的类),type_info对象与vtable一起写出[...]对于所有其他类型,我们在使用时写出type_info对象:当将`typeid'应用于表达式时,抛出一个对象,或引用一个catch子句或异常规范中的类型.

在同一页面上稍早一点:

如果类声明任何非内联非纯虚函数,则选择第一个作为类的"键方法",并且仅在定义键方法的转换单元中发出vtable.

因此,当"密钥方法"缺少其定义时会发生此错误,正如已经提到的其他答案一样.

  • 在我的例子中,我有一个声明但没有定义非虚拟虚拟方法的基类.一旦我将它们变成纯虚拟的,这就是我的意思,链接器错误消失了. (2认同)

小智 19

如果你将一个.so链接到另一个,那么还有一种可能性是用gcc或g ++中的"-fvisibility = hidden"进行编译.如果两个.so文件都是使用"-fvisibility = hidden"构建的,并且key方法与另一个虚函数的实现不同,那么后者将不会看到前者的vtable或typeinfo.对于链接器,这看起来像一个未实现的虚函数(如paxdiablo和cdleary的答案).

在这种情况下,您必须为基类的可见性设置例外

__attribute__ ((visibility("default")))
Run Code Online (Sandbox Code Playgroud)

在课堂宣言中.例如,

class __attribute__ ((visibility("default"))) boom{
    virtual void stick();
}
Run Code Online (Sandbox Code Playgroud)

当然,另一种解决方案是不使用"-fvisibility = hidden".这确实使编译器和链接器复杂化,可能会损害代码性能.

  • 如果基类是抽象的或未使用的,则不需要导出(取消隐藏)基类,只需导出(取消隐藏)非虚函数,通常只是构造函数。另一方面,如果使用了*派生*类,则必须导出它们。 (2认同)

Tyl*_*nry 15

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

如果您希望类型信息适用于您不想要任何虚函数的类,请将析构函数设置为虚拟.

  • 我必须习惯SO的一件事并不是指"上面"的答案,因为订单可能会根据投票而改变.我现在通常不会引用任何其他答案,因为它们也可以删除.我的信念是答案应该是独立的.不过我仍然会提到用户名. (4认同)
  • 更新,因为我认为这更可能是该特定错误消息的原因(而不是更普遍的未定义方法的情况...) (2认同)

din*_*elk 10

我只花了几个小时来解决这个错误,虽然这里的其他答案帮助我理解了发生了什么,但他们并没有解决我的特殊问题.

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

重点:链接订单MATTERS与g++.如果列出要以不正确的顺序链接的库,则可能会收到typeinfo错误.

有关将订单与/ 链接的详细信息,请参阅此SO问题.gccg++


Goo*_*mps 10

就我而言,它是接口类中的虚拟函数,未定义为纯虚拟函数。

class IInterface
{
public:
  virtual void Foo() = 0;
}
Run Code Online (Sandbox Code Playgroud)

我忘记了= 0一点。


小智 9

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

a)用-frtti或-fno-rtti重新编译所有内容
b)如果a)不可能,请尝试以下方法:

假设libfoo是在没有RTTI的情况下构建的.您的代码使用libfoo并使用RTTI编译.如果在libfoo中使用具有虚拟的类(Foo),则可能会遇到链接时错误,该错误表示:缺少类Foo的typeinfo.

定义没有虚拟的另一个类(例如FooAdapter),并将调用转发给您使用的Foo.

在不使用RTTI且仅依赖于libfoo符号的小型静态库中编译FooAdapter.为它提供一个标题,并在您的代码中使用它(使用RTTI).由于FooAdapter没有虚函数,因此它不具有任何typeinfo,您将能够链接您的二进制文件.如果你使用libfoo中的很多不同的类,这个解决方案可能不方便,但它是一个开始.


小智 6

与上面的RTTI,NO-RTTI讨论类似,如果使用dynamic_cast并且无法包含包含类实现的目标代码,也会发生此问题.

我在Cygwin上构建了这个问题,然后将代码移植到Linux上.make文件,目录结构甚至gcc版本(4.8.2)在两种情况下都是相同的,但代码在Cygwin上正确链接和操作但在Linux上无法链接.Red Hat Cygwin显然已经进行了编译器/链接器修改,避免了目标代码链接要求.

Linux链接器错误消息正确地指示我到dynamic_cast行,但是这个论坛中的早期消息让我寻找缺少的函数实现而不是实际的问题:缺少对象代码.我的解决方法是替换基类和派生类中的虚拟类型函数,例如virtual int isSpecialType(),而不是使用dynamic_cast.这种技术避免了链接对象实现代码的要求,只是为了让dynamic_cast正常工作.


小智 5

在基类(一个抽象基类)中,您声明了一个虚拟析构函数,因为您不能将析构函数声明为纯虚函数,要么您必须在抽象类中定义它,只需要像virtual~base这样的虚拟定义( ){}会做,或者在任何派生类中.

如果您没有这样做,您将在链接时以"未定义的符号"结束.由于VMT具有匹配NULL的所有纯虚函数的条目,因为它根据派生类中的实现更新表.但是对于非纯虚函数,它需要在链接时定义,以便它可以更新VMT表.

使用c ++ filt来解码符号.像$ c ++ filt _ZTIN10storageapi8BaseHostE将输出类似"typeap for storageapi :: BaseHost"的内容.