C ++ - 一个函数声明,多个定义,它仍然有效吗?

时间:2017-06-01 20:01:06

标签: c++ function-definition

好的,我有3个文件,一个标题,所述标题的来源和一个主要文件。在头文件中,我定义了一个带有函数的类。在源文件中,我定义了该函数。但是,在主文件中,我重新定义函数,然后在main函数中创建类的实例并调用该函数。编译得很好 - 没有任何警告。至少可以说输出是可怕的。

标题: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: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.

为什么这样做?为什么没有关于多个定义的错误?为什么源文件的定义会被忽略?为什么没有警告?

部分答案 当它被重新编译为&#34; g ++ main.cpp testme.cpp -o sanity.o&#34;事实上,它确实会产生链接器错误。

然而,我得到的这个小案例反映了我在一个更大的程序中遇到的问题,该程序具有在库中定义的函数,但我们在另一个&#34;测试套件中重新定义了一个函数&#34;程序大致相同。为什么会这样?它在库中的情况如何允许它覆盖ODR?

1 个答案:

答案 0 :(得分:0)

  

为什么这样做?为什么多个定义没有错误?为什么源文件的定义会被忽略?为什么没有警告?

     

部分答案当重新编译为“g ++ main.cpp testme.cpp -o sanity.o”时,它确实会产生链接器错误。

C ++的设计和演变中,Stroustrup说,在设计一个特性时,他偶尔可以选择(1)编译器能够强制执行警告和错误消息的复杂规则。 ,或(2)编译器可能无法在所有情况下强制执行的简单规则。他试图选择简单的规则,他希望编译器最终能够执行/检测错误。 C和C ++的几个部分有效地展示了20世纪70年代,80年代,90年代等的艺术水平(例如,inlineregistervolatile )。可能会有更多。

粗略地说,这种权衡是标准中未定义行为的来源。如果无法强制执行简单规则(或委员会认为强制执行该规则很昂贵),则规则将保留在标准中,并且违规将被声明为未定义的行为。 It is the programmer's responsibility to never trigger undefined behavior。编译器和相关工具有可能在某些时候强制执行其中一些规则。某些编译器或相关工具甚至可以一直强制执行其中一些规则。但是,一般来说,你是独立的。

设计和演变甚至包括对您的具体问题的讨论。在编写编译器之后,Stroustrup也不想编写链接器。他提出了一种依赖系统链接器的简单方法,但在早期,许多连接器对符号的长度有严格的限制。他努力说服编写系统链接器的人改变它们以使它们与Cfront一起工作。最终,他成功了。

今天,链接器通常附带编译器,但标准委员会仍将其视为一个单独的工具,可能不在编写编译器的人的控制之下。一个定义规则主要用于确保编译器的输出与大多数标准链接器兼容,但委员会不要求这些链接器能够检测到ODR的违规。此外,程序可以逐步编译和链接,因此不能保证链接器有足够的信息来检测违反ODR的行为。

值得注意的是many compilers将为inline函数和template函数生成多个符号,期望链接器抛出冗余定义。因此,链接器看到多个定义并不一定是错误。 (就ODR而言,这些定义必须相同,以便链接器可以丢弃除一个以外的所有定义。)

  

它在库中的含义如何允许它覆盖ODR?

我不会说“覆盖”。听起来这种行为是故意的。我会说“没有被发现就违反了。”答案很简单“标准委员会不希望链接器始终检测到违规行为,许多链接器不会。”避免ODR违规是您的责任。但是,您不是自己的:名称空间部分存在部分是为了使这个问题更容易解决。