Matthew Wilson 著
树人译
Chapter 11静态变量
Chapter 11静态变量
静态对象不同于栈变量和堆变量,因为它们的内存是由链接器固定并分配的,而且它们的生存期(很大程度上)与进程的执行流是不相关的。静态对象分为三种类别:
1. 定义在函数作用域的局部静态变量。
2. 定义在全局名称空间或具名和匿名名称空间中的全局和名称空间级的静态变量,也就是通常所说的非局部静态变量。
3. 定义在类类型中的静态成员变量,它可以在同一类类型的多个类实例之间共享该变量。
本章着眼于静态对象的使用中所固有的问题,而且提出了一些改进它们的方法。
静态对象的初始化是一个二阶操作,这点是很重要的。首先,“实现”对它们所拥有的内存进行清零初始化。在标准(C++-98:3.6.3;1)中是这样阐述的:“所有具有静态存储生存期的局部变量会在其它任何初始化操作发生之前进行清零初始化操作。”事实上,由于这些变量被放置在链接单元(可执行程序或动态链接库)的.data(或.bss)段中,上述的过程通常是由操作系统进程装载机制来完成的,也就是简单地把数据复制到一个读写内存段中。在Listing11.1中,变量i1和i2都被初始化为零。
Listing 11.1
extern int SomeOtherModulesFunc();
namespace StaticsAbound
{
inline int LocalFunc()
{
return 10;
}
struct Thing
{
Thing()
: i(SomeOtherModulesFunc())
{}
int i;
} thing;
int i1 = 0;
int i2;
int i3 = 1;
int i4 = LocalFunc();
int i5 = ::SomeOtherModulesFunc();
}
如果静态对象是POD类型而且是从一个常量表达式来进行初始化的,那么编译器可能会把它们的值写入可执行程序中,而且因此而不需要任何运行时的初始化。也就是说,在编译/链接阶段i3的值可能被赋为1。此外,在能够确定动态初始化将导致一个相同的值的地方,一个作相关的优化并应用静态初始化的实现(编译器)是合法的(C++-98:3.6.2;2)。因此:编译器把i4静态初始化为10将是合法的。
清零和常量初始化合在一起就是通常所说的静态初始化:这只是第一个阶段。所有其它的初始化被认为是动态初始化。任何在编译时不能完全确定的东西都要求动态初始化,包括POD类型到动态值的初始化,以及非局部静态类类型对象的构造:Listing11.1中的i5和thing。
所有的静态初始化都是在任何的动态初始化之间进行的。而且,通常所有非局部静态对象的动态初始化动作会在main()函数执行之前进行,但标准并没有要求。如上所述(C++-98:3.6.2;3),非局部静态对象必须在“任何定义在同一翻译单元中的函数或对象的使用”之前进行初始化。实际上,上述的滞后初始化很少见,而且我所熟悉的编译器会在main()的入口之前进行初始化。[1](C++-98:3.6.1;1)是这样表述的:在一个独立的环境中,“启动阶段(start-up)包含了名称空间作用域中的具有静态存储生存期的对象的构造函数的执行,”这让人相信标准本身的期望是在main()之前完成非局部静态对象的动态初始化。
[1]大概这是为了迎合嵌入式或其它一些非一般的环境而定义的一个实现。