7

The following small example implements a singleton pattern that I've seen many times:

#include <iostream>

class SingletonTest {
private:
  SingletonTest() {}
  static SingletonTest *instance;
  ~SingletonTest() {
    std::cout << "Destructing!!" << std::endl;
  }

public:
  static SingletonTest *get_instance()  {
    if(!instance) instance = new SingletonTest;
    return instance;
  }
};

SingletonTest *SingletonTest::instance = 0;

int main(int argc, char *argv[]) {
  SingletonTest *s = SingletonTest::get_instance();

  return 0;
}

The main problem I have with this is that the destructor of my singleton is never called.

I can instead make instance a (c++0x?) shared_ptr, which works well - except that it means my destructor has to be public.

I could add a static 'cleanup' method but that opens up the possibility of user error (i.e. forgetting to call it). It would also not allow for proper cleanup in the face of (unhandled) exceptions.

Is there a common strategy/pattern that will allow lazy instantiation, 'automatically' call my destructor, and still allow me to keep the destructor private?

sje397
  • 41,293
  • 8
  • 87
  • 103

6 Answers6

20

...not exactly a direct answer, but too long for a comment - why not do the singleton this way:

class SingletonTest {
private:
  SingletonTest() {}
  ~SingletonTest() {
    std::cout << "Destructing!!" << std::endl;
  }

public:
  static SingletonTest& get_instance()  {
    static SingletonTest instance;
    return instance;
  }
};

Now you have a lazy singleton that will be destructed on exit... It's not any less re-entrant than your code...

Nim
  • 33,299
  • 2
  • 62
  • 101
  • I like this method. I'm not a huge fan of static locals, but given the alternatives this is good. – sje397 Aug 09 '11 at 08:47
  • 1
    @sje397, I think this is about the only time I would use them (static locals), but as usual best advice is to avoid globals entirely if possible... ;) – Nim Aug 09 '11 at 08:51
  • 1
    +1 I never use member, but static local instead. It also avoids the problem of multithread initialization. – Antonio Pérez Aug 09 '11 at 09:09
  • 2
    @Antonio - how? (I mean how does it avoid the problem of multithread initialization, the above is not threadsafe). – Nim Aug 09 '11 at 09:10
  • @Nim - I used to think local static initialization was thread-safe, but seems like it's a non-standard behaviour, but a GCC (and probaly other compilers) extension/option http://stackoverflow.com/questions/1270927/are-function-static-variables-thread-safe-in-gcc Apparently, local statics will make it in C++1x http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/ca2026b58e6651dd – Antonio Pérez Aug 09 '11 at 11:37
  • 2
    @Antonio: it is non-standard in C++03, but only because the C++03 Standard makes no mention of threads. Any decent compiler already made this thread-safe. I had issues with this idiom on Windows (DLL) if the method was inlined (and not defined in the .cpp file), as each DLL got its own local static variable... it was with VC++03 though, so things may have changed since. – Matthieu M. Aug 09 '11 at 12:14
  • Yes, this is an idiomatic way of creating a singleton. And now, since C++0x requires it to be thread-safe, I don't see any reasons creating singletons in any other way. One still has to be careful though not to use the singleton after its destruction. – Gene Bushuyev Aug 09 '11 at 18:28
  • I had a try and found that `~SingletonTest()` must be public, otherwise it won't compile in gcc. – Deqing Feb 06 '14 at 05:29
  • Two years later (with gcc 5.3.1), destructor *can* be private. – Aconcagua Jul 08 '16 at 03:47
  • returning static locals is dangerous as you can run into dead reference issues http://stackoverflow.com/questions/3509763/singleton-dead-reference-problem – PapaDiHatti Sep 10 '16 at 02:11
  • Didn't work for me, constructor is called, destructor didn't – Maxim Jun 05 '18 at 19:23
2

You could write a deinitialization function and call atexit() inside the object constructor to register it. Then when C++ runtime deinitializes the module it will at some point after main() call your deinitialization function. That bold italic is there because you get rather loose control on when exactly it is called and that can lead to deinitialization order fiasco - be careful.

