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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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__

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

答案

如果B.cpp和C.cpp包含A.h,则两者都将以Mdefinition进行编译,因此两个目标文件都将包含M的代码。当链接器收集所有功能时,他看到M在两个目标文件中定义,并且不知道使用哪个。因此,链接器将引发LNK2005。

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

如果您写过

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

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

这将引起与您的初始版本相同的问题(LNK2005),因为那时CA::M不再是内联的。

所以您可能会猜到,有两种解决方案可供您选择。如果要内联M,则将代码更改为

__inline int M()
{
    return 1;
}

如果您不关心内联,那么请按标准方式进行操作,并将函数declaration放入头文件中:

extern int M();

并将函数definition放入一个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的多个定义,所有定义都具有相同的实现。这是浪费空间。

另一答案

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

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

另一答案

我不知道它到底是什么,但是如果您不需要一个类,我想编译器会自动将“ extern”键添加到您的函数中,因此您将收到包括标头的错误两次。

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

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

以上是关于C ++链接错误,符号重新定义的主要内容,如果未能解决你的问题,请参考以下文章

C ++ / Eclipse中的未定义符号错误[关闭]

idea2016.2编译时有错误,代码不提示怎么办

嵌入式 C/C++:现有符号的未定义引用

C ++ XCODE ld:未找到架构x86_64 clang的符号:错误:链接器命令失败,退出代码为1(使用-v查看调用)

error LNK1169: 找到一个或多个多重定义的符号”的解决方法(转载)

error LNK2019: 无法解析的外部符号