0

I've stumbled across a rather annoying issue relating to when the Visual C++ runtime destructs my objects as the program quits.

I have a class which is used to ensure references to certain states are valid. I need to store the states for an unknown amount of time, and during this time the state can be destroyed and I can't use shared_ptr. So I use

class MyClass
{
private:   
    static std::list<MyClass*> make_list();
    static std::list<MyClass*> refed_list;          
    static void StateClosed(B* state);

public:
    B* state;
    MyClass(B* state);
    virtual ~MyClass();

    bool still_valid() const;
 };

Each instance of MyClass adds itself to refed_list in its constructor and removes itself in its destructor. If the encapsulated state is closed, MyClass is notified and it checks refed_list for the encapsulating instance and invalidates its pointer. This isn't really relevant, the important thing is that it's using a static list and it accesses this list in the constructor/destructor. I initialize refed_list in the file where MyClass is defined.

Now, the issue.. When my program closes the runtime cleans up refed_list at some point, and after this it cleans up the instances of MyClass, calling their destructors. They then try to access refed_list which has already been cleaned up. This results in my iterators being incorrect, and I get undefined behaviour, in this case a debug assertion failure.

Is there a way around this issue? I doubt I can specify which order objects in different compilation units are destructed, but is there a way to check if refed_list is still valid? At the moment I check if refed_list.size() == 0 and it seems to work, but the behaviour of this is undefined too (I think?).

user1520427
  • 1,345
  • 1
  • 15
  • 27
  • 1
    Sounds like a singleton lifetime problem. I think Scott Meyers or Andrei Alexandrescu wrote something about managing them in three or so different ways. The Phoenix Singleton might be up your alley. – ta.speot.is Feb 24 '13 at 11:14
  • And this is one of many reasons why I disallow global class instances on my team. Because the order of object destruction during program exit is either non-deterministic, or just hard to get right. In any case, can you just have an application cleanup function run before WinMain/main returns? Or better yet, unless those destructors do something mission critical, why not just let all the objects leak at application shutdown? – selbie Feb 24 '13 at 11:45
  • @selbie I'm starting to think that's a good idea. The reason I'm doing this is because I need to store an object which may be invalidated before it's used, and I can't use `shared_ptr`, so I need someway of notifying the objects if they're valid or not. As such I need to track the objects, and that's why I have the static list. But yeah, if I rewrite a few things I can manually close it which should solve my issue. Hooking into processes makes things difficult sometimes.. – user1520427 Feb 24 '13 at 21:47

2 Answers2

2

You can always make refed_list a pointer that is initialised on start up. That way it will never be cleaned up. (And any memory it uses will be recovered by the OS when your process exits.)

If that sounds like a hack to work around a deeper design problem, then it probably is. :)

  • Yeah the design isn't a good one, I ended up having to make a method that mimics the destructor so I can clean it before the runtime cleans up my globals. – user1520427 Feb 24 '13 at 21:53
0

I don't think this is true:

When my program closes the runtime cleans up refed_list at some point, and after this it cleans up the instances of MyClass, calling their destructors.

The runtime will certainly clean up the list, but not the objects in the list because they're pointers. The only way to do that is to use a smart pointer class like shared_ptr. Still, if you did that, the objects would be trying to access the list while it is being destroyed, which i'm not sure is undefined behavior but it certainly seems shaky.

Perhaps you should redesign it so that the objects don't need to reference the list where they're stored, or at least to do it before the list is destroyed (and by that I mean before list::~list is even called).

user1610015
  • 6,561
  • 2
  • 15
  • 18
  • I didn't mean that it frees the memory of the pointers it stores, I meant the objects which inherit `MyClass` are destroyed later, so the `MyClass` destructor is run, which tries to access the now destructed list. But yeah, sounds like a redesign is in order.. – user1520427 Feb 24 '13 at 21:39