什么是未定义的引用 / 未解析的外部符号错误,如何修复它?

请问什么是未定义的引用 / 未解析的外部符号错误,该如何修复它?

指定相互依赖的链接库的顺序是错误的
如果library之间相互依赖,那么library链接的顺序确实很重要。 一般来说,如果 a 依赖于 b,那么 libA 必须出现在链接器标志中的 libB 之前。
例如:

// B.h#ifndef B_H#define B_H
struct B {
    B(int);
    int x;};
#endif
// B.cpp#include "B.h"
B::B(int xx) : x(xx) {}
// A.h#include "B.h"
struct A {
    A(int x);
    B b;};
// A.cpp#include "A.h"

A::A(int x) : b(x) {}
// main.cpp#include "A.h"
int main() {
    A a(5);
    return 0;};

创建库:

$ g++ -c A.cpp
$ g++ -c B.cpp
$ ar rvs libA.a A.o 
ar: creating libA.a
a - A.o
$ ar rvs libB.a B.o 
ar: creating libB.a
a - B.o

编译:

$ g++ main.cpp -L. -lB -lA./libA.a(A.o): In function `A::A(int)':
A.cpp:(.text+0x1c): undefined reference to `B::B(int)'
collect2: error: ld returned 1 exit status
$ g++ main.cpp -L. -lA -lB
$ ./a.out

所以再重复一遍,顺序确实很重要!

第一是确保编译时能找到所有依赖的共享库,第二是依赖库添加的顺序,按照依赖顺序列出。

引入变量,但没有定义变量或函数
一个典型的变量引入是
extern int x;
因为这只是引入,所以需要一个单一的定义。一个相应的定义是:
int x;
例如,下面的代码会产生一个错误:
extern int x;int main(){
x = 0;}//int x; // uncomment this line for successful definition
类似的注释也适用于函数。引入一个函数而不定义它会导致错误:
void foo(); // declaration onlyint main(){
foo();}//void foo() {} //uncomment this line for successful definition
请注意,实现的函数与引入的函数完全匹配。例如,你可能有不匹配的 cv 修饰符:

void foo(int& x);int main(){
   int x;
   foo(x);}void foo(const int& x) {} //different function, doesn't provide a definition
                          //for void foo(int& x)

其他不匹配的例子包括
• 在一个namespace中引入的函数 / 变量,在另一个namespace中定义
• 引入为类成员的函数 / 变量,定义为全局(反之亦然)
• 函数返回类型、参数号和类型以及调用约定并不完全一致

所谓的Class
纯virtual函数需要执行
确定一个析构函数仍然需要定义它(不像一个常规函数) :

struct X{
    virtual ~X() = 0;};struct Y : X{
    ~Y() {}};int main(){
    Y y;}//X::~X(){} //uncomment this line for successful definition

这是因为在消除对象时调用基本类别析构函数,所以需要定义。
这类似于没有定义non-virtual方法,添加了定义生成虚拟 vtable 的推理,您可能会在不使用函数的情况下得到链接器错误:

