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

时间:2013-04-11 06:05:09

标签: visual-c++

我最近遇到了一个问题。

我有三个文件,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__

没有错误!!有人能告诉我发生了什么吗?

3 个答案:

答案 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,它是复制/粘贴错误吗?