GCC C++ 链接错误:未定义对 'XXX 的虚表, 未定义对 'ClassName::ClassName()' 的引用。

72

我正在Ubuntu x64上用Eclipse-CDT设置一个C++项目,基本上是在做hello world并链接到一个商业第三方库。

我已经包含了头文件,链接到他们的库,但我仍然遇到链接错误。除了显而易见的问题(例如,我99%确定我正在链接到正确的库之外),这里还有可能出现的其他问题吗?

  1. 有没有办法确认我链接的静态库是64位的?
  2. 有没有办法确认该库具有我期望其具有的类(和方法)?

Eclipse显示:

构建目标:LinkProblem
调用:GCC C ++链接器
g++ -L /home/notroot/workspace/somelib-3/somelib/target/bin -o“ LinkProblem”./src/LinkProblem.o -lsomelib1 -lpthread -lsomelib2 -lsomelib3
./src/LinkProblem.o:在函数“main”中:
/home/notroot/workspace/LinkProblem/Debug/../src/LinkProblem.cpp:17:对“SomeClass :: close()”的未定义引用
./src/LinkProblem.o:在函数“SomeOtherClass”中:
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:148:对“SomeClass :: SomeClass()”的未定义引用
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:148:对“SomeOtherClass”的虚表
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:151:对“SomeClass ::〜SomeClass()”的未定义引用
./src/LinkProblem.o:在函数“〜SomeOtherClass”中:
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:140:对“SomeOtherClass”的虚表
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:140:对“SomeClass ::〜SomeClass()”的未定义引用
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:140:对“SomeClass ::〜SomeClass()”的未定义引用
collect2:ld返回1个退出状态make: *** [LinkProblem] 错误 1

第三方库是64位的吗? - Daniel A. White
是的,它是64位的。不过你可能有所发现。我如何确保我的代码/项目是64位的?在Visual Studio中,我创建了一个x64构建配置。 - waterlooalex
1
有没有一种方法可以确认第三方库是64位的?例如,使用工具检查.a文件或其他什么东西? - waterlooalex
它在哪里?谷歌显示它位于 /usr/lib64 中。 - Daniel A. White
@Daniel:在谷歌上关于它们的信息不多。我有它们的样例,构建很好。我也可以在Windows上链接它们的库(x64)没问题。我正在联系他们寻求支持,只是需要一些时间。可能我的困难之一是我对gcc/linux还比较新手。 - waterlooalex
显示剩余4条评论
12个回答

168

这个链接器错误通常(根据我的经验)意味着你在子类中重写了一个虚函数的声明,但没有给出该方法的定义。例如:

class Base
{
    virtual void f() = 0;
}
class Derived : public Base
{
    void f();
}

但是您没有给出f的定义。当您使用该类时,您会得到链接器错误。就像普通的链接器错误一样,这是因为编译器知道您在说什么,但链接器找不到定义。它只是显示了一个非常难理解的消息。


4
谢谢。我找了两个小时都没找到解决方案。 - problemofficer
2
正是我遇到的问题。谢谢你,你帮我省了时间和精力。 - Haider
有趣的是,在我的情况下,这只发生在“纯虚拟函数”中!错误信息真的很误导。 - mishmashru
这是我的问题。忘记将它设置为0了! - scottc

74
假设这些方法在库文件中,看起来是一个排序问题。
将库文件链接到可执行文件时,按照它们声明的顺序进行链接。 此外,链接器仅使用解决当前未解决的依赖项所需的方法/函数。如果随后的库文件使用了最初由对象不需要的方法/函数,则会出现缺失依赖项。
它是如何工作的:
- 将所有对象文件合并为一个可执行文件 - 解决对象文件之间的任何依赖关系。 - 按顺序对每个库:
- 检查未解决的依赖项,并查看该库是否解决了这些依赖项。 - 如果是,则将所需部分加载到可执行文件中。
例如:
对象需要:
- Open - Close - BatchRead - BatchWrite Lib 1 提供:
- Open - Close - read - write Lib 2 提供:
- BatchRead(但使用了lib1:read) - BatchWrite(但使用了lib1:write)
如果像这样链接:
gcc -o plop plop.o -l1 -l2
那么链接器将无法解析读取和写入符号。
但如果我像这样链接应用程序:
gcc -o plop plop.o -l2 -l1
那么它将正确链接。因为l2解决了BatchRead和BatchWrite依赖项,但也添加了两个新依赖项(read和write)。当我们下一步链接l1时,将解决这四个依赖项。

是的。 :-) - Martin York
2
如果你的代码包含C++内容,你需要使用g++来获取正确的标准库。无论你之前用了什么,都需要这样做。 - Martin York
3
这真的帮了我很多!我的小组项目突然停止接受一个库的新cpp文件,所有问题都与未定义的引用有关,然后我改变了位置,问题就迎刃而解了。说真的,应该有更具体的文章在某些网页上介绍这个问题。非常感谢Martin! - Jonathan
关于覆盖虚拟声明而没有匹配定义的答案,以下答案应该真正成为“被接受”的答案 - 这个答案是正确的,但它更多地是一个短暂的边缘情况,而不是其他答案,后者更经常困扰人们 :-) - Mark Mullin
@MarkMullin: 嗨,马克,欢迎来到SO。对答案的评论会被标记给回答者。如果你想让原始发布者看到评论,你应该在问题下发表评论,并以更有意义的方式解释为什么你的观点比他们的更重要。这可能会促使他们改变接受的答案。根据我的经验,这更可能是问题所在(链接器问题通常在链接时出现(测试代码通常会在构建库之前找到缺少的定义)),并且更重要的是解决了原始发布者遇到的问题。 - Martin York
显示剩余11条评论