struct X{
    virtual void foo();};struct Y : X{
   void foo() {}};int main(){
   Y y; //linker error although there was no call to X::foo}

为了实现这一点,将 x: : foo ()定为 pure:

struct X{
    virtual void foo() = 0;};

Non-virtual类型
有些即使没有明确使用也需要定义:
struct A{
~A();};
下列情况会产生错误:
A a; //destructor undefined
在类定义本身中,实现可以是内联的:
struct A{
~A() {}};
或者在外部关联:
A::~A() {}
一个常见的错误是,忘记修改命名:

struct A{
   void foo();};
void foo() {}
int main(){
   A a;
   a.foo();}

定义应该是
void A::foo() {}
static 数据必须在类之外的单个翻译单元中定义:

struct X{
    static int x;};int main(){
    int x = X::x;}//int X::x; //uncomment this line to define X::x

要初始化为类定义中的 static const 数据,但是,他们的odr-use 使用仍然需要上面描述的名称空间范围定义。C + + 11允许在类中为所有staticconst数据初始化。

编译一个 c + + 程序需要几个步骤,步骤如下:
翻译的语法规则之间的优先顺序由以下几个阶段规定(见脚注)。
1. 如果需要,物理源文件字符将以实现定义的映射到基本源字符集(为行尾指示符引入新的行字符)[SNIP]
2. 删除新行字符的每个反斜杠字符()实例,将物理源代码行拼接成逻辑源代码行[SNIP]
3. 源文件被分解为预处理标记(2.5)和空白字符序列(包括注释)[SNIP]
4. 执行预处理指令,展开宏调用,并执行杂注一元运算符表达式[SNIP]
5. 字符文本或字符串文本中的每个源字符集成员,以及字符文本或非原始字符串文本中的每个转义序列和通用字符名,都转换为执行字符集;[SNIP]
6. 连接相邻字符串文字标记
7. 分隔标记的空白字符不再有意义。每个预处理token都被转换为一个token。 (2.7). 结果标记从语法和语义上进行分析,并作为翻译单元进行翻译[SNIP]
8. 翻译翻译单位和实例化单位合并如下:[SNIP]
9. 解析所有外部实体引用。 库组件链接以满足对当前翻译中未定义的实体的外部引用。 所有这样的转换器输出被收集到一个程序映像中,该映像包含在其执行环境中执行所需的信息。
[footnote] Implementations must behave as if these separate phases occur, although in practice different phases might be folded together.
脚注:执行过程应该像这些独立的阶段发生了一样,尽管在实践中不同的阶段可能会合并在一起。
指定的错误发生在编译的最后阶段,通常称为链接。它基本上意味着您将一组实现文件编译成目标文件或库,现在想让它们一起工作。
假设您在 a.cpp 中定义了 a , 现在,b.cpp确定了并使用a。 在链接之前,它只是假设这个符号是在某个地方定义的,但是它还不关心在哪里定义的。 链接阶段负责查找符号并正确地将其链接到 b.cpp (实际上是使用它的对象或库)。
如果您正在使用 Microsoft Visual Studio,将生成.lib文件。包含一个输出符号表和一个输入符号表。导入的符号根据所链接的库进行解析,并为使用该库的库提供导出的符号。
其他编译器 / 平台也有类似的机制。
在Microsoft Visual Studio,常见的错误消息是 error LNK2001, error LNK1120, error LNK2019
代码如下:

struct X{
   virtual void foo();};struct Y : X{
   void foo() {}};struct A{
   virtual ~A() = 0;};struct B: A{
   virtual ~B(){}};extern int x;void foo();int main(){
   x = 0;
   foo();
   Y y;
   B b;}

在 GCC 中产生以下错误:

/home/AbiSfw/ccvvuHoX.o: In function `main':
prog.cpp:(.text+0x10): undefined reference to `x'
prog.cpp:(.text+0x19): undefined reference to `foo()'
prog.cpp:(.text+0x2d): undefined reference to `A::~A()'/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD1Ev[B::~B()]+0xb): undefined reference to `A::~A()'/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD0Ev[B::~B()]+0x12): undefined reference to `A::~A()'/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1Y[typeinfo for Y]+0x8): undefined reference to `typeinfo for X'/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1B[typeinfo for B]+0x8): undefined reference to `typeinfo for A'
collect2: ld returned 1 exit status

and similar errors with :
和Microsoft Visual Studio类似的错误:

1>test2.obj : error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)1>test2.obj : error LNK2001: unresolved external symbol "int x" (?x@@3HA)1>test2.obj : error LNK2001: unresolved external symbol "public: virtual __thiscall A::~A(void)" (??1A@@UAE@XZ)1>test2.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall X::foo(void)" (?foo@X@@UAEXXZ)1>...\test2.exe : fatal error LNK1120: 4 unresolved externals