g++对typeinfo的未定义引用

我只是遇到了以下错误:

< p > (.gnu.linkonce。[内容]):定义 引用[方法][对象] 文件:(.gnu.linkonce。[内容]): 对' typeinfo for '的未定义引用 [名字]' < / p >

为什么一个人可能会得到这样一个“未定义的引用到类型信息”;链接器错误?< br / > 有人能解释一下幕后发生了什么吗?< / p >

235537 次浏览

当声明的(非纯)虚函数缺少主体时,就会发生这种情况。在你的类定义中,如下所示:

virtual void foo();

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

virtual void foo() {}

或声明为纯虚拟的:

virtual void foo() = 0;

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

当你声明它而没有在同一个编译单元中定义它时,你就表明它在其他地方定义了——这意味着链接器阶段将尝试在其他编译单元(或库)中找到它。

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

virtual void fn() { /* insert code here */ }

在本例中,您将定义附加到声明中,这意味着链接器稍后不需要解析它。

这条线

virtual void fn();

声明fn()而没有定义它,这将导致你所询问的错误消息。

它与代码非常相似:

extern int i;
int *pi = &i;

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

前面的答案是正确的,但此错误也可能是由于试图对具有没有虚函数的类的对象使用typeid而导致的。c++ RTTI需要虚表,因此希望对其执行类型标识的类至少需要一个虚函数。

如果希望类型信息作用于一个实际上不需要任何虚函数的类,可以将析构函数设为虚函数。

引用自GCC手册:

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

在同一页稍早的地方

如果类声明了任何非内联、非纯虚函数,则选择第一个虚函数作为类的“key方法”,虚表只在定义key方法的转换单元中发出。

因此,当“key方法”缺少它的定义时,就会发生这个错误,正如前面提到的其他答案。

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

在这种情况下,必须为基类的可见性做出例外

__attribute__ ((visibility("default")))

在类声明中。例如,

class __attribute__ ((visibility("default"))) boom{
virtual void stick();
}

当然,另一个解决方案是不使用"-fvisibility=hidden "。这使得编译器和链接器的事情变得复杂,可能会损害代码的性能。

当你混合使用-fno-rtti-frtti代码时也会发生这种情况。然后你需要确保在-frtti代码中访问type_info的任何类,它们的键方法都是用-frtti编译的。这样的访问可以发生在创建类的对象时,使用dynamic_cast等。

()

在基类(抽象基类)中声明虚析构函数,因为不能将析构函数声明为纯虚函数,所以要么必须在抽象类中定义它,就像virtual ~base(){}这样的虚拟定义,要么在任何派生类中定义。

如果你没有做到这一点,你将在链接时以“未定义的符号”结束。 因为VMT在根据派生类中的实现更新表时,为所有纯虚函数提供了一个带有匹配NULL的条目。但对于非纯虚函数,则需要在链接时定义,以便更新VMT表

使用c++filt来要求符号。比如$c++filt _ZTIN10storageapi8BaseHostE 将输出类似"typeinfo for storageapi::BaseHost"的内容

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

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

假设libfoo是在没有RTTI的情况下构建的。您的代码使用libfoo并使用RTTI编译。如果你在libfoo中使用一个有虚拟对象的类(Foo),你很可能会遇到一个链接时错误:Foo类缺少typeinfo。

定义另一个类(例如FooAdapter),它没有虚值,并且会将调用转发给你使用的Foo。

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

与上面的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正常工作而链接对象实现代码的需求。

我刚才有很多这样的错误。我将一个只包含头文件的类拆分为一个头文件和一个cpp文件。但是,我没有更新我的构建系统,所以cpp文件没有被编译。除了在头文件中声明但未实现的函数的未定义引用之外,我得到了很多此类typeinfo错误。

解决方案是重新运行构建系统来编译和链接新的cpp文件。

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

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

重点是:链接顺序与g++有关。如果你以不正确的顺序列出你想要链接的库,你会得到typeinfo错误。

有关链接顺序与gcc/g++的更多细节,请参见这个SO问题

