首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >漂亮的/施瓦兹计数器,符合标准吗?

漂亮的/施瓦兹计数器,符合标准吗?
EN

Stack Overflow用户
提问于 2011-04-11 14:14:12
回答 2查看 2.5K关注 0票数 17

今天上午,我与一位同事讨论了静态变量初始化顺序。他提到了漂亮/施瓦兹计数器,我有点困惑。我理解它的工作原理,但我不确定这在技术上是否符合标准。

假设以下3个文件(前两个文件是从更多C++成语复制过来的):

代码语言:javascript
复制
//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.
代码语言:javascript
复制
//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.
  }
}
代码语言:javascript
复制
// Program.cpp
#include "Stream.hpp" // initializer increments "nifty_counter" from 0 to 1.

// Rest of code...
int main ( int, char ** ) { ... }

..。问题就在这里!有两个静态变量:

  1. "nifty_counter“(Stream.cpp);以及
  2. Program.cpp中的“初始化器”。

由于这两个变量恰好位于两个不同的编译单元中,因此在调用nifty_counter的构造函数之前,没有官方保证将initializer初始化为0。

我可以认为两个快速解决方案是为什么这个“有效”的两个原因:

  1. 现代编译器足够聪明地解决这两个变量之间的依赖,并将代码按适当的顺序放置在可执行文件中(极不可能);
  2. 正如本文所述,nifty_counter实际上是在“加载时”初始化的,并且它的值已经放在可执行文件的“数据段”中,所以它总是在“运行任何代码之前”初始化(很有可能)。

在我看来,这两者似乎都依赖于一些非官方的,但可能的实现。这个标准是符合标准的,还是只是“很有可能工作”,所以我们不应该担心它?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2011-04-11 14:33:10

我相信它一定会成功的。根据标准($3.6.2/1):“具有静态存储时间(3.7.1)的对象在进行任何其他初始化之前必须为零初始化(8.5)。”

由于nifty_counter具有静态存储持续时间,因此在创建initializer之前会初始化它,而不管跨翻译单元的分布情况如何。

编辑:在重新阅读了该部分之后,并考虑到@Tadeusz Kopec的评论中的输入,我不太确定它现在是否定义得很好,但要确保它是定义良好的是非常简单的:从nifty_counter的定义中删除初始化,所以看起来如下:

代码语言:javascript
复制
static int nifty_counter;

因为它具有静态存储持续时间,所以它将是零初始化的,即使没有指定一个初始化器--并且移除初始化器可以消除在零初始化之后发生的任何其他初始化的疑问。

票数 22
EN

Stack Overflow用户

发布于 2013-08-21 22:04:06

我认为在这个例子中缺少的是如何避免流的构造,这通常是不可移植的。除了漂亮的计数器之外,初始化程序的作用是构造如下内容:

代码语言:javascript
复制
extern Stream in;

其中一个编译单元具有与该对象相关联的内存,无论是在使用就地新操作符之前有一些特殊的构造函数,还是在我已经看到的情况下,内存是以另一种方式分配的,以避免任何冲突。在我看来,这个流上有一个no-op构造函数,那么是先调用初始值还是没有定义no-op构造函数的顺序。

要分配字节区域,通常是不可移植的,例如对于gnu iostream,cin的空间定义为:

代码语言:javascript
复制
typedef char fake_istream[sizeof(istream)] __attribute__ ((aligned(__alignof__(istream))))
...
fake_istream cin;

llvm使用:

代码语言:javascript
复制
_ALIGNAS_TYPE (__stdinbuf<char> ) static char __cin [sizeof(__stdinbuf <char>)];

两人都对物体所需的空间作了一定的假设。其中Schwarz计数器以新的放置方式初始化:

代码语言:javascript
复制
new (&cin) istream(&buf)

实际上这看上去没那么便携。

我注意到一些编译器,如gnu、microsoft和AIX,确实有编译器扩展来影响静态初始化程序顺序:

  • 对于Gnu,这是:使用init-priority标志启用-f并使用__attribute__ ((init_priority (n)))
  • 在带有microsoft编译器的windows上,有一个#杂注(http://support.microsoft.com/kb/104248)
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5622574

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档