在C++中,全局和静态对象在进入main()函数之前初始化。然而,标准不保证不同翻译单元中对象的初始化顺序。这可能导致所谓的"静态初始化顺序崩溃"——一个全局对象引用另一个对象,而那个对象还没有初始化。
为了确保正确的初始化并避免问题,使用**"首次使用时构造"**模式:声明一个函数,返回对静态局部对象的引用(该对象在首次调用时以线程安全的方式创建,适用于C++11)。
// foo.h class Config { public: int value; }; Config& getConfig() { static Config config; config.value = 42; // 初始化有保障! return config; }
如果在两个不同的.cpp文件中全局对象互相创建,会发生什么?
答案: 结果是不确定的,可能会出现对未初始化对象的引用。解决方案是用函数内部的静态局部对象替代直接的全局初始化(见上文)。
故事
-在CRM系统中,两个模块声明了全局日志记录器,在初始化时相互引用。在不同的应用构建中,初始化顺序发生变化,出现了难以捕捉的bug:崩溃和记录到空指针。
故事
-在图形引擎中,一个用于资源的全局对象容器引用了资源管理器,而资源管理器本身也是全局的。在Linux和Windows中,编译器以不同的方式实现加载顺序,导致不同步和各种故障。
故事
-开发人员引入了全局配置初始化,后来同事又添加了一个依赖于配置的全局变量。这在本地工作正常,但在CI上失效,因为模块的链接方式不同。