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

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的顺序进行编译了,这会儿就不用担心不能保证初始化的顺序了。
### C++ 静态全局变量初始化C++中,静态全局变量可以在定义时进行初始化。这类变量具有文件作用域或命名空间作用域,在程序启动前由编译器自动完成初始化。 对于基本类型的静态全局变量,可以直接赋初值: ```cpp // 定义并初始化一个整型静态全局变量 int g_nVal = 10; ``` 如果未显式指定初始值,则会根据类型默认初始化为零或其他适当值[^2]。 对于复杂对象类型的静态全局变量,可以通过调用构造函数来初始化: ```cpp class MyClass { public: MyClass(int val): value(val) {} private: int value; }; MyClass myObject(42); // 使用参数化构造函数初始化 ``` 需要注意的是,多个翻译单元中的静态变量初始化顺序是不确定的,这可能导致潜在的风险。为了避免此类问题,可以采用局部`static`变量的方式延迟初始化,这种方式不仅安全而且支持多线程环境下的正确初始化[^3]。 #### 多线程环境下静态全局变量的安全初始化 为了确保在多线程环境中静态全局变量能够被安全地初始化,现代C++标准提供了内置的支持机制。例如,下面的例子展示了如何利用局部静态变量特性实现线程安全的单例模式实例创建: ```cpp Singleton& getSingletonInstance() { static Singleton instance; // 自动处理一次性线程安全性 return instance; } ``` 此方法通过编译器保障了即使是在并发场景下也能恰当地执行初始化操作,并且只会在第一次访问该函数时发生。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值