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

2024-04-24 23:24:25 发布

您现在位置:Python中文网/ 问答频道 /正文


Tags: python
3条回答

班级成员:

纯析构函数需要实现

声明析构函数pure仍然需要定义它(与常规函数不同):

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

这是因为基类析构函数是在对象被隐式销毁时调用的,所以需要定义。

virtual方法必须实现或定义为纯方法。

这类似于没有定义的非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()为纯:

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

非-virtual类成员

即使未明确使用,也需要定义一些成员:

struct A
{ 
    ~A();
};

以下情况会产生错误:

A a;      //destructor undefined

实现可以是内联的,在类定义本身中:

struct A
{ 
    ~A() {}
};

或者在外面:

A::~A() {}

如果实现在类定义之外,但在头中,则必须将方法标记为inline,以防止多重定义。

如果使用,则需要定义所有已使用的成员方法。

一个常见的错误是忘记限定名称:

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

可以为类定义中的staticconst整数或枚举类型的数据成员提供初始值设定项;但是,odr使用此成员仍需要如上所述的命名空间作用域定义。C++ 11允许在所有类内初始化{^ }数据成员。

未能链接到适当的库/对象文件或编译实现文件

通常,每个翻译单元将生成一个对象文件,其中包含在该翻译单元中定义的符号的定义。 要使用这些符号,必须链接到这些对象文件。

gcc下,您可以在命令行中指定要链接在一起的所有对象文件,或者一起编译实现文件。

g++ -o test objectFile1.o objectFile2.o -lLibraryName

这里的libraryName只是库的裸名称,没有特定于平台的添加。例如,在Linux上,库文件通常被称为libfoo.so,但您只需编写-lfoo。在Windows上,同一个文件可能被称为foo.lib,但您将使用相同的参数。您可能需要添加使用-L‹directory›可以找到这些文件的目录。请确保不要在-l-L之后写入空格。

对于XCode:添加用户头搜索路径->;添加库搜索路径->;将实际库引用拖放到项目文件夹中。

MSVS下,添加到项目中的文件会自动将其对象文件链接在一起,并生成一个lib文件(常用)。要在单独的项目中使用这些符号,您需要 需要在项目设置中包含lib文件。这是在项目属性的链接器部分中完成的,位于Input -> Additional Dependencies。(指向lib文件的路径应该是 添加到Linker -> General -> Additional Library Directories)使用随lib文件提供的第三方库时,如果不这样做,通常会导致错误。

还可能发生忘记将文件添加到编译中的情况,在这种情况下,将不会生成对象文件。在gcc中,您可以将文件添加到命令行。在MSVS中,将文件添加到项目将使其自动编译(尽管可以手动将文件从生成中单独排除)。

在Windows编程中,未链接必要库的标志是未解析符号的名称以__imp_开头。在文档中查找函数的名称,它应该说明需要使用哪个库。例如,MSDN将信息放在一个名为“Library”的部分中每个函数底部的框中。

编译C++程序,分为几个步骤,如<强> 2.2 <强>(credits to Keith Thompson for the reference):

The precedence among the syntax rules of translation is specified by the following phases [see footnote].

  1. Physical source file characters are mapped, in an implementation-defined manner, to the basic source character set (introducing new-line characters for end-of-line indicators) if necessary. [SNIP]
  2. Each instance of a backslash character (\) immediately followed by a new-line character is deleted, splicing physical source lines to form logical source lines. [SNIP]
  3. The source file is decomposed into preprocessing tokens (2.5) and sequences of white-space characters (including comments). [SNIP]
  4. Preprocessing directives are executed, macro invocations are expanded, and _Pragma unary operator expressions are executed. [SNIP]
  5. Each source character set member in a character literal or a string literal, as well as each escape sequence and universal-character-name in a character literal or a non-raw string literal, is converted to the corresponding member of the execution character set; [SNIP]
  6. Adjacent string literal tokens are concatenated.
  7. White-space characters separating tokens are no longer significant. Each preprocessing token is converted into a token. (2.7). The resulting tokens are syntactically and semantically analyzed and translated as a translation unit. [SNIP]
  8. Translated translation units and instantiation units are combined as follows: [SNIP]
  9. All external entity references are resolved. Library components are linked to satisfy external references to entities not defined in the current translation. All such translator output is collected into a program image which contains information needed for execution in its execution environment. (emphasis mine)

[footnote] Implementations must behave as if these separate phases occur, although in practice different phases might be folded together.

指定的错误发生在编译的最后阶段,通常称为链接。它基本上意味着你将一堆实现文件编译成对象文件或库,现在你想让它们一起工作。

假设您在a.cpp中定义了符号a。现在,b.cpp声明了这个符号并使用了它。在链接之前,它只是假设符号是在某处定义的,但它还不关心在哪里。链接阶段负责找到符号并将其正确链接到b.cpp(实际上,是链接到使用符号的对象或库)。

如果您使用的是Microsoft Visual Studio,您将看到项目生成.lib文件。其中包含一个导出符号表和一个导入符号表。导入的符号将根据链接所针对的库进行解析,导出的符号将为使用该.lib(如果有)的库提供。

其他编译器/平台也有类似的机制。

常见的错误消息有error LNK2001error LNK1120error LNK2019(对于Microsoft Visual Studioundefined reference tosymbolName对于GCC

代码:

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

以及与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

常见原因包括:

相关问题 更多 >