我遇到了一种罕见的情况,但这可能会对其他有类似情况的朋友有所帮助。我必须在使用gcc 4.4.7的旧系统上工作。我必须编译支持c++11或以上版本的代码,所以我构建了最新版本的gcc 5.3.0。当构建我的代码和链接到依赖项时,如果依赖项是用旧的编译器构建的,那么我得到了'未定义的引用'错误,即使我清楚地定义了链接路径-L/path/to/lib -llibname。一些包,比如boost和用cmake构建的项目,通常倾向于使用旧的编译器,它们通常会导致这样的问题。你必须走很长的路来确保他们使用更新的编译器。

在我的例子中,我使用了带有头文件等文件的第三方库。我子类化了一个类,当我尝试实例化我的子类时,发生了这样的链接错误。

正如@sergiy所提到的,知道这可能是'rtti'的问题,我设法通过将构造函数实现放入单独的.cpp文件中,并对该文件应用'-fno-rtti'编译标志来解决它。它工作得很好。

由于我仍然不太清楚这个链接错误的内部,我不确定我的解决方案是否通用。然而,我认为在尝试@francois提到的适配器方式之前值得一试。当然,如果所有源代码都可用(不是在我的情况下),最好在可能的情况下使用'-frtti'重新编译。

还有一件事,如果你选择尝试我的解决方案,试着让单独的文件尽可能简单,不要使用c++的一些花哨的功能。特别注意boost相关的事情,因为它很大程度上依赖于rtti。

我有同样的错误时,我的接口(与所有纯虚函数)需要一个更多的函数,我忘记“空”它。

我有

< p > <代码>类ICommProvider { 公众: /** 如果连接建立,它将消息发送到服务器。 要发送的消息 如果成功返回0,否则返回错误 * / 虚拟int vaSend(const std::字符串&msg) = 0; /** 如果连接已建立,则等待服务器响应。 * @param[out] MSG是从服务器接收到的消息 如果成功返回0,否则返回错误 * / vreceive (std::string &msg) = 0; vaSendRaw(const char *buff, int bufflen) = 0; vreceiveraw (char *buff, int bufflen) = 0; /** * @bief服务结束后关闭当前连接(如果需要) 如果成功返回0,否则返回错误 * / 虚拟int vaClose(); }; < /代码> < / p >

Last vaClose不是虚拟的,所以编译时不知道从哪里获得它的实现,因此感到困惑。我传达的信息是:

…TCPClient.o:(.rodata+0x38): undefined reference to ' typeinfo for ICommProvider'

简单的更改

virtual int vaClose();

virtual int vaClose() = 0;

修复了问题。希望能有所帮助

检查你的依赖是在没有-f-nortti的情况下编译的。

对于某些项目,你必须显式地设置它,比如在RocksDB中:

USE_RTTI=1 make shared_lib -j4

在我的情况下,这纯粹是一个库依赖问题,即使我有dynamic_cast调用。在makefile中添加了足够的依赖后,这个问题就消失了。

在我的例子中,它是一个接口类中的虚函数,没有定义为纯虚函数。

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

我忘记了= 0位。

有了这个错误消息,g++的链接器是在告诉你,当需要时,它无法为给定的类组装完整的静态typeinfo描述符。正如许多人已经指出的那样,这很可能是由于缺少虚函数的定义。

然而,不好的事情是,错误消息的顺序可能与直觉相反,“未定义的引用到typeinfo”。出现之前未定义的引用到缺失的虚拟定义。这里有一个例子,我刚刚经历过:

/usr/bin/ld: module.o:(.data.rel.ro+0x10): undefined reference to `typeinfo for type_xxx'
/usr/bin/ld: module.o:(.data.rel.ro+0x28): undefined reference to `typeinfo for type_xxx'
/usr/bin/ld: module.o:(.data.rel.ro+0x40): undefined reference to `typeinfo for type_xxx'
/usr/bin/ld: module.o:(.data.rel.ro+0x150): undefined reference to `type_xxx::has_property(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'

因此,缺少type_xxx::has_property(const std::string&)的定义仅作为第四个错误报告。所以,有时它值得跳过那些错误消息,一个不理解,并处理那些,首先,是容易理解的。因为在这种情况下,添加缺失的定义也会修复未定义typeinfo引用的问题。