我该怎么解释这个LNK2005
How do I explain this LNK2005?
所以,有人带着一个项目来找我,这个项目链接失败,错误是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中留下声明来修复。
我对链接器如何工作的理解基本上来自这里。根据我的理解,我们提供的是一个地方需要的符号。
我在LNK2005上查找了MSDN文章,这与我期望链接器的行为相匹配(多次提供符号->链接器混淆),但似乎没有涵盖这种情况(这意味着我不理解链接的一些明显的东西)。
Google和StackOverflow在不包含#ifndef
或#pragma once
的情况下产生问题(这会导致对所提供的符号进行多次声明)
我在这个网站上发现了一个相关的问题,有同样的问题,但答案并没有解释为什么我们得到了这个问题充分到我的理解水平。
我有一个问题,我知道解决方案,但我不知道为什么我的解决方案工作
在典型的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):
每个程序应该只包含一个在该程序中使用的非内联函数或变量的定义;不需要诊断。
你在程序中有多个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++规则——它不能通过使用include守卫或#pragma once
指令来解决,但是,在一个自由函数的情况下,通过将其标记为inline
或将其实现移动到源文件来解决。
当一个非内联方法在头文件中实现时,所有包含该头文件的翻译单元都将定义它。当相应的.obj
文件链接在一起时,链接器检测到相同的符号被多次导出(和定义),并报错。
将实现移动到cpp
文件有效地将您的初始定义转换为声明。
myAwesomeFunction
在两个源文件中定义:something.cpp
和main.cpp
。
- 请解释"函数1(p1,p2,p3);"的输出
- 请解释这句话(cout<<1+int((a<b)^((b-a)&1) )<<endl
- 被解释为低级别const的const对象的地址
- 计算每个节点的树高,帮助我解释这个代码解决方案
- MSVC将仅移动结构参数解释为指针
- 内联程序集printf将整数解释为地址
- 有人能解释一下为什么下界是这样工作的吗C++的
- Visual Studio(或任何其他工具)能否将地址解释为调用堆栈(boost上下文)的开头
- 我是c ++的新手,你能解释一下在这种情况下的指针吗
- 有人能为我解释一下C++代码吗
- 你能解释一下什么运行时错误是如何解决它的吗?
- 请解释字谜的代码,我看不懂计数器数组,每个值已经是0
- 有人可以向我解释为什么控制台输出 0 吗?
- 有人可以解释一下这段代码如何能够反转字符串
- 在 C++ 中解释多维向量的语句时感到困惑
- 解释一下 for (char c : str) 的作用?
- 确切地说,如何解释 std::getline(stream, string) 函数在C++中填充的字符串
- 隐式重新解释引用时强制转换,没有警告/错误
- 运行 C++ 单元测试时LNK2005链接错误
- 我该怎么解释这个LNK2005