繁体   English   中英

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

[英]Classes with static members containing static members in C++

我有一个包含静态成员'obj'的类。 静态类成员obj本身包含一个静态成员(碰巧是类的互斥体类型)。

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

如何从中干净地生存? 我觉得这没有多发生。 通常,拥有静态的非POD成员看起来非常危险。 特别是如果您不太了解其内部结构。

如果使它们在函数内部是static ,则将根据谁先调用函数来对它们的生存期进行排序。 这就是单例易于实现的方式。

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

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

不过,最好还是避免使用静态变量。

尝试避免动态分配时,这是常见的陷阱。

我避免此问题的方法是遵循“静态类”的模式-对于没有静态数据的实例,通常静态数据属于“ manager”类。 然后,通过显式的“ initialise”调用处理静态数据初始化,并在仅具有静态数据和静态成员函数的manager类上,通过显式的“ shutdown”调用处理破坏操作。

您可能必须使用new或delete并明确说明正在执行的操作-在这种情况下,您会否定自动机制为您工作的优势,但是作为交换,您将获得可靠且易于调试的初始化和关闭例程。

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

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

如果需要在构造函数或析构函数中由静态对象使用的对象,通常最好在单例模式上使用变体。 这解决了初始化问题的顺序,如果正确执行,对象将永远不会被破坏,因此也不应该存在破坏顺序的问题。

由于您的应用程序显然是多线程的(因为您有互斥锁),因此应采取通常的预防措施,通过确保在输入main之前进行初始化来使对象线程安全。 基本想法是这样的:

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实例StaticInstanceWrapper ,其类型都是不同的,并且具有唯一的静态成员,则必须强制对模板进行单独的实例化。 这就是为什么我们使用id模板参数的原因; 只要每个实例都具有唯一的标识符,则该参数的类型实际上可以是任意值。 在实践中,我可能会使用宏进行定义,类似于以下内容:

#define DEFINE_STATIC_INSTANCE_WRAPPER(type, name) \
char const PASTE(name, _Identifier)[] = STRINGIZE(name); \
StaticInstanceWrapper<type, PASTE(name, _Identifier)> name

这样可以确保每个实例都有唯一的ID。 (如果您想变得更高级,也可以在__LINENO__中进行__LINENO__ ,但是由于该名称在范围上必须是唯一的,因此我怀疑这是必要的。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM