Preventing shared_ptr from eating your singleton objects
Suppose you have a simple (non-threadsafe) singleton object like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class singleton
{
public:
static singleton *getInstance()
{
if (instance == NULL)
{
instance = new singleton();
}
return instance;
}
void doStuff()
{
std::cout << "doing stuff" << std::endl;
}
private:
singleton() { }
static singleton *instance;
};
singleton *singleton::instance = NULL;
Invoking this is easy:
1
2
singleton *s1 = singleton::getInstance();
s1->doStuff();
What happens if you decide to switch to boost::shared_ptr all over your app and convert your singleton pointer full of enthousiasm:
1
2
boost::shared_ptr<singleton> s1(singleton::getInstance());
s1->doStuff();
This will work until you reach the end of your scope, the shared_ptr will then call the destructor on your object! Wait a minute, that's not what we want to happen here right!
The solution is actually quite simple. The singleton object here has a default public destructor (generated by the compiler). So we just add a private destructor like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class singleton2
{
public:
static singleton2 *getInstance()
{
if (instance == NULL)
{
instance = new singleton2();
}
return instance;
}
void doStuff()
{
std::cout << "doing stuff2" << std::endl;
}
private:
singleton2() { }
~singleton2() { // THIS WILL PREVENT USAGE OF SHARED_PTR!! }
static singleton2 *instance;
};
singleton2 *singleton2::instance = NULL;
The cool thing is that this forces you to use a regular pointer (that will still work fine). If you try to use the class with the private destructor in combination with a shared_ptr the compiler will automatically complain:
1
2
3
4
5
6
shared_test.cpp: In function 'void boost::checked_delete(T*) [with T = singleton]':
/usr/include/boost/detail/shared_count.hpp:86: instantiated from 'boost::detail::shared_count::shared_count(Y*) [with Y = singleton]'
/usr/include/boost/shared_ptr.hpp:149: instantiated from 'boost::shared_ptr<T>::shared_ptr(Y*) [with Y = singleton, T = singleton]'
shared_test.cpp:108: instantiated from here
shared_test.cpp:27: error: 'singleton::~singleton()' is private
/usr/include/boost/checked_delete.hpp:34: error: within this context
Cool stuff! It's not pretty but at least it keeps your singletons safe...