简体   繁体   中英

Classes with static members containing static members in C++

I have a class which contains a static member 'obj'. The static class member obj itself contains a static member (which happens to be a mutex type of class).

Now when my program terminates it is crashes. This happens when static object 'obj' gets destructed. The destructor of 'obj' calls its static member (my own mutex type following RAII idiom to destruct the underlying low level objects). 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. 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. 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. 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.

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.

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)

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. 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; , and access it as obj.instance().someFunction() , rather than just obj.someFunction() (which won't compile, because obj doesn't have a someFunction() member).

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. This is why we hae the id template argument; 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. (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.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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