有一次我读了一篇很棒的 C++ 常见问题解答(真的很棒!!)并阅读了关于如何防止静态初始化顺序“惨败”的主题。所以作者建议将静态变量封装到函数中,通过维护变量的创建顺序来防止“惨败”。但这在我看来是一个粗鲁的解决方法。所以我的问题是,是否有任何现代的、更面向模式的方法来防止这种“惨败”,而是将“静态的东西”包装到函数中???
现代的、更面向模式的方法是首先不使用全局变量。
没有其他办法。
否则就不算“惨败”了!
所以我的问题是,是否有任何现代的、更面向模式的方法来防止这种“惨败”,而是将“静态的东西”包装到函数中???
在大多数情况下,您可以在主函数中声明您的“全局”数据,并在需要的地方使用依赖注入来传递它。换句话说,根本没有静态。
在实践中,您可能会遇到需要静态数据的情况。如果对其他静态数据没有依赖性,则将静态数据设为
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%?)静态/全局/共享数据应该依赖注入到使用它的地方,而不是创建为静态数据。
在极少数情况下,由于某种原因需要静态变量并且它们不依赖于其他静态变量,请声明静态变量。
在 非常 需要静态并且它们相互依赖的极少数情况下,将它们替换为静态方法。
根据经验,如果你有很多第二种和第三种情况,那么你在第一种情况下做得还不够。
解决该问题的更常用方法是尽可能避免静态 - 尤其是在依赖构造顺序的对象之间。
然后按要求的顺序构造对象。例如,如果我们有两个对象 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;
}
但是,实际上,如果可能的话,最好避免使用静力学。
不是一个确切的答案,但我从 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;
}