g ++对typeinfo的未定义引用


208

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

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

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

(如果您可以解释幕后发生的事情,则可以加分。)


31
我知道这是一篇过时的文章,但是今天我遇到了同样的问题,解决方案是将我的虚拟函数定义为基类中的virtual abc(){},而不是virtual abc();这给了错误。
导航

15
更好的是virtual void abc() =0;(如果从不调用基本版本)
dhardy 2012年

3
@Nav:如果这样定义abc(),您很容易忘记abc()在派生类中重新定义,并认为一切都很好,因为您仍然可以毫无问题地调用该函数。实现纯虚函数的好的做法中找到这篇文章,这是使函数打印“称为纯虚函数”,然后程序崩溃。
HelloGoodbye 2012年

1
我有同样的错误。我发现更改对“ lib”的引用的顺序可能会有所帮助。我只是将问题库的名称从
开头提到

2
GAH。现在,这至少是我第二次完全导航至此页面,以阅读@dhardy的评论并对自己说“ Doh”。仅仅花了45分钟试图找出一些疯狂的行为,我需要的是= 0;
dwanderson

Answers:


222

一个可能的原因是因为您在声明虚拟函数而不定义它。

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

定义虚函数的示例是:

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

在这种情况下,您要在声明上附加一个定义,这意味着链接程序以后不需要解析它。

线

virtual void fn();

声明fn()但未定义它,这将导致您询问错误消息。

它与代码非常相似:

extern int i;
int *pi = &i;

它指出该整数i是在另一个编译单元中声明的,必须在链接时进行解析(否则pi无法将其设置为它的地址)。


28
说这virtual void fn() = 0是一个定义是不正确的。它不是一个定义,而仅仅是一个声明。链接器不尝试解析的唯一原因是相应的VMT条目将不引用函数主体(最有可能包含空指针)。但是,没有人禁止您以非虚拟方式(即使用完全限定的名称)调用此纯虚拟函数。在这种情况下,链接器寻找主体,您将必须定义函数。是的,您可以为纯虚函数定义主体。
AnT 2010年

1
有时甚至必须为纯虚函数声明一个主体。

3
编译器(g ++)会告诉您缺少的符号。注意:在动态库链接的情况下,您可能会得到错误的名称。使用c ++ filt <mangledNameVariable>以可读的形式获取它。在我的情况下,带有类名的typeinfo错误是因为某些基类中缺少虚拟析构函数实现。
chmike

1
这个问题专门提到缺少typeinfo,这与rtti有关。请参阅stackoverflow.com/questions/11904519/…中
wilsonmichaelpatrick,2014年

1
@gbmhunter,很公平。进行了更改。
paxdiablo 2014年

149

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

[ 来源 ]


20
非常感谢。经过5小时的搜索,这解决了我的问题。
steipete 2011年

1
源链接已死,它肯定与permalink.gmane.org/gmane.comp.gcc.help/32475
math

1
感谢您指出了这一点。原始页面仍在这里可用:web.archive.org/web/20100503172629/http
//www.pubbs.net/201004/…– Sergiy Belozorov'4

3
StackOverflow.com再次解救!我希望我可以不止一次投票。在用键盘敲打我的头一个小时之后,您的答案就是我所需要的。
spartygw

1
已保存n + 1条生命,仍在计数:)
加百利

53

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

virtual void foo();

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

virtual void foo() {}

或声明为纯虚拟的:

virtual void foo() = 0;

27

引用gcc手册

对于多态类(具有虚函数的类),type_info对象与vtable一起写出。抛出对象,或在catch子句或异常规范中引用类型。

在同一页面上的更早一点:

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

因此,如其他答案所述,当“键方法”缺少其定义时会发生此错误。


2
就我而言,我有一个基类,该基类声明但未定义不是纯虚拟的虚方法。一旦将它们变成纯虚拟的(即我的意思),链接器错误就消失了。
Tatiana Racheva 2011年

@TatianaRacheva谢谢!链接器的错误报告没有帮助,对于较大的接口,很容易错过缺少'= 0;'的情况。纯粹的虚拟!
罗尔姆斯,2015年

20

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

在这种情况下,您必须使用

__attribute__ ((visibility("default")))

在类声明中。例如,

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

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


1
如果基类是抽象的或未使用的,则不需要导出(取消隐藏)基类,只需导出非虚拟函数,通常仅导出构造函数即可。所述衍生的另一方面类必须被导出,如果使用它们。
克里斯·黄·利弗

感觉就像是在砍,但确实解决了我这边的症状。谢谢 !
malat

15

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

如果您希望类型信息在您确实不希望使用任何虚函数的类上工作,则将析构函数设为虚拟。


2
升级,因为我认为这更可能是该特定错误消息的原因(而不是更普遍的未定义方法的情况...)
Alastair

4
我必须习惯于SO的一件事不是指“以上”的答案,因为顺序可能会基于投票而改变。我现在通常不提及任何其他答案,因为它们也可以删除。我认为答案应该是独立的。不过,我仍然引用用户名作为归因。
paxdiablo

您可以使用没有vtable的typeid。请参阅我对gcc手册中引号的回答。
CesarB

