C++/Constructors of Global Object

本文详细解析了C++中全局对象的初始化过程及其顺序,包括静态与动态初始化的区别,并探讨了不同源文件间全局对象初始化顺序的不确定性,提供了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Answer
This behaviour is defined in the ISO C++ standard in section "3.6.2 Initialisation of non-local objects" and covers over a page.

1/ Storage for such objects are first zero-initialised.

Zero initialisation and initialisation with a constant expression are called static initialisation. All other types of initialisation are called dynamic initialisation. 

As you ask about when global objects have their constructors called it is therefore dynamic initialisation we are interested in as such constructors would be called for the dynamic initialisation of those global objects that require it.

2/ These points are important: 

Global objects (i.e. non-local objects with static storage duration) defined in namespace scope (i.e. not in a function) in the same translation unit (source file) and dynamically initialised shall be initialised in the order in which their definitions appear in the translation unit.

That is, provided your global objects are in the same source file then they are dynamically initialised in the order they appear in the source file.

If however your global objects are in different translation units (i.e. different source files, as you indicate in your question) then the order of initialisation is _undefined_. That is there is _no_ predicable order of initialisation for this case. This can apply not only between building a project using different tools (compilers, linkers etc), or even building using differing versions of the same tool set, but even between different builds of the same project using exactly the same tools! 

So say you think you have a related set of global objects initialised in a sensible sequence, then maybe you modify some code, re-build your project, and crunch! The order of global object initialisation has changed and one object is now initialised _before_ some other object it relies on. It may even be that just re-building the whole project from scratch could cause a change that breaks the order of initialisation. This I think is due to the possible ways the compiled code (in object code files) could be linked together by a linker. A partial re-build could cause a different global object initialisation sequence to that produced by a full re-build - as maybe a quick rebuild uses incremental linking and a full rebuild requires a full link.

Generally such dynamic initialisation is done before the first statement of main. However a compiler is permitted to defer dynamic initialisation of global objects until after the first statement of main but before first use of any object or function that is defined in the _same_ translation unit as the global object requiring initialisation is defined. Again such compiler-controlled initialisation is restricted to a single translation unit. Again I suspect this is because the language, which is implemented by compilers, has little or no say over the ways that linkers function. Or where it does then it just covers existing linker technology.

So, in short, the constructor of a global object is called either before the first statement of main or may be deferred until after the first statement of main but before the first use of any object or function in the same translation unit as the global object is defined in.

Only global objects in the same translation unit have a predicable sequence of initialisation, which matches the order they are defined in the translation unit.

There is _no_ defined or predicable order of initialisation between global objects defined in different translation units. Code relying on such orderings is _extremely_ fragile and likely to break with no obvious reason. For example: It works for you, you check the code in, and then it fails to work for anyone else...

This problem of undefined initialisation order of global objects is well known and so is a solution - see Scott Meyers' excellent book "Effective C++" - every serious C++ developer should read this book. Of particular interest here is "Item 47 Ensure that non-local static objects are initialised before they're used".

The answer is to replace such objects with functions that return a reference to a local static object, so for example rather than:

In a.cpp:

   SomeClass gSomeObject( gSomeOtherObject );

In b.cpp:

   SomeOtherClass gSomeOtherObject( 1, 2.0, 4 );

Where it is undefined whether gSomeOtherObject defined in b.cpp will be initialised before it is used to construct gSomeObject in a.cpp, use something like:

In a.cpp:

   SomeClass & GSomeObject()
   {
       static SomeClass obj( GSomeOtherObject() );

       return obj;
   }

In b.cpp:

   SomeOtherClass & GSomeOtherObject()
   {
       static SomeOtherClass obj( 1, 2.0, 4 );

       return obj;
   }

Obviously a.cpp and b.cpp would need to include appropriate headers containing the relevant class definitions and function declarations.

The reason this fixes the initialisation order problem is that the point of initialisation for _local_ static objects (those defined as static in functions) _is_ well defined - it occurs when execution first passes through the static object declaration. 

Thus if the static SomeOtherClass obj in GSomeOtherObject() is not initialised when GSomeObject() is first called then the initialisation of obj in GSomeObject() will call GSomeOtherObject() and cause its static obj to be initialised and then a reference to it (now initialised) will be returned for use in the initialisation of SomeClass obj in GSomeObject().

As someone I worked with once pointed out it does not help so much with the associated problem of order of static object destruction. However local static objects are destroyed as a result of returning from main, and done so in the reverse order to which they were initialised so hopefully this will be sufficient in most cases.

For more sophisticated schemes see for example Andrei Alexandrescu's book "Modern C++ Design", specifically chapter 6 "Implementing Singletons".

Finally I should of course note that reliance on (too much) global data (many would say this includes singletons!) is considered bad design/development practice. It is worth considering if other techniques might be applicable, such as the parameterisation from above (PfA) pattern - see  http://accu.org/index.php/journals/1411 http://accu.org/index.php/journals/1420,   http://www.software-architect.co.uk/slides/sa07-KevlinHenney-Selfish_Object.pdf  then maybe read  http://accu.org/index.php/journals/1327  for some balance!


Hope this helps  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值