C 在多个源文件中包含具有同一类实现的不同标题文件



例如

A.H

class Dummy {
public:
  Dummy() { std::cout << "a.h" << std::endl; }
};

b.h

class Dummy {
public:
  Dummy() { std::cout << "b.h" << std::endl; }
};

c.cc

#include "a.h"
void test() {
  Dummy a;
}

d.cc

#include "b.h"
int main() {
  Dummy a;
  return 0;
}

然后用命令编译源文件

g++ d.cc c.cc

输出是

b.h

但是使用命令

g++ c.cc d.cc

输出是

a.h

我的问题是为什么没有multiple definition错误,为什么输出取决于汇编的顺序?

您的程序具有未定义的行为。总结C 标准的拍摄,以下是[Basic.def.odr/6],我的重点是:

一个类型的定义不止一个,[...] 程序规定每个定义出现在不同的 翻译单元,并提供了满足以下定义 要求。给定这样一个名为D的实体,以多个以上定义 翻译单元,然后

  • d的每个定义应由代币的相同序列组成;和

  • [...]

[...]如果D的定义满足所有这些要求,则 行为好像是D. 的单个定义。 D的定义不满足这些要求,然后是行为 是未定义的。

因此,您观察到两种不同的行为。完全可以接受的,鉴于语言对您甚至应该看到的行为没有任何限制。您违反了合同,因此没有保证。

现在,从实际的角度来看,您看到的只是在上述合同下运行的海湾合作委员会。它假设您不会违反它(即使您这样做),而只是忽略了Dummy和/或其成员的任何后续重新定义。第一个"获胜"。

编译器未检测到多重定义错误,因为c.ccd.cc是单独的翻译单元。它们彼此分开处理;每个都有Dummy::Dummy构造函数的一个定义。

链接器未检测到多个定义错误,因为从标题中的Dummy::Dummy构造函数的定义视为内联定义。该语言允许每个翻译单元中的内联定义,只要所有翻译单元都相同。通常,这些定义是相同的原因是它们都来自同一标头文件,但是标准也要求这些定义是相同的,即使它们来自不同的文件。

当您的程序违反此规则时,其行为是不确定的。这就是为什么您的程序取决于翻译过程中更改翻译单元顺序的看似无关的行为。

最新更新