为什么结构和类定义可以在多个翻译单元上重复?

时间:2019-04-03 04:12:20

标签: c++ language-lawyer linker-errors

根据一个定义规则,类和结构定义不得在单个翻译单元中重复。

但是,为什么这样的话,如果它们实际上是定义的话,允许以多个翻译单元重复它们。在这种情况下,链接器为什么不引发多定义错误?

例如-以下应该通过相同的逻辑抛出多个定义错误

test.h

#ifndef TEST
#define TEST

class s {
  int a;
  int b;
};

#endif

test1.cpp

#include "test.h"

int main() {}

test2.cpp

#include "test.h"

3 个答案:

答案 0 :(得分:7)

  

但是,既然如此,为什么要允许它们以多个翻译单元重复出现呢?

在语言级别,答案很简单:因为该标准是这样说的,特别是在[basic.def.odr]/6

  

类类型的定义,枚举类型,具有外部链接的内联函数([dcl.inline]),具有外部链接的内联变量([dcl.inline])可能不止一个,类模板,非静态函数模板,类模板的静态数据成员,类模板的成员函数或未指定某些模板参数([temp.spec],[temp.class.spec] )中的程序,前提是每个定义出现在不同的翻译单元中,并且前提是这些定义满足以下要求。 […]

当然,有一定的原因可以说明语言的规则是否正确。在实现级别上,类的定义仅告诉编译器在各个类类型的对象上运行的代码如何进行业务,例如,对象中的成员位于何处等等。但是类定义本身并不能真正生成代码。编译器必须查看使用该类的每个翻译单元中每个类类型的定义,以便编译器可以为每个翻译单元独立生成正确的代码。在每个翻译单元中,类类型的定义也必须相同,以便为每个翻译单元生成的代码与为所有其他翻译单元生成的代码兼容。

  

在这种情况下,为什么链接器不会引发多定义错误?

最后在符号级别工作的链接器只能看到生成的目标代码。而且类定义在机器代码级别没有明确的表示形式。在这个级别上,类的概念并不存在。它们以代码运行的方式而不是直接在代码中找到。如果您愿意,它们可以住在组装线之间……

答案 1 :(得分:2)

类定义在广义上包含两部分-成员变量定义和成员函数声明/定义。 (我知道类可能包含嵌套的类型,枚举,typedef等)。

成员变量定义提供了用于创建对象的模板,它们不是可执行代码。因此,在多个文件中定义它们应该不是问题。

成员函数声明正是-声明。再一次,它们不是可执行代码。因此,在多个文件中定义它们应该不是问题。

成员函数定义(如果是内联的)与任何其他内联函数一样对待。在多个文件中定义它们应该不是问题。

如果成员函数定义不是内联的,则如果它们在.h文件中定义并且.h文件以多个转换单位#include d定义,则会引起问题。

答案 2 :(得分:1)

C语言设计为可在具有非常原始的链接器的平台上使用,从而对它们的要求最低。 C ++语言要求更高。为了使平台实际上支持用C ++编写的单独编译的模块的链接,其链接器必须至少支持弱符号和部分符号或其他类似构造。如果一个弱符号存在两个或多个弱定义,并且没有强定义,则链接器会将一个弱符号(任意选择)视为强符号,而忽略其余符号。如果一个符号存在两个或多个部分定义,则关联数据将以任意顺序连接,并且对该符号的所有引用都将标识连接的Blob的开头。通常,还可以使用一种生成具有相关名称的符号的方式,该符号可以标识Blob的长度或结束地址。

一种要求链接器具有这种功能的语言,即使是极简的链接器,也比必须使用的语言能够为程序员提供更好的语义。