简体   繁体   English

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

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

I have a class which contains a static member 'obj'. 我有一个包含静态成员'obj'的类。 The static class member obj itself contains a static member (which happens to be a mutex type of class). 静态类成员obj本身包含一个静态成员(碰巧是类的互斥体类型)。

Now when my program terminates it is crashes. 现在,当我的程序终止时,它崩溃了。 This happens when static object 'obj' gets destructed. 当静态对象'obj'被破坏时,会发生这种情况。 The destructor of 'obj' calls its static member (my own mutex type following RAII idiom to destruct the underlying low level objects). 'obj'的析构函数调用其静态成员(我自己的RAII惯用的互斥锁类型来破坏底层的低级对象)。 Unfortunatly this member happened to be destroyed already since initialization order (-> the opposite destructor order) of static objects is undefined. 不幸的是,由于未定义静态对象的初始化顺序(->反析构顺序),因此该成员已经被破坏。

How to survive from this cleanly? 如何从中干净地生存? I am surpriced this has not happened more often. 我觉得这没有多发生。 Looks like it is quite dangerous to have static non-POD members in general. 通常,拥有静态的非POD成员看起来非常危险。 Escpecially if you do not know their inner structure well. 特别是如果您不太了解其内部结构。

If you make them static inside functions then you'll have their lifetimes be ordered based on who called the function first. 如果使它们在函数内部是static ,则将根据谁先调用函数来对它们的生存期进行排序。 This is how singletons tend to be implemented. 这就是单例易于实现的方式。

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

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

You are really better off avoiding statics though. 不过,最好还是避免使用静态变量。

This is a common gotcha when trying to avoid dynamic allocations. 尝试避免动态分配时,这是常见的陷阱。

The way I avoid this problem is to follow a pattern for 'static classes' - usually static data belongs to a 'manager' class for instances which have no static data. 我避免此问题的方法是遵循“静态类”的模式-对于没有静态数据的实例,通常静态数据属于“ manager”类。 The static data initialisation is then handled in explicit 'initialise' calls, with destruction handled in explicit 'shutdown' calls on the manager class which only has static data and static member functions. 然后,通过显式的“ initialise”调用处理静态数据初始化,并在仅具有静态数据和静态成员函数的manager类上,通过显式的“ shutdown”调用处理破坏操作。

you may have to use a new or delete and be explicit about what you are doing - in this sense you negate the advantage of the automatic mechanisms doing work for you, but in exchange you get dependable and easily debuggable initialisation and shutdown routines. 您可能必须使用new或delete并明确说明正在执行的操作-在这种情况下,您会否定自动机制为您工作的优势,但是作为交换,您将获得可靠且易于调试的初始化和关闭例程。

this approach creates essentially a singleton, so there are no instances of the manager class - basically a load of static data and some C functions with the class syntax providing the benefits of encapsulation (eg private members not being accessible outside of the class) 这种方法实质上创建了一个单例,因此没有管理器类的实例-基本上是静态数据的加载,并且某些C函数具有类语法,从而提供了封装的好处(例如,在类外部无法访问私有成员)

this is also friendly for the compiler in many cases, since it knows where all your data is and what to do with it 在许多情况下,这对编译器也很友好,因为它知道所有数据在哪里以及如何处理

If you need an object which is used by static objects, in the constructor or the destructor, it's generally best to use a variation on the singleton pattern. 如果需要在构造函数或析构函数中由静态对象使用的对象,通常最好在单例模式上使用变体。 This solves the order of initialization issues, and if you do it right, the object will never be destructed, so there should be no order of destruction issues either. 这解决了初始化问题的顺序,如果正确执行,对象将永远不会被破坏,因此也不应该存在破坏顺序的问题。

Since your application is clearly multithreaded (since you have a mutex), you should take the usual precautions to make the object thread safe, by ensuring initialization before you enter main. 由于您的应用程序显然是多线程的(因为您有互斥锁),因此应采取通常的预防措施,通过确保在输入main之前进行初始化来使对象线程安全。 The basic idea is something like: 基本想法是这样的:

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

You then define the static object as static StaticInstanceWrapper<Whatever> obj; 然后,将静态对象定义为static StaticInstanceWrapper<Whatever> obj; , and access it as obj.instance().someFunction() , rather than just obj.someFunction() (which won't compile, because obj doesn't have a someFunction() member). ,然后将其作为obj.instance().someFunction()而不是obj.someFunction() (因为obj没有someFunction()成员,因此无法编译)。

Note that for each instance of StaticInstanceWrapper to have a different type, and so have unique static members, you have to force a separate instantiation of the template. 请注意,对于每个StaticInstanceWrapper实例StaticInstanceWrapper ,其类型都是不同的,并且具有唯一的静态成员,则必须强制对模板进行单独的实例化。 This is why we hae the id template argument; 这就是为什么我们使用id模板参数的原因; the type of this argument could practically be anything, as long as each instantiation has a unique identifier. 只要每个实例都具有唯一的标识符,则该参数的类型实际上可以是任意值。 In practice, I'd probably use a macro for definition, something along the lines of: 在实践中,我可能会使用宏进行定义,类似于以下内容:

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

This insures that each instance has a unique id. 这样可以确保每个实例都有唯一的ID。 (If you want to get fancier, you can mangle in __LINENO__ as well, but since the name has to be unique in scope anyway, I doubt that this is necessary.) (如果您想变得更高级,也可以在__LINENO__中进行__LINENO__ ,但是由于该名称在范围上必须是唯一的,因此我怀疑这是必要的。)

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

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