C ++链接错误,符号重新定义

Qua*_*ark 2 visual-c++

我最近遇到了一个问题。

我有三个文件,Ah,B.cpp和C.cpp:

#ifndef __A_H__
#define __A_H__

int M()
{
    return 1;
}

#endif // __A_H__
Run Code Online (Sandbox Code Playgroud)

丙型肝炎

#include "A.h"
Run Code Online (Sandbox Code Playgroud)

丙型肝炎

#include "A.h"
Run Code Online (Sandbox Code Playgroud)

当我通过MSVC编译这三个文件时,出现一个错误:

C.obj : error LNK2005: "int __cdecl M(void)" (?M@@YAHXZ) already defined in B.obj
Run Code Online (Sandbox Code Playgroud)

众所周知,B.obj有一个名为“ M”的符号,C.obj也有一个“ M”。错误来了。

但是,如果我将M方法更改为包含如下所示的方法M的类:

#ifndef __A_H__
#define __A_H__

class CA
{
public:
    int M()
    {
        return 1;
    }
};

#endif // __A_H__
Run Code Online (Sandbox Code Playgroud)

没有更多的错误!有人可以告诉我发生了什么吗?

Wer*_*nze 6

如果B.cpp和C.cpp包括啊,那么两者都编译你定义M,所以这两个目标文件将包含的代码M。当链接器收集所有功能时,他看到M在两个目标文件中定义了该功能,并且不知道要使用哪个功能。因此,链接器引发了LNK2005。

如果将函数M放入类声明中,则编译器将标记/处理M为内联函数。该信息被写入目标文件。链接器看到两个目标文件都包含的内联版本的定义CA::M,因此他假定两者都相等,并随机选择这两个定义之一。

如果你写过

class CA {
public:
    int M();
};

int CA::M()
{
    return 1;
}
Run Code Online (Sandbox Code Playgroud)

这会引起与您的初始版本相同的问题(LNK2005),因为那样CA::M就不会再进行内联了。

因此,您可能已经猜到了,有两种解决方案可供您选择。如果要M内联,则将代码更改为

__inline int M()
{
    return 1;
}
Run Code Online (Sandbox Code Playgroud)

如果您不关心内联,那么请以标准方式进行,然后将函数声明放入头文件中:

extern int M();
Run Code Online (Sandbox Code Playgroud)

并将函数定义放入cpp文件中(对于Ah,理想情况下,它将是A.cpp):

int M()
{
    return 1;
}
Run Code Online (Sandbox Code Playgroud)

请注意,extern头文件中的并不是必需的。

另一个用户建议您写

static int M()
{
    return 1;
}
Run Code Online (Sandbox Code Playgroud)

我不建议这样做。这意味着编译器将同时M放入您的目标文件并将其标记M为仅在每个目标文件本身中可见的函数。如果链接器发现B.cpp中的函数正在调用M,它将M在B.obj和C.obj中找到。两者都M标记为静态,因此链接器M在C.obj中忽略并M从B.obj中选择。反之亦然,如果C.cpp中的函数调用M,则链接器M将从C.obj中选择。您将最终获得M的多个定义,所有定义都具有相同的实现。这是浪费空间。