【发布时间】:2017-11-03 02:39:43
【问题描述】:
好的,我有 3 个文件、一个标头、该标头的来源和一个主文件。在头文件中,我定义了一个带有函数的类。在源文件中,我定义了函数。但是,在主文件中,我重新定义函数,然后在主函数中创建类的实例并调用该函数。这编译得很好 - 没有任何警告。至少可以说输出是可怕的。
头文件:testme.h
#ifndef testme_h_
#include <iostream>
using namespace std;
class wtf {
public:
string getStr();
};
#endif
来源:testme.cpp
#include "testme.h"
string wtf::getStr() {
return "Hello World!";
};
主要:main.cpp
#include <iostream>
using namespace std;
#include "testme.h"
string wtf::getStr()
{
return "God is Dead.";
}
int main()
{
wtf f;
cout << f.getStr() << endl;
}
输出:
God is Dead.
为什么会这样?为什么没有关于多个定义的错误?为什么源文件的定义会被忽略?为什么没有警告?
部分回答 当它被重新编译为“g++ main.cpp testme.cpp -o sanity.o”时,实际上会产生链接器错误。
然而,让我感到惊讶的是,我写的这个小案例反映了我在一个更大的程序中遇到的一个问题,该程序在库中定义了一个函数,然而我们在另一个“测试套件”程序中重新定义了一个函数,几乎相同大大地。为什么会这样?如果它在库中允许它覆盖 ODR 怎么样?
【问题讨论】:
-
未定义行为,您违反了单一定义规则。请参阅en.cppreference.com/w/cpp/language/definition中的一个定义规则
-
否,编译器仅将每个 .cpp 文件视为单独的编译单元。链接器可能会抱怨,或者它可能只是选择一个并丢弃另一个。正如他们所说,UB就是UB。
-
不一定——你的编译器/链接器命令是什么?您是否与 testme.o 链接?
-
@UtMan88 使用该命令,编译器永远不会看到
testme.o,因此它不知道还有另一个定义。 -
未定义行为的部分理由是某些规则难以执行。如果规则易于执行,委员会通常需要错误或某种诊断。但是,如果规则难以执行,那么委员会宣布违反规则将是未定义的。在某些情况下,编译器可能会发现这个问题;特别是如果链接器和编译器是同一个程序。但是在某些情况下(单独编译),编译器不一定有足够的信息来执行一个定义规则。