防止静态初始化顺序“惨败”,C++

问题描述 投票:0回答:4

有一次我读了一篇很棒的 C++ 常见问题解答(真的很棒!!)并阅读了关于如何防止静态初始化顺序“惨败”的主题。所以作者建议将静态变量封装到函数中,通过维护变量的创建顺序来防止“惨败”。但这在我看来是一个粗鲁的解决方法。所以我的问题是,是否有任何现代的、更面向模式的方法来防止这种“惨败”,而是将“静态的东西”包装到函数中???

c++ static word-wrap static-initialization static-order-fiasco
4个回答
20
投票

现代的、更面向模式的方法是首先不使用全局变量

没有其他办法。

否则就不算“惨败”了!


5
投票

所以我的问题是,是否有任何现代的、更面向模式的方法来防止这种“惨败”,而是将“静态的东西”包装到函数中???

在大多数情况下,您可以在主函数中声明您的“全局”数据,并在需要的地方使用依赖注入来传递它。换句话说,根本没有静态。

在实践中,您可能会遇到需要静态数据的情况。如果对其他静态数据没有依赖性,则将静态数据设为

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;
}

但是,实际上,如果可能的话,最好避免使用静力学。


0
投票

不是一个确切的答案,但我从 cppstories 引用了有用的技巧,它使用来自 C++20

constinit

功能

constinit 强制静态或线程本地的常量初始化 变量。它可以帮助限制静态订单初始化失败 使用预编译值和明确定义的顺序而不是动态的 初始化和链接顺序……

#include <array>

// init at compile time
constexpr int compute(int v) { return v*v*v; }
constinit int global = compute(10);

// won't work:
// constinit int another = global;

int main() {
    // but allow to change later...
    global = 100;

    // global is not constant!
    // std::array<int, global> arr;
}
© www.soinside.com 2019 - 2024. All rights reserved.