非静态全局对象与动态对象的全局指针

时间:2012-04-30 06:42:04

标签: c++ global-variables object-initializers non-static

使用以下内容获得无法解释的行为:

案例1:

a.cpp编译为.dll库并在main.cpp的main()中使用

Bar b; 

//constr
Bar::Bar(){
  //... initialize members
}

//private library init
Bar::init(){ ...}

//public API init
bool lib_init(){
  b.init();
}

据我所知,由于全局变量的未定义初始化行为,这种方法可能会失败。

案例2:

a.cpp编译为.dll库并在main.cpp的main()中使用

Bar* b; 

//constr
Bar::Bar(){
  //... initialize members
}

//private library init
Bar::init(){ ...}

//public API init
bool lib_init(){
  b = new Bar;
  b->init();
}

这次使用动态分配时可行。

案例3(最令人惊讶的)

a.cpp编译为.dll库并在main.cpp的main()中使用

static Bar& getBarObj()
{
  static Bar g_objBar;
  return g_objBar;
}

//constr
Bar::Bar(){
  //... initialize members
}

//private library init
Bar::init(){ ...}

//public API init
bool lib_init(){
  getBarObj().init();
}

与案例1相反,Bar obj实例化可能未定义,在案例3中,它是“根据请求”使用的。然而,案例3提供了与案例1相同的行为。

我的问题是......有谁能解释这里发生了什么? 所有内容都是使用VC2008 Release模式构建的(此项目的调试模式没有选项)

2 个答案:

答案 0 :(得分:0)

在C ++中未定义来自不同文件的初始化的初始化顺序(你肯定有的情况)。这意味着如果依赖于在不同编译单元中的全局初始化期间构造的Bar b,则程序未定义。

为什么Case 3应该起作用,是因为它迫使你使用一个函数来引用Bar b,这个函数保证在这个函数返回的时候构造Bar b。 / p>

如果您告诉我们您正在获得的确切未定义行为,并提供最少量的代码进行分析,我们可能会提供进一步的帮助。

P.S。您的Bar构造函数是否依赖于另一个已构造的全局?

答案 1 :(得分:0)

案例1可能会失败,因为b的构造基本上留给链接顺序(不受C ++规范控制:它是C ++的未定义行为,但行为可以由链接器定义! ) 在任何情况下,一旦您致电lib_initb被授予构建为lib_initb留在同一模块中。

案例3在需要时构造b,并将在构造逆序中终止时与所有其他静态和全局对象一起销毁它。 如果Bar::Bar()依次调用foo_init()内置静态Foo,则可能会对旧编译器造成一些问题:您需要BarBar需要{{1但Foo之前创建BarFoo之前FooBar之前仍然需要它。 自2003年规格以来,这个事实应该再发生了。施工完成后需要进行破坏调度(Foo ctor在Bar ctor之前完成,因此在Foo dtor之前终止将调用bar .dtor。

案例2是“丑陋的”:像案例3一样工作(根据需要创建)但有两个问题:

  • 指针应该是静态的,只是为了避免在多次调用lib_init()和...时创建多个对象。
  • 谁将摧毁Bar? Os会在终止时重新锁定内存,但不会调用dtor。

案例2可能更好地定义为

void lib_init()
{
    static std::unique_ptr<Bar> p(new Bar);
    p->init();
}

但这会使它像案例3一样。