1

出现以下无法解释的行为:

情况1:

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

Bar b; 

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

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

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

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

案例二:

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

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() 中使用main.cpp

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();
}

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

我的问题是……谁能解释这里发生了什么?一切都是用 VC2008 发布模式构建的(这个项目没有调试模式选项)

4

2 回答 2

0

情况 1 可能会失败,因为 的构造b本质上是由链接顺序决定的(这不受 C++ 规范的控制:它是 C++ 的未定义行为,但行为可以由链接器定义!)无论如何,一旦你调用lib_init b被授予在同一个模块中构建lib_init和离开。b

情况 3 在需要时构造 b,并将在终止时以构造相反的顺序将其与所有其他静态和全局对象一起销毁。这可能会给旧的编译器带来一些问题,如果Bar::Bar()调用内部foo_init()有一个静态Foo:你需要Bar并且Bar需要Foo,但是在mat 仍然需要它之前Bar创建FooFoo销毁。Bar自 2003 年规范以来,这一事实应该不再发生。需要在构造完成后进行销毁调度(因此Fooctor 在Barctor 之前完成,因此 bar dtor 将在Foodtor 之前终止时被调用。

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

  • 指针应该是静态的,只是为了避免在多次调用 lib_init() 和...的情况下创建多个对象
  • 谁会摧毁巴尔?Os 将在终止时收回内存,但不会调用 dtor。

案例 2 可能更好地定义为

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

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

于 2012-04-30T07:24:29.340 回答
0

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

为什么案例 3 应该起作用,是因为它强制您使用一个函数来引用,Bar b并且该函数保证Bar b在该函数返回时已经构造了该函数。

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

PS您的Bar构造函数是否依赖于已构造的另一个全局变量?

于 2012-04-30T06:56:46.177 回答