具有静态成员的类包含C++中的静态成员



我有一个包含静态成员"obj"的类。静态类成员obj本身包含一个静态成员(恰好是类的互斥类型)。

现在,当我的程序终止时,它就会崩溃。当静态对象"obj"被破坏时,就会发生这种情况。"obj"的析构函数调用其静态成员(我自己的互斥对象类型遵循RAII习惯用法来析构函数底层低级别对象)。不幸的是,由于静态对象的初始化顺序(->相反的析构函数顺序)未定义,该成员碰巧已经被销毁。

如何干净地从中生存?我感到奇怪的是,这种情况并没有发生得更频繁。一般来说,拥有静态的非POD成员似乎是非常危险的。特别是如果你不了解它们的内部结构。

如果在函数中使它们成为static,那么它们的生存期将根据谁首先调用函数来排序。这就是单例的实现方式。

foo& get_foo() {
static foo instance;
return instance;
}
bar& get_bar() {
static bar instance;
return instance;
}

不过,你最好还是避免静电。

当试图避免动态分配时,这是一个常见的问题。

我避免这个问题的方法是遵循"静态类"的模式——对于没有静态数据的实例,静态数据通常属于"管理器"类。然后在显式"initialize"调用中处理静态数据初始化,在只具有静态数据和静态成员函数的管理器类上的显式"shutdown"调用中进行销毁。

你可能必须使用new或delete,并明确你正在做什么——从这个意义上说,你否定了自动机制为你工作的优势,但作为交换,你可以获得可靠且易于调试的初始化和关闭例程。

这种方法本质上创建了一个单例,因此没有管理器类的实例-基本上是静态数据和一些C函数的负载,类语法提供了封装的好处(例如,在类之外无法访问私有成员)

在许多情况下,这对编译器来说也是友好的,因为它知道所有数据在哪里,以及如何处理

如果您需要一个由静态对象使用的对象,请在构造函数或析构函数,通常最好使用singleton模式的变体。这解决了初始化问题,如果你做对了,对象会永远不要被破坏,所以不应该有破坏的顺序问题。

由于您的应用程序显然是多线程的(因为您互斥对象),您应该采取通常的预防措施对象线程安全,通过确保在进入之前进行初始化主要的基本思想是这样的:

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__中乱搞,但是由于名称的作用域必须是唯一的,我对此表示怀疑这是必要的。)

最新更新