2

我最近遇到一个问题。

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

#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 的类:

#ifndef __A_H__
#define __A_H__

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

#endif // __A_H__

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

4

3 回答 3

7

如果 B.cpp 和 C.cpp 包含 Ah,那么两者都使用您的定义进行编译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 文件(对于 Ah,理想情况下是 A.cpp):

int M()
{
    return 1;
}

请注意,extern在头文件中并不是真正需要的。

另一个用户建议你写

static int M()
{
    return 1;
}

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

于 2013-04-11T07:28:25.470 回答
0

请参阅http://faculty.cs.niu.edu/~mcmahon/CS241/c241man/node90.html如何执行 ifdef 守卫。您必须在定义之前从 ifndef 开始。

编辑:啊,不,虽然你的警卫错了,但这不是问题。将 static 放在您的函数前面以使其工作。类是不同的,因为它们定义了类型。

于 2013-04-11T06:13:09.467 回答
0

我不知道幕后是什么,但如果你不需要一个类,我猜编译器会自动将“extern”键添加到你的函数中,所以你会得到包括标题在内的错误 2 次。

您可以将 static 关键字添加到 M() 方法,以便在内存中只有该函数的一个副本,并且在编译时没有错误。

顺便说一句:我看到你有一个#endif,但没有#ifdef 或#ifndef,是复制/粘贴错误吗?

于 2013-04-11T06:51:48.893 回答