代码之家  ›  专栏  ›  技术社区  ›  PinkTurtle

如何以复杂的方式建立工会?

  •  0
  • PinkTurtle  · 技术社区  · 6 年前

    所以我在玩编译单元的时候偶然发现了这个问题。

    我有两个头定义了一个同名的类。第一个编译单元包含第一个头并声明指向类的外部指针,第二个编译单元包含第二个头并定义指针。

    现在我已经指着一个U。

    MCVE:

    H1.H

    #pragma once
    struct a_struct {
        int i;
    
        a_struct(int _i) : i{ _i } {}
    };
    

    氢H

    #pragma once
    struct a_struct {
        float f;
    
        a_struct(float _f) : f{ _f } {}
    };
    

    福奥

    #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);
    

    主CPP

    #include "foo.h"
    #include <iostream>
    
    int main() {
    
        foo f;
    
        std::cout << f.bar() << std::endl; // <- 1065353216
    
        system("PAUSE");
        return 0;
    }
    

    为什么链接器没有看到h1.h::a_结构不是h2.h::a_结构?标准中是否提到这是未定义的行为?

    (我也知道用同样的名字命名两个类是愚蠢的…)

    2 回复  |  直到 6 年前
        1
  •  1
  •   Jeffrey    6 年前

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

    当您执行上述操作时,您将诱使编译器为某些类编译两个具有两个不同定义的文件。每个文件生成一段自一致的代码。

    然后链接器进来 链接 你各种各样的代码在一起。有一种对象/库格式,可以在所有编译器之间共享。这是为了允许每个链接器与每个编译器一起工作。此时,链接器只知道一些代码将通过 对象和其他一些代码将接收 对象。偷看、检查、抱怨都不关它的事。

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

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

        2
  •  2
  •   aschepler    6 年前

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

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

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

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

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