11

我只是花了几个小时来解决这个错误,尽管这里的其他答案可以帮助我了解发生了什么,但它们并不能解决我的特定问题。

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

要点:将订单MATTERS与关联g++。如果您以错误的顺序列出要链接的库,则会收到typeinfo错误消息。

有关与/ 链接顺序的更多详细信息,请参见此SO问题gccg++


谢谢!!!我花了整整一天的时间来找出为什么我得到此错误,但直到看到此答复和您链接到的答复,任何方法都无济于事。非常感谢!!
艾琳

10

处理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使用许多不同的类,则此解决方案可能并不方便,但这只是一个开始。


对我来说就是这样,链接到具有不同RTTI设置的库。
沼泽

6

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

我在构建Cygwin时遇到了这个问题,然后将代码移植到Linux。在这两种情况下,make文件,目录结构甚至gcc版本(4.8.2)都相同,但是代码在Cygwin上链接并正确运行,但在Linux上链接失败。红帽Cygwin显然已经进行了编译器/链接器修改,从而避免了目标代码链接的要求。

Linux链接器错误消息正确地将我定向到dynamic_cast行,但是该论坛中的较早消息使我正在寻找缺少的函数实现,而不是实际的问题:缺少目标代码。我的解决方法是在基类和派生类中替换虚拟类型函数,例如,虚拟int isSpecialType(),而不是使用dynamic_cast。该技术避免了仅为了使dynamic_cast正常工作而链接对象实现代码的需求。


5

在基类(抽象基类)中,您声明了一个虚拟析构函数,并且由于您不能将析构函数声明为纯虚函数,因此您必须在抽象类中在此处定义它,只是一个虚定义,例如virtual〜base( ){}或在任何派生类中使用。

如果不这样做,则在链接时将以“未定义符号”结尾。由于VMT在更新表时会根据派生类中的实现为所有纯虚拟函数提供一个带有匹配NULL的条目。但是对于非纯虚函数,它需要在链接时进行定义,以便可以更新VMT表。

使用c ++ filt对符号进行解贴。像$ c ++ filt一样,_ZTIN10storageapi8BaseHostE将输出类似“ typeinfo for storageapi :: BaseHost”的信息。


3

我现在有很多这些错误。发生的是,我将仅标头文件的类拆分为标头文件和cpp文件。但是,我没有更新构建系统,因此没有编译cpp文件。在对标题中声明但未实现的函数的未定义引用中,我遇到了许多这些typeinfo错误。

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


3

就我而言,我使用了带有标头文件和so文件的第三方库。我将一个子类划分为子类,当我尝试实例化我的子类时发生了这样的链接错误。

正如@sergiy所提到的那样,已知可能是“ rtti”的问题,我设法通过将构造函数实现放入单独的.cpp文件中并将“ -fno-rtti”编译标志应用于该文件来解决此问题。它运作良好。

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

还有一件事,如果您选择尝试我的解决方案,请尝试使单独的文件尽可能简单,并且不要使用C ++的某些高级功能。要特别注意与boost相关的事情,因为其中很大一部分取决于rtti。


2

当我的界面(带有所有纯虚拟功能)需要一个以上的功能,而我却忘记将其“清空”时,我也遇到了同样的错误。

我有

class ICommProvider { public: /** * @brief If connection is established, it sends the message into the server. * @param[in] msg - message to be send * @return 0 if success, error otherwise */ virtual int vaSend(const std::string &msg) = 0; /** * @brief If connection is established, it is waiting will server response back. * @param[out] msg is the message received from server * @return 0 if success, error otherwise */ virtual int vaReceive(std::string &msg) = 0; virtual int vaSendRaw(const char *buff, int bufflen) = 0; virtual int vaReceiveRaw(char *buff, int bufflen) = 0; /** * @bief Closes current connection (if needed) after serving * @return 0 if success, error otherwise */ virtual int vaClose(); };

最后一个vaClose不是虚拟的,因此编译后不知道从何处获取实现,因此感到困惑。我的讯息是:

... TCPClient.o :(。rodata + 0x38):对`typeInfo for ICommProvider'的未定义引用

简单的改变

virtual int vaClose();

virtual int vaClose() = 0;

解决了问题。希望能帮助到你


1

我遇到的情况很少见,但这可能会帮助处于类似情况的其他朋友。我必须使用gcc 4.4.7在较旧的系统上工作。我必须使用c ++ 11或更高版本的支持来编译代码,因此我构建了最新版本的gcc 5.3.0。如果依赖项是使用较旧的编译器构建的,则在构建代码并链接至依赖项时,即使我使用-L / path / to / lib -llibname明确定义了链接路径,也会出现“未定义引用”错误。有些软件包,例如boost和使用cmake构建的项目,通常倾向于使用较旧的编译器,并且通常会引起此类问题。您必须走很长一段路,以确保他们使用较新的编译器。


1

就我而言,即使我进行了dynamic_cast调用,也纯粹是库依赖问题。将足够的依赖项添加到makefile中之后,此问题就消失了。



0

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

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

我忘了= 0一点。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.