An issue with the SingletonDestroyer class.
Wednesday, December 26, 2007, 11:30 AM -
Programming
In my last
post, I had posted a link to a
report by John Vlissides, where he illustrates a beautiful way of destructing a Singleton object.
Hoping that you have understood his SingletonDestroyer class, I bring here again the class declaration:
class SingletonDestroyer {
public:
SingletonDestroyer(Singleton* = 0);
~SingletonDestroyer();
void SetSingleton(Singleton* s);
private:
Singleton* _singleton;
};
Recollecting a little more from the article, the Singleton class declares a static SingletonDestroyer member, which gets created automatically at program startup.
And here is the snippet of the Singleton class itself.
class Singleton {
public:
static Singleton* Instance();
protected:
Singleton() { }
friend class SingletonDestroyer;
virtual ~Singleton() { }
private:
static Singleton* _instance;
static SingletonDestroyer _destroyer;
};
Singleton* Singleton::_instance = 0;
SingletonDestroyer Singleton::_destroyer;
Singleton* Singleton::Instance () {
if (!_instance) {
_instance = new Singleton;
_destroyer.SetSingleton(_instance);
}
return _instance;
}Now lets come to the point: Note that the constructor of the SingletonDestroyer class takes an optional argument (defaulting to 0) which seems to be pretty fine.
Here is the chain of events that happens most of the time:
1. At startup the SingletonDestroyer is created since it is a static variable.
2. The Singleton object is created somewhere in the code:
Singleton* s = Singleton::Instance();
The Instance() function internally calls _destroyer.SetSingleton(_instance);
Till the end of the program the varibale SingletonDestroyer::_singleton holds the address of the actual singleton object created on the heap.
Now what if the step 1 as mentioned above occurs after step 2. How is this possible?
What if step 2 mentioned above is called outside the main() function of your program. In such a case both the creation of the static variable, SingletonDestroyer variable and the Singleton object are declared in the global space and if they are declared in different files, then there is no guarantee about the sequence in which the two variables would be created.
If step 1 happens after step 2, ie, if the _destroyer variable's constructor gets called after the Singleton object is created, then the SingletonDestroyer::_singleton gets reset to NULL value because of the default argument in the constructor.
If such a thing happens that the destructor of your Singleton object will ever get called at shutdown.
There are two possible solutions to this:
1. Dont create your singleton object outside the main() function. This ensures that the SingletonDestroyer's constructor is always called before the Singleton object is created.
2. Let the constructor of the SingletonDestroyer class have no arguments.
Below is the complete program that illustrates the scenario where the destructor of the Singleton object never gets called.
#include <iostream>
template <class SINGLETON>
class SingletonDestroyer {
public:
SingletonDestroyer(SINGLETON* = 0);
~SingletonDestroyer();
void SetSingleton(SINGLETON*);
private:
// Prevent users from making copies of a
// Destroyer to avoid double deletion:
SingletonDestroyer(const SingletonDestroyer<SINGLETON>&);
void operator=(const SingletonDestroyer<SINGLETON>&);
SINGLETON* _singleton;
};
template <class SINGLETON>
SingletonDestroyer<SINGLETON>::SingletonDestroyer (SINGLETON* s) {
std::cout << "Step 1: Constructor of the SingletonDestroyer object" << std::endl;
_singleton = s;
}
template <class SINGLETON>
SingletonDestroyer<SINGLETON>::~SingletonDestroyer () {
delete _singleton;
}
template <class SINGLETON>
void SingletonDestroyer<SINGLETON>::SetSingleton(SINGLETON* s) {
_singleton = s;
}
class Singleton {
public:
static Singleton* GetInstance();
protected:
Singleton();
~Singleton();
private:
friend class SingletonDestroyer<Singleton>;
static Singleton* _instance;
static SingletonDestroyer<Singleton> _destroyer;
};
Singleton *single = Singleton::GetInstance();
Singleton* Singleton::_instance = NULL;
SingletonDestroyer<Singleton> Singleton::_destroyer;
Singleton* Singleton::GetInstance() {
std::cout << "Step 2: GetInstance : Creation of the Singleton object" << std::endl;
if (!_instance) {
_instance = new Singleton;
_destroyer.SetSingleton(_instance);
}
return _instance;
}
Singleton::Singleton() {
}
Singleton::~Singleton() {
std::cout << "Destructing the Singleton object." << std::endl;
}
int main() {
}
Compiled in Visual Studio 2005, the output is:
Step 2: GetInstance : Creation of the Singleton object
Step 1: Constructor of the SingletonDestroyer object
Press any key to continue . . .
Applying either if the above two suggestions, the output is:
Step 1: Constructor of the SingletonDestroyer object
Step 2: GetInstance : Creation of the Singleton object
Destructing the Singleton object.
Press any key to continue . . .
In this sample program, I had intentionally placed the line
Singleton *single = Singleton::GetInstance();
before
SingletonDestroyer<Singleton> Singleton::_destroyer;
But the point here is that it is perfectly reasonable to have these two lines in different .cpp files or header files and there is no guarantee in what sequence the compiler would consider it.
Happy Destruction.//