今天上午,我与一位同事讨论了静态变量初始化顺序。他提到了漂亮/施瓦兹计数器,我有点困惑。我理解它的工作原理,但我不确定这在技术上是否符合标准。
假设以下3个文件(前两个文件是从更多C++成语复制过来的):
//Stream.hpp
class StreamInitializer;
class Stream {
friend class StreamInitializer;
public:
Stream () {
// Constructor must be called before use.
}
};
static class StreamInitializer {
public:
StreamInitializer ();
~StreamInitializer ();
} initializer; //Note object here in the header.//Stream.cpp
static int nifty_counter = 0;
// The counter is initialized at load-time i.e.,
// before any of the static objects are initialized.
StreamInitializer::StreamInitializer ()
{
if (0 == nifty_counter++)
{
// Initialize Stream object's static members.
}
}
StreamInitializer::~StreamInitializer ()
{
if (0 == --nifty_counter)
{
// Clean-up.
}
}// Program.cpp
#include "Stream.hpp" // initializer increments "nifty_counter" from 0 to 1.
// Rest of code...
int main ( int, char ** ) { ... }..。问题就在这里!有两个静态变量:
Stream.cpp);以及Program.cpp中的“初始化器”。由于这两个变量恰好位于两个不同的编译单元中,因此在调用nifty_counter的构造函数之前,没有官方保证将initializer初始化为0。
我可以认为两个快速解决方案是为什么这个“有效”的两个原因:
nifty_counter实际上是在“加载时”初始化的,并且它的值已经放在可执行文件的“数据段”中,所以它总是在“运行任何代码之前”初始化(很有可能)。在我看来,这两者似乎都依赖于一些非官方的,但可能的实现。这个标准是符合标准的,还是只是“很有可能工作”,所以我们不应该担心它?
发布于 2011-04-11 14:33:10
我相信它一定会成功的。根据标准($3.6.2/1):“具有静态存储时间(3.7.1)的对象在进行任何其他初始化之前必须为零初始化(8.5)。”
由于nifty_counter具有静态存储持续时间,因此在创建initializer之前会初始化它,而不管跨翻译单元的分布情况如何。
编辑:在重新阅读了该部分之后,并考虑到@Tadeusz Kopec的评论中的输入,我不太确定它现在是否定义得很好,但要确保它是定义良好的是非常简单的:从nifty_counter的定义中删除初始化,所以看起来如下:
static int nifty_counter;因为它具有静态存储持续时间,所以它将是零初始化的,即使没有指定一个初始化器--并且移除初始化器可以消除在零初始化之后发生的任何其他初始化的疑问。
发布于 2013-08-21 22:04:06
我认为在这个例子中缺少的是如何避免流的构造,这通常是不可移植的。除了漂亮的计数器之外,初始化程序的作用是构造如下内容:
extern Stream in;其中一个编译单元具有与该对象相关联的内存,无论是在使用就地新操作符之前有一些特殊的构造函数,还是在我已经看到的情况下,内存是以另一种方式分配的,以避免任何冲突。在我看来,这个流上有一个no-op构造函数,那么是先调用初始值还是没有定义no-op构造函数的顺序。
要分配字节区域,通常是不可移植的,例如对于gnu iostream,cin的空间定义为:
typedef char fake_istream[sizeof(istream)] __attribute__ ((aligned(__alignof__(istream))))
...
fake_istream cin;llvm使用:
_ALIGNAS_TYPE (__stdinbuf<char> ) static char __cin [sizeof(__stdinbuf <char>)];两人都对物体所需的空间作了一定的假设。其中Schwarz计数器以新的放置方式初始化:
new (&cin) istream(&buf)实际上这看上去没那么便携。
我注意到一些编译器,如gnu、microsoft和AIX,确实有编译器扩展来影响静态初始化程序顺序:
init-priority标志启用-f并使用__attribute__ ((init_priority (n)))。https://stackoverflow.com/questions/5622574
复制相似问题