我有一个包含静态成员'obj'的类。静态类成员obj本身包含一个静态成员(恰好是类的互斥类型)。
现在,当我的程序终止时,它会崩溃。当静态对象'obj'被破坏时会发生这种情况。 'obj'的析构函数调用它的静态成员(我自己的互斥体类型遵循RAII惯用法来破坏底层的低级对象)。不幸的是,由于静态对象的初始化顺序( - >相反的析构函数顺序)未定义,因此该成员恰好被销毁了。
如何干净利落地生存?我被高估了这种情况并没有经常发生。看起来一般来说静态非POD成员是非常危险的。特别是如果你不清楚他们的内心结构。
答案 0 :(得分:0)
如果你在函数内部使它们static
,那么你将根据谁先调用函数来命令它们的生命周期。这就是单身人士倾向于实施的方式。
foo& get_foo() {
static foo instance;
return instance;
}
bar& get_bar() {
static bar instance;
return instance;
}
你最好避免使用静力学。
答案 1 :(得分:0)
当试图避免动态分配时,这是一个常见的问题。
我避免这个问题的方法是遵循'静态类'的模式 - 对于没有静态数据的实例,通常静态数据属于'manager'类。然后在显式的“初始化”调用中处理静态数据初始化,在管理器类的显式“关闭”调用中处理销毁,该类只有静态数据和静态成员函数。
你可能不得不使用一个新的或删除并明确你正在做什么 - 在这个意义上你否定了自动机制为你工作的优势,但作为交换,你得到可靠和易于调试的初始化和关闭例程
这种方法基本上创建了一个单例,因此没有管理器类的实例 - 基本上是静态数据的加载和一些带有类语法的C函数提供了封装的好处(例如私有成员不能在类外访问)
在许多情况下,这对编译器也很友好,因为它知道你的所有数据在哪里以及如何处理它
答案 2 :(得分:0)
如果需要静态对象使用的对象,请在 构造函数或析构函数,通常最好使用 单身模式的变化。这解决了顺序 初始化问题,如果你做得对,那么对象就会 永远不会被破坏,所以不应该有破坏的顺序 问题。
因为你的应用程序显然是多线程的(因为你有 一个互斥体),你应该采取通常的预防措施来制作 对象线程安全,通过在输入之前确保初始化 主要。基本想法是这样的:
template <typename T, char const* id>
class StaticInstanceWrapper
{
static T* myObject;
public:
static T& instance();
};
template <typename T, char const* id>
T* StaticInstanceWrapper<T, char const* id>::myObject =
&StaticInstanceWrapper<T>::instance();
template <typename T, char const* id>
T& StaticInstanceWrapper<T>::instance()
{
if ( myObject == NULL ) {
myObject = new T;
}
return *myObject;
}
然后,您将静态对象定义为static
StaticInstanceWrapper<Whatever> obj;
,并将其作为
obj.instance().someFunction()
,而不仅仅是
obj.someFunction()
(不会编译,因为obj
没有
有一个someFunction()
成员。
请注意,StaticInstanceWrapper
的每个实例都有一个
不同的类型,所以有独特的静态成员,你必须
强制单独实例化模板。这就是我们的原因
有id
模板参数;这个论点的类型可以
实际上是任何东西,只要每个实例都有一个
唯一标识符。在实践中,我可能会使用宏
定义,类似于:
#define DEFINE_STATIC_INSTANCE_WRAPPER(type, name) \
char const PASTE(name, _Identifier)[] = STRINGIZE(name); \
StaticInstanceWrapper<type, PASTE(name, _Identifier)> name
这可以确保每个实例都具有唯一的ID。 (如果你想
为了获得更好的体验,你也可以在__LINENO__
中进行训练,但是
既然这个名字在范围上必须是独一无二的,我对此表示怀疑
这是必要的。)