我最近遇到了一个问题。
我有三个文件,A.h,B.cpp,C.cpp:
A.H
#ifndef __A_H__
#define __A_H__
int M()
{
return 1;
}
#endif // __A_H__
B.cpp
#include "A.h"
C.cpp
#include "A.h"
当我通过MSVC编辑这三个文件时,出现错误:
C.obj : error LNK2005: "int __cdecl M(void)" (?M@@YAHXZ) already defined in B.obj
很容易理解,正如我们所知,B.obj有一个名为“M”的符号,C.obj也有一个“M”。 这里出现了错误。
但是,如果我将M方法更改为包含如下所示的方法M的类:
A.H
#ifndef __A_H__
#define __A_H__
class CA
{
public:
int M()
{
return 1;
}
};
#endif // __A_H__
没有错误!!有人能告诉我发生了什么吗?
答案 0 :(得分:4)
如果B.cpp和C.cpp包含A.h,则两者都使用M
的定义进行编译,因此两个目标文件都将包含M
的代码。当链接器收集所有函数时,他看到M
在两个目标文件中定义,并且不知道使用哪个。因此,链接器会引发LNK2005。
如果将函数M
放入类声明,则编译器将/ M
标记/处理为内联函数。此信息将写入目标文件。链接器看到两个目标文件都包含内联版本CA::M
的定义,因此他假设两者都相等并随机选取两个定义中的一个。
如果你写过
class CA {
public:
int M();
};
int CA::M()
{
return 1;
}
这会导致与初始版本相同的问题(LNK2005),因为那时CA::M
不再内联。
正如您现在可能猜到的那样,有两种解决方案可供选择。如果您想要内联M
,请将代码更改为
__inline int M()
{
return 1;
}
如果您不关心内联,请以标准方式执行,并将函数声明放入头文件中:
extern int M();
将函数定义放入cpp文件中(对于A.h,理想情况下是A.cpp):
int M()
{
return 1;
}
请注意,头文件中并不一定需要extern
。
另一位用户建议你写
static int M()
{
return 1;
}
我不推荐这个。这意味着编译器将M
放入两个目标文件中,并将M
标记为仅在每个目标文件本身中可见的函数。如果链接器看到B.cpp中的函数调用M
,它会在B.obj和C.obj中找到M
。两者都将M
标记为静态,因此链接器忽略C.obj中的M
并从B.obj中选择M
。反之亦然,如果C.cpp中的函数调用M
,则链接器从C.obj中选择M
。最终会得到M的多个定义,所有定义都具有相同的实现。这是浪费空间。
答案 1 :(得分:0)
请参阅http://faculty.cs.niu.edu/~mcmahon/CS241/c241man/node90.html如何做ifdef警卫。你必须在定义之前用ifndef开始。
编辑:啊不,虽然你的后卫是错的,但这不是问题。将静态放在函数前面以使其工作。类是不同的,因为它们定义了类型。
答案 2 :(得分:0)
我不知道底层是什么,但如果你不需要一个类,我猜编译器会自动为你的函数添加“extern”键,所以你会得到包括标题2的错误次。
您可以将static关键字添加到M()方法中,这样您在内存中只能拥有该函数的一个副本,并且在编译时没有错误。
顺便说一下:我看到你有一个#endif,但不是#ifdef或#ifndef,它是复制/粘贴错误吗?