我最近遇到了一个问题。
我有三个文件,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)
没有更多的错误!有人可以告诉我发生了什么吗?
如果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的多个定义,所有定义都具有相同的实现。这是浪费空间。