GCC C ++链接器错误:对“ vtable for XXX”的未定义引用,对“ ClassName :: ClassName()”的未定义引用


70

我正在使用Eclipse-CDT在Ubuntu x64上设置一个C ++项目。我基本上是在打个招呼,并连接到商业的3rd Party库。

我已经包含了链接到它们的库的头文件,但是仍然出现链接器错误。除了明显的问题之外,这里是否还有其他可能的问题(例如,我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:对`vtable for 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的vtable的引用
/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退出状态
制作:*** [LinkProblem]错误1

第三方库是64位的吗?
Daniel A. White

是的,它是64位。不过,您可能会遇到一些麻烦。如何确保我的代码/项目为64位?在Visual Studio中,我创建了一个x64构建配置。
亚历克斯·布莱克

1
有没有办法确定第三方库是64位的?例如,使用工具或其他工具检查.a文件?
亚历克斯·布莱克

它在哪里?阿谷歌表明,存在具有其半约定在/ usr / lib64下
丹尼尔A.白色

第三方lib文件在这里:/ home / notroot / workspace / somelib-3 / somelib / target / bin
Alex Black,2009年

Answers:


74

假设这些方法在其中一个库中,则看起来像是一个排序问题。

将库链接到可执行文件时,它们按声明的顺序完成。
同样,链接器将仅采用解决当前未解决的依赖关系所需的方法/功能。如果随后的库随后使用对象最初不要求的方法/功能,则将缺少依赖项。

这个怎么运作:

  • 提取所有目标文件并将其合并为可执行文件
  • 解决目标文件之间的所有依赖关系。
  • 对于每个库,顺序为:
    • 检查未解决的依赖项,看看lib是否能解决它们。
    • 如果是这样,将所需的部分加载到可执行文件中。

例:

对象需要:

  • 打开
  • 批量读取
  • 批写

库1提供:

  • 打开

库2提供

  • BatchRead(但使用lib1:read)
  • BatchWrite(但使用lib1:write)

如果像这样链接:

gcc -o plop plop.o -l1 -l2

然后,链接器将无法解析读取和写入符号。

但是,如果我像这样链接应用程序:

gcc -o plop plop.o -l2 -l1

然后它将正确链接。由于l2解决了BatchRead和BatchWrite依赖关系,但还添加了两个新的依赖关系(读和写)。当我们与l1链接时,接下来的所有四个依赖关系都已解决。


您是否在G ++命令行中参考lib文件的顺序?
亚历克斯·布莱克

是。:-)
马丁·约克

我注意到您的示例使用“ gcc”,而我的问题使用“ g ++”,我应该使用“ gcc”吗?
亚历克斯·布莱克

2
如果您的代码包含C ++内容,则需要使用g ++来获取正确的标准库。
马丁·约克

3
这真的帮助了我!我的groupproject突然停止了从我的一个库中接受新的cpp文件,这全都与未定义的引用有关,然后我换了位置,它起了很大的作用。认真的说,关于此问题的某些页面上应该有更具体的文章。非常感谢马丁!
乔纳森

166

通常,此链接器错误(以我的经验)意味着您已使用声明覆盖了子类中的虚函数,但未提供方法的定义。例如:

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

但是您没有给出f的定义。使用该类时,会出现链接器错误。很像普通的链接器错误,这是因为编译器知道您在说什么,但是链接器找不到定义。它只是很难理解的消息。


3
谢谢。我两个小时都找不到解决方案。
problemofficer

2
正是我遇到的问题。谢谢您节省了我的时间和精力。
海德

有趣的是,在我的情况下,它仅在“纯虚拟机”中发生!该错误消息确实令人误解。
mishmashru 2013年

这是我的问题。忘记将其设置为0!
斯科特,

52

当您更改一个类以使其现在从QObject继承时(即现在可以使用信号/插槽),Qt C ++将显示此错误。运行qmake -r将调用moc并解决此问题。

如果您正在通过某种版本控制与他人合作,则需要对.pro文件进行一些更改(即,添加/删除空白行)。当其他所有人进行更改并运行make时,make将看到.pro文件已更改并自动运行qmake。这将使您的队友免于再次感到沮丧。


谢谢 !没有线索很难找到!
雷米·戴维

1
另外,如果您忘记将类添加到* .pro文件的HEADER部分中,则会遇到相同的错误。该类的标头必须为HEADER,moc才能选择Q_OBJECT。
vpicaver

15

对我来说,这个问题非常模糊。我的课看起来像这样:

//-----------------------------------------
// 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;
}
//-----------------------------------------

问题出在链接器中。我的头文件放在某个地方的库中,但是所有虚函数在类声明中都声明为“内联”。由于还没有使用虚拟函数的代码,因此,编译器或链接器忽略了将实际函数体放置到位的情况。它也无法创建vtable。

在从该类派生的主代码中,链接器尝试将我的类连接到基类和他的vtable。但是该vtable已被丢弃。

解决方案是在类声明之外声明至少一个虚拟函数的主体,如下所示:

//-----------------------------------------
// 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 '18

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 2014年

8

我收到此错误消息。问题是我在头文件中声明了虚拟析构函数,但实际上并未实现虚拟函数的主体。


5

当我们简单地在基类中声明没有任何定义的虚函数时,也会发生此错误。

例如:

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

}

将上面的声明更改为下面的声明,它将正常工作。

class Base
{
    virtual void method1()
    {
    }
}

这真的对我有用!你能解释为什么会这样吗?我见过很多类,其中的虚函数声明时没有大括号。
kunal18 2015年

@stalin如果声明不带花括号,则表示将在其他位置实现(通常在.cpp文件中)。
2015年

4

就我而言,当我忘记在纯虚拟类中的一个函数上添加= 0时,便会出现问题。添加= 0时,此问题已修复。与上述弗兰克相同。

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

当尝试使用Qt之类的“ hello world”时,我收到此错误消息。通过正确运行qt moc(元对象编译器)并正确编译+包括这些moc生成的文件,问题得以解决。


0

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


实际上,它与构造函数或析构函数无关。没有这些将使您的通用链接器失败。您必须缺少虚拟方法才能在vtable上获取链接器错误。
Mysticial

0

我把这个给未来的访客:

如果在创建Exception对象时收到错误,则其原因可能是what()虚函数的定义不足。

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.