|
Boost : |
From: Jason Hise (chaos_at_[hidden])
Date: 2005-01-05 21:48:35
Joe Gottman wrote:
> 1) I strongly dislike your virtual operator=(const SingletonBase &).
>Most of the Singletons I use don't have any virtual functions, and I would
>rather not add any if I don't have to. In your comments you say you only
>define it to make sure that SingletonBase cannot be instantiated. Keeping
>the destructor (or all the constructors) non-public has the same effect.
>
> 2) It would be easier on users if, instead of Singleton<T> inheriting
>from T, you reversed it so that T must inherit from Singleton<T> (the
>curiously recurring template pattern). This would make it possible to
>communicate in both directions between T and Singleton<T>. It would also
>make it much easier to turn an existing class into a Singleton.
>
First I should note that I made some changes since the first version I
posted, the new version is available at
http://www.ezequal.com/chaos/libs/singleton_revised.h. Specifically,
the singleton no longer has a static dependency on itself, so client
code can control lifetimes much more dynamically. In addition, I made
GetInst return a smart pointer containing a dependency so that GetInst
will never fail.
It looks like I should post some sample client code so that it is clear
exactly how my design is intended to be used. It is a bit unusual, but
actually very clean and safe:
#include <iostream>
#include "Singleton.h"
using namespace utils;
using namespace std;
class A : public SingletonBase
{
public:
A ( )
{
cout << "A created\n";
}
~ A ( )
{
cout << "A destroyed\n";
}
void DoSomethingUseful ( )
{
cout << "A being useful\n";
}
};
class B : public SingletonBase
{
private:
// 1)
//Singleton < A > :: Dependency require_a;
public:
B ( )
{
cout << "B created\n";
}
~ B ( )
{
cout << "B destroyed\n";
}
void DoSomethingUseful ( )
{
cout << "B being useful\n";
Singleton < A > :: SingletonPtr instance = Singleton < A > ::
GetInst ( );
instance->DoSomethingUseful ( );
}
};
// 2)
//Singleton < B > :: Dependency require_b;
int main ( )
{
cout << "Entering main\n";
{
// 3)
//Singleton < B > :: SingletonPtr instance = Singleton < B > ::
GetInst ( );
//instance->DoSomethingUseful ( );
}
cout << "Exiting main\n";
return 0;
}
If both points 2 and 3 stay commented out, no singletons are created.
If 2 is uncommented, B will exist for the lifetime of the program. If 1
is also uncommented, A will exist before and after B is created and
destroyed. If 2 remains commented but 3 is uncommented, the singletons
are created in the scope where the instance is referenced, and destroyed
when the scope exits. Singletons would be able to be reincarnated any
number of times.
The important thing to notice is the safety that is enforced at compile
time:
A) It is illegal at compile time to have a Singleton < T > where T does
not derive from SingletonBase.
B) It is illegal at compile time to create an instance of
SingletonBase. It is also illegal to create an instance of a derived
type, because SingletonBase makes assignment pure virtual, therefore
making child classes pure virtual as well.
C) Even though Singleton < T > overrides the assignment operator to
become non abstract, it still cannot be created by client code because
of it's private constructor and destructor.
D) The singleton instance cannot be copied; only SingletonPtrs to it can
be copied, which essentially just increase the number of dependencies.
I cannot think of any way I could have enforced all of this if singleton
only acted as a base class.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk