【问题标题】:How to create a Union the complicated way?如何以复杂的方式创建联盟?
【发布时间】:2019-07-16 01:58:24
【问题描述】:

所以我在玩编译单元时偶然发现了这一点。

我有 2 个标题,它们定义了一个同名的类。第一个编译单元包含第一个头文件并声明指向该类的外部指针,第二个编译单元包含第二个头文件并定义指针。

现在我有 T* 指向一个 U。

麦克维:

h1.h

#pragma once
struct a_struct {
    int i;

    a_struct(int _i) : i{ _i } {}
};

h2.h

#pragma once
struct a_struct {
    float f;

    a_struct(float _f) : f{ _f } {}
};

foo.h

#pragma once
struct foo {
    int bar();
};

cu1.cpp

#include "foo.h"
#include "h1.h"

extern a_struct* s;

int foo::bar() {
    return s->i;
}

cu2.cpp

#include "h2.h"

a_struct* s = new a_struct(1.0f);

main.cpp

#include "foo.h"
#include <iostream>

int main() {

    foo f;

    std::cout << f.bar() << std::endl; // <- 1065353216

    system("PAUSE");
    return 0;
}

为什么链接器看不到 h1.h::a_struct 不是 h2.h::a_struct ?这在标准中是否被称为未定义行为?

(我也知道用相同的名字命名 2 个类是愚蠢的......)

【问题讨论】:

    标签: c++ visual-c++


    【解决方案1】:

    这是在标准中提到的未定义行为吗?

    是的,这违反了单一定义规则的“标题版本”。在此版本中,适用于类定义、inline 函数和变量以及其他通常在头文件中定义的东西,单个实体的多个定义允许在单独的翻译单元中,但这些定义必须都具有相同的标记 (预处理后)并且必须都意味着本质上相同的东西。以这种方式不同的多个定义是未定义的行为。请参阅 C++20 草案中的[basic.def.odr]/12,以及One Definition Rule at cppreference.com 下的第五段。

    为什么链接器看不到 h1.h ::a_struct 不是 h2.h ::a_struct

    在大多数 C++ 实现中,编译器将翻译单元转换为包含函数代码和符号定义的目标文件,并且函数代码可能会使用其他对象定义的附加“未定义符号”。就目标文件而言,除了可能在调试器数据中之外,几乎没有保存关于 C++ 源代码或类型信息的信息。链接器可能会看到 cu1.o 中的函数 foo::bar() 使用未定义符号 s,cu2.o 定义符号 s,并且 cu2.o 的全局动态初始化函数也使用符号 @ 987654329@。链接器只会调整一些东西,以便执行 foo::bar() 将正确访问同一个对象 s,而不用太关心任何函数实际上对属于该符号的字节做了什么。

    (当目标文件不同意与符号关联的字节数时,链接器有时会发出警告,但两个指向类类型对象的指针可能具有相同的大小。)

    【讨论】:

      【解决方案2】:

      编译器编译每个源文件分开。它相信给定的类声明对于所有源文件都是相同的。

      当你这样做时,你会欺骗编译器为某个类编译两个具有两个不同定义的文件。每个文件都会生成一段自洽的代码。

      然后链接器进入并链接您的各种代码位。有一种在所有编译器之间共享的对象/库格式。这是为了允许每个链接器与每个编译器一起工作。此时,链接器只知道一些代码将传递一个 foo 对象,而另一些代码将接收一个 foo 对象。偷看、检查和抱怨不是它的事。

      请记住,在链接时,源代码甚至可能不可用。您可能有来自某些供应商的库,但没有源代码。并且可能有各种#defines 可能会影响此对象。链接器不需要知道编译设置是什么,甚至源是什么。代码甚至可以用另一种语言编写。

      要获得这种灵活性和互操作性,您必须遵守一些规则。其中之一是“不要以不同的方式定义同一个类两次”。

      【讨论】:

      • 是的,我刚刚更新了我的问题,我的意思是链接器。
      • 添加了第 3 段,这可能会提供见解。
      猜你喜欢
      • 1970-01-01
      • 2012-01-14
      • 2019-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-28
      • 1970-01-01
      • 2013-11-11
      相关资源
      最近更新 更多