sharptooth
  • 167,383
  • 100
  • 513
  • 979
1

You could always friend the shared_ptr (or rather scoped_ptr, which is more fitting) to allow it access to your private destructor.

Note that there's also the system atexit() function which can register a function to call at the end of the application. You could pass a static function of your singleton that just does delete instanance; to it.

Note that it's usually a good idea separates the class that is to be a singleton from the singleton-ness of it. Especially for testing and/or when you do need the doubleton. :)

While I'm at it, try to avoid lazy initialization. Initialize/create your singletons at startup, in a well determined order. This allows them to shut down properly and resolves dependencies without surprises. (I have had cyclic singleton hell... it's easier than you think...)

Macke
  • 24,812
  • 7
  • 82
  • 118
  • I read somewhere that 'scoped_ptr' was dropped from c++0x...? – sje397 Aug 09 '11 at 08:41
  • @Macke: I could not get the friend approach [to work](http://ideone.com/h0UnR). What am I doing wrong? – Björn Pollex Aug 09 '11 at 08:43
  • 1
    @Björn: my bad. The deletion is actually done by checked_delete(), so you need to friend that. Look at the source pointed to by the error messages. – Macke Aug 09 '11 at 08:48
  • @Björn: You need to do something like this-->> "friend void boost::checked_delete<>( Test* x );" -- however, due to compiler non-support described at http://drdobbs.com/184403853, i had to settle for-->> "template friend void boost::checked_delete(T * );" – pestophagous Sep 09 '11 at 18:11
1

You can use a private destructor with shared_ptr by passing in a deleter that has access to the destructor (such as a class defined as a member of SingletonTest).

However, you need to be very careful when destroying singletons to ensure that they are not used after they are destroyed. Why not just use a plain global variable anyway?

Anthony Williams
  • 66,628
  • 14
  • 133
  • 155
1

if you declare the class which does the actual delete op as a friend (let it be shared_ptr<SingletonTest> or some kind of default deleter) a friend, your destructor can be private. Although i dont see any necessarity for making it private.

smerlin
  • 6,446
  • 3
  • 35
  • 58
  • The reason for making the destructor private is the same as the reason for making the constructor private: so that the lifetime of the object cannot be influenced by other code. – sje397 Aug 09 '11 at 10:36
  • @sje397: Well, a noble objective, but i think its really damn obvious that a reference returned by singleton class may not be cast to a pointer and deleted. If the guys, which will use your API do things like that, you will have a hard time making your complete API "safe" for them (especially since C++ allows `delete` to be called on const objects aswell). – smerlin Aug 10 '11 at 17:04
0

The first question is: do you want the singleton to be destructed. Destructing a singleton can lead to order of destruction problems; and since you're shutting down, the destructor can't be necessary to maintain program invariants. About the only time you want to run the destructor of a singleton is if it manages resources that the system won't automatically clean up, like temporary files. Otherwise, it's better policy to not call the destructor on it.

Given that, if you want the destructor to be called, there are two alternatives: declare the single object as a static local variable in the instance function, or use std::auto_ptr or something similar, instead of a raw pointer, as the pointer to it.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • 2
    Call me obsessive, but hate leaving things undone and/or up to the OS. Difficulties with interdependencies should be sorted out, IMHO. – sje397 Aug 09 '11 at 13:20
  • @sje397 The whole point of the C++ singleton idiom is to resolve order of initialization dependencies. Which supposes that dependencies may be an issue. And you can't avoid leaving a lot up to the OS; it recovers your memory, for example (including static memory and the memory where your code resides). – James Kanze Aug 09 '11 at 14:07
  • There are many things that the system won't automatically do for you, like stopping spawned processes, or correctly terminating threads, or updating database, or releasing licenses, or saving the program state, etc, etc – Gene Bushuyev Aug 09 '11 at 18:46
  • @Gene Bushuyev Certainly, but how many singletons handle any of those. You want to correctly terminate threads before calling `exit`, for example; you can't save state in a static destructor, because a lot of the state is already gone; and all of the others are related to specific activities within the application, and shouldn't be managed by a singleton anyway. – James Kanze Aug 10 '11 at 08:15