71

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

我已经包含了链接到他们的库的头文件,但我仍然遇到链接器错误。除了明显的问题之外,这里是否存在一些可能的问题(例如,我 99% 确定我正在链接到正确的库)。

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

日食 说:

构建目标: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 的 vtable”的引用
/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 个退出状态
make: *** [LinkProblem] 错误 1
4

12 回答 12

168

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

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

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

于 2010-08-07T13:06:15.130 回答
74

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

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

这个怎么运作:

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

例子:

对象要求:

  • 打开
  • 关闭
  • 批量读取
  • 批量写入

Lib 1 提供:

  • 打开
  • 关闭

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 依赖项,但还添加了两个新的依赖项(读取和写入)。当我们与 l1 链接时,所有四个依赖项都被解决。

于 2009-07-07T23:11:57.463 回答
52

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

如果您通过某种版本控制与其他人一起工作,您将需要对您的 .pro 文件进行一些更改(即添加/删除空白行)。当其他人得到您的更改并运行 make 时,make 将看到 .pro 文件已更改并自动运行 qmake。这将使您的队友免于重复您的挫败感。

于 2011-02-14T18:34:15.500 回答
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;
}
//-----------------------------------------

问题出在链接器中。我的头文件放在某个库中,但所有虚函数在类声明中都被声明为“内联”。由于(还)没有使用虚拟函数的代码,编译器或链接器忽略了将实际函数体放置到位。它也未能创建 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() 
{
}
//-----------------------------------------
于 2011-05-26T21:06:02.647 回答
9

关于 Qt4 的问题,我不能使用上面提到的 qmake moc 选项。但这无论如何都不是问题。我在类定义中有以下代码:

class ScreenWidget : public QGLWidget
{
   Q_OBJECT        // must include this if you use Qt signals/slots
...
};

我不得不删除“Q_OBJECT”行,因为我没有定义信号或槽。

于 2011-06-02T07:51:50.757 回答
8

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

于 2012-01-09T07:55:32.363 回答
5

当我们在基类中简单地声明一个没有任何定义的虚函数时,也会出现这个错误。

例如:

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

}

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

class Base
{
    virtual void method1()
    {
    }
}
于 2013-05-13T06:15:37.660 回答
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
}
于 2013-05-02T11:44:01.197 回答
1

我现在也偶然发现了这个问题。该应用程序定义了一个纯虚拟接口类,并且通过共享库提供的用户定义类应该实现该接口。链接应用程序时,链接器抱怨共享库不会为基类提供 vtable 和 type_info,也无法在其他任何地方找到它们。结果我只是忘记将接口的方法之一设为纯虚拟(即在声明末尾省略了“= 0”。非常简陋,如果您无法将链接器诊断连接到根本原因。

于 2013-02-15T14:36:35.910 回答
0

尝试使用 Qt 之类的“hello world”时出现此错误消息。通过正确运行 qt moc(元对象编译器)并正确编译+包含这些 moc 生成的文件,问题就消失了。

于 2010-11-03T21:26:48.007 回答
0

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

于 2014-03-24T17:36:53.927 回答
0

我把这个给未来的访客:

如果您在创建Exception对象时收到错误,那么其原因可能是缺少what()虚函数的定义。

于 2016-10-18T20:56:02.570 回答