【问题标题】:Why does the same class being defined in multiple .cpp files not cause a linker multiple definition error?为什么在多个 .cpp 文件中定义同一个类不会导致链接器多重定义错误?
【发布时间】:2019-12-22 07:25:01
【问题描述】:

我遇到了一种我不理解的奇怪行为。因此,我在两个不同的 cpp 文件中定义了两个具有相同名称的不同类。我知道这不会在翻译单元的编译过程中导致任何错误,因为它们彼此不了解。但是链接器在将这些文件链接在一起时不应该抛出一些错误吗?

【问题讨论】:

  • 与 C++ 中的很多东西一样,如果你搞砸了,不一定有任何检查可以告诉你。你只会得到未定义的行为。不搞砸是你的工作,你不能总是依靠工具链来告诉你什么时候搞砸了。
  • 请注意,类定义一般在头文件中,然后通过#include在多个cpp文件中。

标签: c++ one-definition-rule


【解决方案1】:

您正在考虑one definition rule。我从那里引用(粗体是我选择的重点,不是原始文档的一部分)。

你的理解是正确的——在多个编译单元中定义同一个函数是违法的:

在整个程序(包括任何标准库和用户定义库)中,需要对每个 odr 使用的非内联函数或变量(见下文)进行一个且只有一个定义。编译器不需要诊断这种违规行为,但违反它的程序的行为是未定义的。

但是,对于可以定义多次(每个编译单元中最多一次)的类,情况并非如此,只要定义完全相同。如果它们相同,那么您可以安全地将该类的实例从一个编译单元传递到另一个编译单元,因为所有编译单元都具有兼容的、相同的定义以及兼容的大小和内存布局。

任何一个翻译单元(其中一些可能有多个声明,但只允许一个定义)。

...

程序中可以有多个定义,只要每个定义出现在不同的翻译单元中,以下各项:类类型、枚举类型、具有外部链接的内联函数 具有外部链接的内联变量 (C++17 起)、类模板、非静态函数模板、类模板的静态数据成员、类模板的成员函数、部分模板特化、概念、 (C++20 起)只要满足以下所有条件:

  • 每个定义都由相同的标记序列组成(通常出现在同一个头文件中)
  • 从每个定义中查找名称会发现相同的实体(在重载解析之后),除了具有内部或没有链接的常量可以引用不同的对象,只要它们不使用 ODR 并且在每个定义中具有相同的值.
  • 重载的运算符,包括转换、分配和解除分配函数在每个定义中引用相同的函数(除非引用定义中定义的函数) 语言链接是相同的(例如,包含文件不在外部“C”块内)
  • 上述三个规则适用于每个定义中使用的每个默认参数
  • 如果定义是针对具有隐式声明构造函数的类,则使用 odr 的每个翻译单元都必须为基类和成员调用相同的构造函数
  • 如果定义是针对模板的,那么所有这些要求都适用于定义点的名称和实例化点的依赖名称

如果满足所有这些要求,程序的行为就好像整个程序中只有一个定义。否则,行为未定义。

项目符号是一种花哨且高度精确的方式,用于指定定义必须在字母上和有效结果上相同。

【讨论】:

  • 那么链接器1定义规则只适用于函数和变量?但是,当我创建一个对象时,链接器如何知道我会使用哪个类呢?
  • @HenryWise 您使用的类的哪个定义并不重要,因为定义必须全部相同(请参阅我最后一个引用块中的要点)。因为定义相同,所以类在内存中具有相同的大小和布局,因此可以安全地由具有定义的任何编译单元处理。
  • 如果它们相同,你会得到未定义的行为。
【解决方案2】:

单一定义规则明确允许这样做,只要这些定义完全、纯正、相同

我的意思是完全相同。即使您将令牌 struct 交换为令牌 classin a case where it would otherwise not matter,您的程序也会有未定义的行为。

这是有充分理由的:通常我们在标题中定义类,并且我们通常将此类标题包含在多个翻译单元中;如果不允许这样做会很尴尬。

同样的道理也适用于inline 函数定义。

至于为什么你没有收到错误:嗯,就像我说的,未定义的行为。从技术上讲,工具链可以诊断这一点,但是由于具有相同名称的多个类定义是一件很平常的事情(如上所述),因此想出非常复杂的逻辑可以说是浪费时间所有事物的链接器试图诊断“事故”。归根结底,就像这种语言中的许多东西一样,您需要自己去尝试把它弄好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-12
    • 1970-01-01
    • 2021-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多