我想写一些关于初始化的文本,以便以后可以链接到它。
首先是可能性列表。
命名空间静态
- 有两种初始化方法。静态(意在编译时发生)和动态(意在运行时发生)初始化。
- 静态初始化会在任何动态初始化之前发生,无论翻译单元的关系如何。
- 动态初始化在翻译单元中有顺序,而静态初始化没有特定的顺序。同一翻译单元的命名空间范围对象以它们的定义出现顺序进行动态初始化。
- 使用常量表达式初始化的POD类型对象是静态初始化的。它们的值可以被任何对象的动态初始化所依赖,无论翻译单元的关系如何。
- 如果初始化引发异常,则调用
std::terminate
。
例子:
以下程序将打印A(1)A(2)
struct A {
A(int n) { std::printf(" A(%d) ", n); }
};
A a(1);
A b(2);
以下代码基于同一类打印:
A(2)A(1)
。
extern A a;
A b(2);
A a(1);
假设存在一个翻译单元,其中msg
被定义为以下内容
char const *msg = "abc";
接下来会打印
abc
。请注意,
p
接收动态初始化。但是因为静态初始化(
char const*
是 POD 类型,
"abc"
是地址常量表达式)在此之前发生,所以这是可以的,并且保证了
msg
被正确初始化。
extern const char *msg;
struct P { P() { std::printf("%s", msg); } };
P p;
- 对象的动态初始化不必要在main函数之前发生。但是,它必须在其翻译单元中的对象或函数第一次使用之前发生初始化。这对于动态可加载库非常重要。
类静态
- 表现得像命名空间静态变量。
- 是否允许编译器在函数或对象第一次使用后(在main函数之后)初始化类静态变量存在一个错误报告。标准中的措辞目前仅允许对命名空间范围内的对象进行此操作,但似乎也意图允许对类范围内的对象进行此操作。请阅读[命名空间范围内的对象](http://groups.google.com/group/comp.std.c++/browse_thread/thread/28cfef85456512c8)。
- 对于作为模板成员的类静态变量,规则是只有在使用它们时才会初始化。不使用它们将不会导致初始化。请注意,在任何情况下,初始化将按上述说明进行。初始化不会因为它是模板的成员而被延迟。
局部静态
对于本地静态变量,会发生特殊规则。
使用常量表达式初始化的POD类型对象在它们所定义的块被进入之前就已经被初始化。
其他本地静态对象在第一次控制通过它们的定义时被初始化。当抛出异常时,初始化不被认为是完成的。初始化将在下一次尝试。
示例:以下程序打印0 1
:
struct C {
C(int n) {
if(n == 0)
throw n;
this->n = n;
}
int n;
};
int f(int n) {
static C c(n);
return c.n;
}
int main() {
try {
f(0);
} catch(int n) {
std::cout << n << " ";
}
f(1);
std::cout << f(2);
}
在所有上述情况中,对于某些不需要静态初始化的对象,在某些有限的情况下,编译器可以静态初始化它,而不是动态初始化它。这是一个棘手的问题,请参见
this answer以获取更详细的示例。
另请注意,销毁顺序是对象构造完成的确切顺序。这是C++中常见的情况,在各种情况下都会发生,包括在销毁临时对象时。
std::cout
就是一个例子)。 - Johannes Schaub - litbextern A a; B b(a); A a;
** - 这段代码可能会产生令人惊讶的结果。 - Johannes Schaub - litb