53

当你修改一个类,使其继承QObject(以便可以使用信号/槽),Qt C++会显示此错误。运行qmake -r将调用moc并解决此问题。

如果你正在通过某种版本控制与他人合作,你需要对.pro文件进行一些更改(例如添加/删除空行)。当其他人获取到你的更改并运行make时,make将检测到.pro文件已更改并自动运行qmake。这将避免你的队友们重复你的挫败感。


谢谢!没有线索很难找到! - Rémy DAVID
1
另外,如果您忘记将类添加到*.pro文件的HEADER部分,则会收到相同的错误。类的头文件必须是HEADER,以便moc能够识别Q_OBJECT。 - vpicaver
谢谢您的提示!我构建了库,但没有 MOC 的东西... Q_OBJECT... - Gelldur

14

对我来说问题非常难以理解。我的类看起来像这样:

//-----------------------------------------
// libbase.h
class base {
public:
   base() { }
   virtual ~base() { }

   virtual int foo() { return 0; }
};
//-----------------------------------------

//-----------------------------------------
// libbase.cpp
#include "libbase.h"
//-----------------------------------------

//-----------------------------------------
// main.h
class derived : public base {
public:
    virtual int foo() ;
};
//-----------------------------------------

//-----------------------------------------
// main.cpp
int main () {
    derived d;
}
//-----------------------------------------
问题在于链接器。我的头文件被放在库中,但是所有的虚函数都在类声明中被声明为“内联”。由于还没有使用虚函数的代码,编译器或链接器忽略了实际的函数体的放置,也未能创建虚表。
在我的主要代码中,当我从这个类派生时,链接器试图连接我的类与基类和其虚表。但是虚表已经被丢弃了。
解决方法是至少将一个虚函数的函数体声明在类声明之外,就像这样:
//-----------------------------------------
// libbase.h
class base {
public:
   base() { }
   virtual ~base() ;   //-- No longer declared 'inline'

   virtual int foo() { return 0; }
};
//-----------------------------------------

//-----------------------------------------
// libbase.cpp
#include "libbase.h"
base::~base() 
{
}
//-----------------------------------------

对我来说,重要的是子类和它的虚函数。 - A. Binzxxxxxx
你在类定义的结尾处漏掉了分号 :) - Justme0

9
关于Qt4的问题,我不能使用上述提到的qmake moc选项。但那也不是问题所在。我在类定义中有以下代码:
class ScreenWidget : public QGLWidget
{
   Q_OBJECT        // must include this if you use Qt signals/slots
...
};

我不得不删除"Q_OBJECT"这一行,因为我没有定义任何信号或槽。

谢谢!我在使用g++和cmake时遇到了这个问题,但是在studio中编译正常。 - Nicolas Holthaus

8

我遇到了这个错误信息。问题在于我在头文件中声明了一个虚析构函数,但实际上虚函数的代码体并没有实现。


5

当我们在基类中仅声明一个虚函数而没有任何定义时,也会发生此错误。

例如:

class Base
{
    virtual void method1(); // throws undefined reference error.

}

将上述声明更改为以下声明,它将正常工作。

class Base
{
    virtual void method1()
    {
    }
}

这对我真的很有用!你能解释一下为什么会发生这种情况吗?我看过许多类,在那里虚函数被声明而没有花括号。 - kunal18
@stalin 如果你声明if语句时没有使用花括号,那意味着你会在其他地方(通常是.cpp文件)实现它。 - Ven

4
在我的情况下,问题出现在我忘记为纯虚类中的一个函数添加=0时。当添加了=0后,问题得到解决。和Frank遇到的问题一样。
class ISettings
{
public: 
    virtual ~ISettings() {};
    virtual void OKFunction() =0;
    virtual void ProblemFunction(); // missing =0   
};

class Settings : ISettings
{
    virtual ~Settings() {};
    void OKFunction();
    void ProblemFunction(); 
};

void Settings::OKFunction()
{
    //stuff
}

void Settings::ProblemFunction()
{
    //stuff
}

1
我现在也遇到了这个问题。应用程序定义了一个纯虚接口类,用户定义的类通过共享库提供实现接口。当链接应用程序时,链接器抱怨共享库没有为基类提供vtable和type_info,也无法在其他地方找到它们。 结果发现我简单地忘记了使接口的其中一个方法成为纯虚方法(即在声明末尾省略了“= 0”)。非常基础,容易被忽视,如果您无法将链接器诊断与根本原因联系起来,则会感到困惑。

0
如果您有一个具有纯虚函数的基类,请确保该基类的构造函数和析构函数具有实现体,否则链接器将失败。

实际上与构造函数或析构函数无关。没有它们会导致一般的链接器失败。你必须缺少一个虚方法才能在vtable上获得链接器错误。 - Mysticial

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