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



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

但是,如果它们是现实定义,为什么允许它们在多个翻译单元中重复。为什么在这种情况下链接器不会抛出多个定义错误?

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

测试.h

#ifndef TEST
#define TEST
class s {
int a;
int b;
};
#endif

测试1.cpp

#include "test.h"
int main() {}

测试2.cpp

#include "test.h"

但是,如果它们是现实定义,为什么允许它们在多个翻译单元中重复呢?

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

。 类类型、枚举类型、具有外部链接的内联函数 ([

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

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

为什么在这种情况下链接器不会抛出多个定义错误?

最终在符号级别工作的链接器只能看到生成的对象代码。类定义在机器代码级别没有明确的表示形式。类的概念在那个级别上并不存在。它们存在于代码的运行方式中,而不是直接存在于代码中。如果你愿意,他们住在装配线之间......

类定义包含广义上的两部分 - 成员变量定义和成员函数声明/定义。(我意识到类可能包含嵌套类型、枚举、类型定义等)

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

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

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

如果成员函数定义不是内联的,则如果在 .h 文件中定义,并且 .h 文件在多个翻译单元中#included,则会导致问题。

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

要求链接器具有此类功能的语言可以为程序员提供更好的语义,而不是即使使用极简链接器也必须可用的语言。

最新更新