防止静态初始化命令“惨败”,C++
Posted
技术标签:
【中文标题】防止静态初始化命令“惨败”,C++【英文标题】:Prevent static initialization order "fiasco", C++ 【发布时间】:2015-07-01 13:32:11 【问题描述】:有一次我阅读了一篇很棒的 C++ FAQ(真的很棒!!)并阅读了 topic 关于如何防止静态初始化命令“惨败”的文章。所以作者建议将静态变量包装成函数,从而通过维护变量的创建顺序来防止“惨败”。但这在我看来是一个粗鲁的解决方法。所以我的问题是,是否有任何现代的、更面向模式的方法来防止这种“惨败”,而是将“静态的东西”包装成函数???
【问题讨论】:
防止失败的优雅方法是永远不要使用依赖于任何东西的静态对象。 FAQ 中提供了关于Construct on first use idiom 的建议。许多 C++ 程序员都熟悉的模式。它易于实现,甚至更易于使用。我不明白您所说的“现代、更面向模式的方式”是什么意思。 另见AddressSanitizerInitializationOrderFiasco。 【参考方案1】:所以我的问题是,是否有任何现代的、更面向模式的方法来防止这种“惨败”,而是将“静态的东西”包装到函数中???
在大多数情况下,您可以在主函数中声明“全局”数据,并在需要时使用依赖注入来传递它。换句话说,根本就没有静态。
在实践中,您可能会遇到需要静态数据的情况。如果对其他静态没有依赖,则将静态数据设为const/constexpr
。
// smart pointer that implements the "Foo" release policy
class FooPointer
static const FooPointer NullFoo; // does not depend on other static values
/* ... */
;
如果静态变量 do 相互依赖,只需将它们包装在静态函数中即可:
// smart pointer that implements the "Foo" release policy
class FooPointer
static const FooPointer& NullFoo(); // depends on other static values
/* ... */
;
总结一下:
大多数(90%?99%?)静态/全局/共享数据应该依赖注入到它使用的地方,而不是创建为静态的。
在极少数情况下,当出于某种原因需要静态且它们不依赖于其他静态时,请声明静态变量。
在非常极少数情况下,当静态需要是静态的并且它们相互依赖时,请在静态方法中交换它们。
根据经验,如果您有很多第二种和第三种情况,那么您在第一种情况下做得还不够。
【讨论】:
【参考方案2】:解决问题的更常用方法是尽可能避免静态 - 在依赖于构造顺序的对象之间更是如此。
然后按要求的顺序构造对象。例如,如果我们有两个对象 x 和 y,如果 x 尚未构造,y 的构造将失败,则先构造 x 并将其提供给 y 的构造函数(或另一个成员))
SomeObject x;
SomeOtherObject y(x);
或
SomeObject *x = new SomeObject;
SomeOtherObject y = new SomeObject(*x);
(以上都假设y
的构造函数需要引用)。
如果您需要在函数之间共享x
和y
,只需将它们作为参数传递给函数即可。
如果您必须使用静态变量(即您不希望在任何地方都键入传递参数),则将静态变量设为指针,并将它们初始化一次(例如,在 main()
中)。
// all source files can use x and y via these declarations (e.g. via a header file)
extern SomeObject *x;
extern SomeOtherObject *y;
// definition in one source file only
SomeObject *x;
SomeOtherObject *y;
int main()
x = new SomeObject;
y = new SomeOtherObject(*x);
// call other functions that use x and y.
delete y;
delete x;
但是,实际上,最好尽可能避免使用静态。
【讨论】:
+1 这比第一次使用成语恕我直言的构造更好。当扩展到更大的项目时,一个可能的改进是使用初始化和访问器函数(隐藏指针并断言初始化已完成)并按库对初始化进行分组。然而,基本的想法是相同的:控制初始化和清理的时间,不要让它随机出现。【参考方案3】:现代的、更面向模式的方式是一开始就不使用全局变量。
没有其他办法。
否则,这不会是一场“惨败”!
【讨论】:
很遗憾,这是不现实的。它对典型的 repo 或 GNU 的 FTP 站点中的数千个软件包没有帮助。 @jww:唯一合理的选择是按照 OP 自己在问题中的建议去做。 这是一个错误的选择。它解决了问题,因此在析构函数中经历了崩溃;而不是构造函数。当一个物体消失得太快时,我亲身经历了 dtor 的崩溃。 @jww 有这个问题的代码存在严重的相互依赖问题,应该重构。以上是关于防止静态初始化命令“惨败”,C++的主要内容,如果未能解决你的问题,请参考以下文章