c++ - 这个LNK2005怎么解释?

标签 c++ linker linker-errors

所以,有人带着一个链接失败的项目来找我,错误是 LNK2005:符号已在对象中定义(使用 Visual Studio 2010)。在这种情况下,我知道什么是错误的(因此可以向他们指出正确的解决方案),但我不知道为什么这是错误的一个很好的解释(以防止它再次发生)。

// something.h
#ifndef _SOMETHING_H
#define _SOMETHING_H
int myCoolFunction();

int myAwesomeFunction() // Note implementing function in header
{
    return 3;
}
#endif

-

// something.cpp
#include "something.h"
int myCoolFunction()
{
    return 4;
}

-

// main.cpp
#include <iostream>
#include "something.h"

int main()
{
    std::cout << myAwesomeFunction() << std::endl;
}

链接失败,通过将 myAwesomeFunction() 放入 .cpp 并在 .h 中留下声明来解决。

我对链接器工作原理的理解大部分来自 here .据我了解,我们提供了一个地方需要的符号。

我查找了 MSDN article on LNK2005 ,这符合我期望链接器的行为方式(不止一次提供符号 -> 链接器混淆),但似乎没有涵盖这种情况(这意味着我不理解关于链接的一些明显的东西)。

Google 和 StackOverflow 对不包含 #ifndef#pragma once 的人产生问题(这会导致提供的符号的多个声明)

A related question I found on this site有同样的问题,但答案没有解释为什么我们以我的理解水平充分解决这个问题。

我有一个问题,我知道解决方案,但我不知道为什么我的解决方案有效

最佳答案

在典型的 C++ 项目中,您分别编译每个实现(或 .cpp)文件 - 您通常从不将头文件(或 .h)传递给直接编译器。在执行所有预处理和包含之后,这些文件中的每一个都成为一个翻译单元。因此,在您给出的示例中,有两个翻译单元如下所示:

  • main.cpp 翻译单元:

    // Contents of <iostream> header here
    
    int myCoolFunction();
    
    int myAwesomeFunction() // Note implementing function in header
    {
        return 3;
    }
    
    int main()
    {
        std::cout << myAwesomeFunction() << std::endl;
    }
    
  • something.cpp 翻译单元:

    int myCoolFunction();
    
    int myAwesomeFunction() // Note implementing function in header
    {
        return 3;
    }
    
    int myCoolFunction()
    {
        return 4;
    }
    

请注意,这两个翻译单元都包含重复的内容,因为它们都包含 something.h。如您所见,只有一个上述翻译单元包含 myCoolFunction 的定义。那挺好的!但是,它们包含 myAwesomeFunction 的定义。太糟糕了!

翻译单元分别编译后,再链接起来形成最终程序。关于跨翻译单元的多个声明有一定的规则。其中一条规则是 (§3.2/4):

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required.

您的程序中有多个 myAwesomeFunction 定义,因此您违反了规则。这就是您的代码无法正确链接的原因。

可以从链接器的角度来想。编译完这两个翻译单元后,您就有了两个目标文件。链接器的工作是将目标文件连接在一起以形成最终的可执行文件。因此它会在 main 中看到对 myAwesomeFunction 的调用,并尝试在其中一个目标文件中找到相应的函数定义。但是,有两个 定义。链接器不知道要使用哪一个,所以它就放弃了。

现在让我们看看如果在 something.cpp 中定义 myAwesomeFunction 翻译单元会是什么样子:

  • 修复了 main.cpp 翻译单元:

    // Contents of <iostream> header here
    
    int myCoolFunction();
    
    int myAwesomeFunction();
    
    int main()
    {
        std::cout << myAwesomeFunction() << std::endl;
    }
    
  • 修复了 something.cpp 翻译单元:

    int myCoolFunction();
    
    int myAwesomeFunction();
    
    int myCoolFunction()
    {
        return 4;
    }
    
    int myAwesomeFunction()
    {
        return 3;
    }
    

现在完美了。现在整个程序中只有一个 myAwesomeFunction 的定义。当链接器在 main 中看到对 myAwesomeFunction 的调用时,它确切地知道应该将它链接到哪个函数定义。

关于c++ - 这个LNK2005怎么解释?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15385083/

相关文章:

c++ - C/C++ getopt optstring 语法

linux - 使用调试符号从源代码编译 glibc

c++ - 使用纯头库时增加链接器错误

c++ - constexpr 函数的 undefined symbol

c++ - 链接器错误 : _main already defined in *. obj

c++ - int * i 和 int** i 的区别

c++ - 如何从 Arch Linux 构建基本 64 位 Amazon Linux?

android - 是否可以释放 C++ 分配的内存输出

gcc - 链接器如何知道将哪些文件链接在一起?

c - C工程编译的时候怎么最后没有创建main.o?