初始化顺序

时间:2012-01-03 06:08:00

标签: c++ initialization

我的问题与全局对象的初始化顺序有关。这里有一定程度的讨论:C++: When (and how) are C++ Global Static Constructors Called?

我想知道的是,如何确保某些系统库对象(虽然不能想到一个例子)在应用程序中使用之前进行初始化? (我知道解决方案是将这个对象包装在一个函数中,但我想了解它今天在编译器中的处理方式)

约翰·莱文(John Levine)在“连接器和装载器”一书中讨论过这个问题,同时讨论.init和.finit部分,但目前的艺术状况尚不清楚。

3 个答案:

答案 0 :(得分:4)

一个例子是cout

编译器并不真正保证在使用它们之前构建这些对象。拿这个代码:

struct Foo
{

    Foo();
}foo;

#include <iostream>

Foo::Foo()
{
    std::cout << "Hello World";
}

int main() {}

它是段错误的,因为foo对象在cout之前构建。当它试图使用cout时,会发生不好的事情。

基本上,所有全局对象都会插入代码来构造它们在main之前运行。一旦你在主,你是安全的,所有对象都是建成的。在此之前,它取决于它们的定义顺序。这就是为什么我可以在声明foo全局之后通过包含iostream来打破它。

答案 1 :(得分:2)

可以使用编译器属性:

  

在标准C ++中,保证在命名空间范围内定义的对象按照严格按照给定转换单元中定义的顺序进行初始化。不保证翻译单元的初始化。但是,GNU C ++允许用户通过指定相对优先级来控制在命名空间范围内定义的对象的初始化顺序,相对优先级是一个当前限制在101和65535之间的常量整数表达式。数字越小表示优先级越高。

Some_Class  A  __attribute__ ((init_priority (2000)));
Some_Class  B  __attribute__ ((init_priority (543)));

我认为101以下是为实现保留的,例如cout

http://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html#C_002b_002b-Attributes

答案 2 :(得分:0)

有一些方法可以确保全局变量在使用之前进行初始化。但是,这些往往是特定于平台的,并且系统之间的细节不同。应在任何用户代码(包括全局变量初始化)运行之前初始化的全局对象的典型示例是全局流对象,特别是std :: cout。我使用了三种方法来确保早期初始化std :: cout:

  1. 标准库已经定义了std::ios_base::Init,它计算了它的构建频率。通过在标头static std::ios_base::Init _Init;中设置<iostream>,当计数器从0更改为1时,可以使用placement new初始化全局流对象,并在计数器从1更改为0时显式销毁在毁灭期间。为了避免双重初始化,std::cout的定义将仅使用例如保留必要的空间。 char std::cout[sizeof(_Standard_output_stream)];。如果用户代码在包含<iostream>
  2. 之前定义了对象,则此方法可能会中断
  3. 在加载共享库时,在共享库中的对象可用之前运行一些初始化代码(在卸载共享库时同样是一些破坏代码)。通过将相应的对象放入共享库,可以在访问对象之前运行初始化代码。主要缺点是这需要使用共享库,这并不总是令人满意的。
  4. 链接器正在有效地构建初始化列表。在某些系统上,可以保证构造各种目标文件的顺序与所看到的目标文件的顺序相反。也就是说,放置例如std::cout到最后一个[C ++]库的最后一个目标文件中导致该对象在其他对象之前被初始化。由于用户可以控制链接线,因此不一定有效。
  5. 此列表中最安全的方法是将对象放入共享库中。最好的方法可能是通过函数访问全局对象来避免这个问题。如果可以在构造之前启动线程,则所有方法都需要一些额外的工作。此外,我确信有特定于平台的方法可以解决这个问题。一般的观察是全球物体是邪恶的:尽量不要使用它们!