在静态初始化期间创建和使用向量是否安全?

时间:2014-06-16 23:46:44

标签: c++ stdvector static-initialization

我有C ++代码,它声明了由函数调用初始化的静态生命周期变量。被调用的函数构造一个vector实例并调用其push_back方法。代码是否会冒险通过C ++静态初始化命令惨败?如果没有,为什么不呢?

补充资料:

  1. 什么是“静态初始化命令惨败”?

    C++ FAQ 10.14

  2. 中对此进行了解释
  3. 为什么我认为使用vector会触发惨败?

    vector构造函数可能会使用动态初始化的另一个静态生存变量的值。如果是这样,那么在我的代码中使用vector之前,没有什么可以确保vector的变量被初始化。初始化result(请参阅下面的代码)可能会在vector的依赖项完全初始化之前调用vector构造函数,从而导致访问未初始化的内存。

  4. 这段代码到底是什么样的?

    struct QueryEngine {
      QueryEngine(const char *query, string *result_ptr)
        : query(query), result_ptr(result_ptr) { }
    
      static void AddQuery(const char *query, string *result_ptr) {
        if (pending == NULL)
          pending = new vector<QueryEngine>;
        pending->push_back(QueryEngine(query, result_ptr));
      }
    
      const char *query;
      string *result_ptr;
    
      static vector<QueryEngine> *pending;
    };
    
    vector<QueryEngine> *QueryEngine::pending = NULL;
    
    void Register(const char *query, string *result_ptr) {
      QueryEngine::AddQuery(query, result_ptr);
    }
    
    string result = Register("query", &result);
    

2 个答案:

答案 0 :(得分:4)

幸运的是,即使在执行任何其他初始化之前(甚至在相同对象的“真实”初始化之前),static个对象也是零初始化的,因此您知道{NULL对象1}}将在首次调用Register之前很久就设置在该指针上。 1

现在,就你的矢量操作而言,(技术上)你可能会遇到这样的问题:

  

[C++11: 17.6.5.9/3]: C ++标准库函数不应直接或间接修改除当前线程以外的线程可访问的对象(1.10),除非通过函数的非const参数直接或间接访问对象,包括{{ 1}}。

     

this [注意:这意味着,例如,实现不能在没有同步的情况下将静态对象用于内部目的,因为它甚至可能导致数据竞争不明确地在线程之间共享对象。 -end note]

请注意,虽然本说明中要求同步,但在段落中已提及最终确认允许[C++11: 17.6.5.9/4]:实施细节的段落。

话虽如此,似乎标准应进一步说明用户代码应避免在静态初始化期间对标准容器进行操作,如果意图是无法保证此类代码的语义;无论哪种方式,我都认为这是标准中的缺陷。它应该更清楚。

1 And it is a NULL pointer, whatever the bit-wise representation of that may be, rather than a blot to all-zero-bits.

答案 1 :(得分:2)

vector并不依赖于阻止其在静态动态初始化中使用的任何内容。你的代码唯一的问题是缺乏线程安全性 - 没有特别的理由认为你应该关心它,除非你有静态的构造产生线程....

  

初始化result(参见下面的代码)可能会在该类完全初始化之前调用向量构造函数,从而导致访问未初始化的内存。

否......初始化调用result的{​​{1}}次调用AddQuery - 在动态初始化之前,对if (pending == NULL)的初始化肯定会完成,每3.6.2 / 2:

  

执行常量初始化:

     

...

     

- 如果 对象具有静态 或线程 存储持续时间 未通过构造函数调用进行初始化,如果对象是值初始化的,或 其初始化程序中出现的每个完整表达式都是常量表达式 < / p>

因此,即使NULL作业位于不同的翻译单元,也是安全的。见3.6.2 / 2:

  

一起,零初始化和常量初始化称为静态初始化;所有其他初始化都是动态初始化。在进行任何动态初始化之前,应执行静态初始化。