2

我知道C++ 中 的静态初始化顺序失败以及第一次使用惯用语的构造来避免它。因此,在下面的代码中,全局赋值a可能发生在 of 之前,foo::a因此值a未定义。另一方面,全局赋值b是可以的,因为它调用了函数foo::b()

#include <iostream>
#include <string>

using namespace std;

// foo.hpp

class foo {
public:
  static const string a;
  static const string& b();
  static const char* const c;
  static const char* const d[2];
  static const int e;
  static const int f[2];
};

// foo.cpp

const string foo::a("astr");
const string& foo::b() {
  static const string t("bstr");
  return t;
}
const char* const foo::c = "cstr";
const char* const foo::d[2] = {"dstr1", "dstr2"};
const int foo::e = 5;
const int foo::f[2] = {6, 7};

// main.cpp

// global initializations
string a = foo::a;              // dangerous, might be "" or "astr"
string b = foo::b();            // safe, guaranteed to be "bstr"
const char* c = foo::c;         // what about these...?
const char* d = foo::d[0];
int e = foo::e;
int f = foo::f[0];

int main() {
  cout << a << " " << b << "\n"
       << c << " " << d << "\n"
       << e << " " << f << "\n";
}

(想象一下,我在这里组合了foo.hppfoo.cppmain.cpp。)但是作为内置类型或它们的数组的变量呢?因此,此代码中的cde和的全局分配是否安全?f链接器似乎可以为这些变量设置内存,因此在运行时不需要进行初始化。但是我可以依靠这个吗?

我知道我不应该使用全局变量。但是,我是一个库(foo.cpp 和 foo.hpp)的作者,我无法控制我的库的用户(main.cpp 的作者)做什么。

4

1 回答 1

3

这里的关键是“static初始化”(使用标准语言正式称为具有静态存储持续时间的对象的动态初始化,具有排序失败)和静态初始化之间的区别。

该标准说(部分[basic.start.static]

对象的常量初始化器o是一个常量表达式,除了它还可以调用constexpr构造函数o及其子对象,即使这些对象是非文字类类型。[注意:这样的类可能有一个重要的析构函数——结束注]

执行常量初始化:

  • 如果出现在具有静态或线程存储持续时间的引用的初始化程序中的每个完整表达式(包括隐式转换)都是常量表达式,并且该引用绑定到指定具有静态存储持续时间的对象的左值,则绑定到临时对象或子对象其中,或功能;
  • 如果具有静态或线程存储持续时间的对象由构造函数调用初始化,并且初始化完整表达式是该对象的常量初始化器;
  • 如果具有静态或线程存储持续时间的对象未由构造函数调用初始化,并且该对象是值初始化的,或者出现在其初始化程序中的每个完整表达式都是常量表达式。

如果不执行常量初始化,则将具有静态存储持续时间或线程存储持续时间的变量初始化为零。零初始化和常量初始化合称为静态初始化;所有其他初始化都是动态初始化。静态初始化应在任何动态初始化发生之前执行。

您的c, d,ef对象具有常量初始化器,因此它们的初始化在静态初始化阶段完成(即使cd本身不是常量),并且它们的值在所有动态初始化期间都可用,即使是之前的词法初始化。

于 2017-05-28T22:12:29.493 回答