C++全局和静态变量初始化顺序的研究

时间:2021-03-30 19:42:40
C++全局和静态变量初始化顺序的研究
我在编程的时候遇到了一个非常棘手的问题,就是静态变量初始化的问题。有的情况一个全局(静态)变量依赖另外一个全局(静态)的变量。比如在工厂模式中使用隐式注册注册一个创造器(Creator),但是它要依赖工厂的初始化,如果工厂都没有初始化,那么注册会失败。程序会抛出一个未知的异常。这些是在C++进入main函数之前就已经完成的。在进入main函数之前,crt0dat.c会为这些全局或静态的变量赋初值。问题是一般的情况它们的初始化顺序到底如何?当初我也是一头雾,但是在自己上网提问和多次调试后,我终于知道了其中的奥秘。


我使用的环境是VS2005,也就是说使用的是VC++8的编译器。在编译的时候我们不是看到输出栏上显示的是编译的源文件吗?编译器在编译源文件的时候会记录那些处于全局范围的全局变量、全局静态变量以及类静态变量。按照编译的顺序形成一个表,然后运行时在main()函数开始之前就初始化,所以有时候我们看到有些程序打开的时候半晌才弹出一个窗口,比如说360,它在初始化之前做了很多的安全检测。


这里就讲得更细了。比如我有一个Factory<CEntity, string> g_Factory全局对象以及BlockCreator g_BlockCreator对象,由于全局变量不能在头文件中定义(如果包含了多个源文件的话会报LNK2005错误),我们将头文件中的Factory加上extern关键字,让它的定义推迟到源文件中。接下来如何保证g_BlockCreator在g_Factory后初始化了。
这里假设将g_Factory的定义放入Factory.cpp中,g_BlockCreator的定义放在Block.cpp中,按照VS2005字母表的编译顺序,Block.cpp将被先编译,Factory.cpp在之后编译,这样g_BlockCreator将先于g_Factory初始化,这是不允许的。所以我们将Factory.cpp改为$Factory.cpp(或者ASCII顺序排在B之前的字母都行),然后关闭VS2005,再打开进行编译,就能达到效果。记住改了文件名后一定要关闭VS2005,否则它还是会按照B-F的编译顺序进行编译的,尽管F开头的源文件已经不存在。重新打开后它就会按照$-B的顺序进行编译了,这会儿就不用担心不能保证初始化的顺序了。