出现以下无法解释的行为:
案例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();
}
据我了解,这种方法可能会由于全局变量的未定义初始化行为而失败。
案例2:
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();
}
与情况 1 相反,在情况 1 中,Bar obj 实例化可能未定义,而在情况 3 中,它是"根据请求"使用的。然而,案例 3 提供与案例 1 相同的行为。
我的问题是...谁能解释一下这里发生了什么?一切都是使用 VC2008 发布模式构建的(此项目没有调试模式选项(
来自不同文件的全局变量的初始化顺序(您肯定有这种情况(在 C++ 中未定义。这意味着,如果您依赖于在不同编译单元中的全局初始化期间构造Bar b
,则您的程序是未定义的。
为什么情况 3 应该有效,是因为它强制您使用函数来引用Bar b
并且此函数保证在此函数返回时Bar b
已经构造。
如果您告诉我们您得到的确切未定义行为,并提供最少数量的代码进行分析,我们也许能够进一步提供帮助。
附言您的Bar
构造函数是否依赖于已构造的另一个全局?
情况 1 可能会失败,因为 b
的构造基本上留给链接顺序(不受C++规范控制:它是C++的未定义行为,但行为可以由链接器定义!无论如何,一旦你调用lib_init
b
就被授予被构造lib_init
和b
离开在同一个模块中。
案例 3 在需要时构造 b,并将在终止时以与所有其他静态和全局对象相反的构造顺序销毁它。如果Bar::Bar()
依次调用内部具有静态Foo
的foo_init()
,这可能会给旧编译器带来一些问题:您需要Bar
,Bar
需要Foo
但Bar
是在Foo
之前创建的,Foo
Bar
该垫子仍然需要它之前销毁。自 2003 年规范要求销毁计划在施工完成后进行(因此Foo
ctor 在 ctor 之前完成Bar
因此 bar dtor 将在 Foo
dtor 之前终止时调用。
案例 2 是"丑陋的":像案例 3(按需创建(一样处理 cotruction,但有两个问题:
- 指针应该是静态的,只是为了避免在多次调用 lib_init(( 和 ...
- 谁会摧毁巴尔?操作系统将在终止时将内存重新压缩,但不会调用 dtor。
案例 2 可能应该更好地定义为
void lib_init()
{
static std::unique_ptr<Bar> p(new Bar);
p->init();
}
但这会使它像案例 3。