Introduction
Object creation is an important step in any Object Oriented Application. An application can use the services of a class only after creating an object of it. In most applications, creating an object of a class is very straight forward, that is, by declaring the instance of the class. For example, to create an object MyObj of class MyClass, the C++ syntax is MyClass MyObj;. But, in a complex Object Oriented Application, the task of creating the objects is usually assigned to other classes. The book, Design Patterns : Elements of Reusable Object-Oriented Software by Erich Gamma et al. ( Addison-Wesley, 1995 ) explores different approaches that can be used for object creation under the classification, Creational Patterns.
This article discusses a Creational Pattern called Singleton and explains different approaches for implementing Singleton pattern using a Visual C++ example.
What is a Singleton Class?
Sometimes, you may want to have only one object for a given class and this object should be easily accessible. For example, an Application should have only one Application object and the Job schedulers should share a single Job Queue object. A global variable may provide easy access, but using a global variable will not stop the user from creating more than one object and moreover it is discouraged in Object Oriented Programming. In the above example, the Application class should not allow the user to create more than one object of it and the Job Queue class should ensure that a single Job Queue object is shared by all the Job schedulers. Application and Job Queue classes are called Singleton classes. So, what is a Singleton class ? A class that assures a maximum of ONE object of its type at a given time and provides a global access point to this object is a Singleton class.
An Example
This article uses "Message Handlers" as an example to explain the concept of a Singleton class. An application interacts with the user through messages. This includes errors, warnings and information messages. The application can use a single message handler object to format or log these messages before presenting it to the user. Hence, the message handler class can be a Singleton class.
The approaches that are presented in this article includes
Singletons using static variables
Listing 1 contains a simple message handler class. The constructor and destructor are declared as protected members to avoid direct object creation or destruction. The static method GetMsgHndlr returns a pointer to the static member m_MsgHndlr.
Listing 1a shows a variation of message handler class, and it uses a function static member instead of a class static member. The object is created only when GetMsgHndlr is called for the first time.
Advantage
- Ensures only one message handler object is present all the time and eliminates the need for a global variable.
- Message handler using function static member ( Listing 1a ) creates the object, only when GetMsgHndlr is called for the first time. So the object is created only if it is needed.
Disadvantage
Class of the object CMsgHndlr is hard coded and the application cannot use a different message handler, without changing the class.
Singletons on heap
Listing 2 contains a message handler class that creates object on the heap. A key is passed as a parameter to GetMsgHndlr for creating the required message handler object. Now, the message handler class not only ensures the uniqueness of the message handler object, but it is also responsible for creating the required message handler object. An object is created when GetMsgHndlr is called for the first time and the subsequent call returns the created object.
Advantage
Applications can select the required message handler by passing the appropriate key. Only message handler class needs to be changed to include a new message handler.
Disadvantage
CMsgHndlr class should be aware of all its subclasses and include their header files, to create their objects.
CMsgHndlr class is responsible for creating message handler objects based on the key passed to GetMsgHndlr method and this needs to be changed when a new message handler is added.
- Application is forced to choose the required message handler class at link time.
Singletons using RTTI
Run-time type information ( RTTI ) is a mechanism to determine the type of an object during program execution. RTTI is now available as part of C++ language. This approach uses MFC’s CRuntimeClass to identify the class of object dynamically and create it when needed ( Listing 3 ).
CObject, the root class of MFC provides the support for Run-time class information and dynamic object creation. To use these services, CMsgHndlr class should be inherited from CObject, DECLARE_DYNCREATE( CMsgHndlr ) macro must be included in the header file and IMPLEMENT_DYNCREATE( CMsgHndlr, CObject ) macro must be added to the implementation file.
GetMsgHndlr method takes CRuntimeClass * as parameter. It ensures that the given Runtime class is of type CMsgHndlr and returns the created object. RUNTIME_CLASS( class_name ) macro can be used to get CRuntimeClass information for a given C++ class.
Advantage
GetMsgHndlr can create object of CMsgHndlr or its descendants just with their Runtime class information, without having to know their exact class names. This method need not change when a new message handler subclass is created.
Disadvantage
Choice of the message handler class is made at link time. If a new message handler class needs to be used, the application must pass the appropriate run time class information to GetMsgHndlr.
Self-Registering Singletons
Let’s take an example of an application making the choice of message handler at runtime based on a key value set in the Registry or value passed as a Command Line Argument. Since, the selection of message handler is made at runtime, the application should be aware of all the available message handlers to select the appropriate one based on the runtime parameter, using a series of if statements. ( Listing 4 )
Listing 4 shows that, the code which checks and creates the message handler is moved from CMsgHndlr ( Listing 2 ) to SelectMsgHndlr function in the application ( Listing 4 ). This kind of function should be written in all the applications using message handler. To complicate the situation further, let us assume that the message handlers are contained in a shared library or a DLL and the application is not aware of all available message handlers ( new message handler classes may be added in future to the library ). How can we select the right message handler ? It is simple. Each message handler class should register their runtime class information in a Message Handler Registry. This runtime information is used to create the required message handler object when needed. I have extended the idea of "Self-Registering, Objects in C++" by Jim Beveridge ( Dr. Dobb’s Journal, August 1998 ), to register the runtime class information. ( Listing 5 )
CObjRegistry maintains a registry object, in which the runtime class information is stored. CMsgHndlrRegistry is a template class inherited from CObjRegistry. Message handler classes register their runtime class information to CObjRegistry via CMsgHndlrRegistry. GetMsgHndlr method takes a key as a parameter, gets the required runtime class information from CObjRegistry and creates the required message handler object.
Advantage
- The class information of the message handlers are registered only at runtime, so a new message handler class can be added without affecting other pieces of the code.
- Choice of the message handler class can be made at run time. If a new message handler class needs to be used, it can be specified to the application through Registry or as a Command Line Argument, without changing the application.
Who’s going to Bell the Cat ?
Object cleanup is a problem with Singleton. Message handler object is created on the heap by GetMsgHndlr method, but who’s going to delete it ? Strictly speaking, the application or the client module should delete the object after its use. It will result in a memory leak, if the object is not deleted. So, I feel that CMsgHndlr should be smart enough to take care of its own cleanup.
Smart Singletons
Listing 6 contains smart message handler that takes care of its cleanup. CMsgHndlr class employs a Smart Cleaner ( CSmartCleaner ) to do the cleanup job. It declares a static data member of CSmartCleaner class and says "Hello" to CSmartCleaner through SetObject( this ) during construction and says "Goodbye" through SetObject( NULL ) during destruction. When an application terminates, C++ will not do an automatic cleanup of the objects allocated on heap, but cleans up the static objects. So, the static CSmartCleaner object gets destructed at the end of application and the destructor of CSmartCleaner deletes the message handler object, if the application forgets to delete it.
Benefits of Singleton
- Singleton ensures that only one object of the class type is created and provides a global access to this object.
- Singleton classes can be used to create an object of its derived class, and the applications can choose the required singleton object without much change in the code.
- Singleton eliminates the need for a global variable.
Beyond Singleton
Even though Singleton is the main topic of discussion, this article introduces the following techniques to C++ developers.
- An approach to use MFC’s RTTI support for dynamic object creation.
- An approach for self-registering objects.
- An approach for object cleanup ( smart cleaner ), that can be used by a class to make sure that its instances are cleaned up properly.
- An approach that can be extended to create more than one object of a class in a controlled fashion.
Conclusion
Singleton classes are used in well-known applications. MFC’s CWinApp is a good example, it ensures that an application has only object of CWinApp type. Smalltalk’s metaclass is another example for a singleton class. A metaclass is the class of a class and there is only one instance of a metaclass for providing class level services.
This article used a message handler example to introduce the concept of Singletons. It walked through the different ways of implementing Singletons with their advantages and disadvantages. It started with a simple approach for creating Singletons using static variables, then it explained how an object of a Singleton class ( or its sub class ) could be created on heap. It also showed how the other two approaches - Singletons using RTTI and Self Registering Singletons, can be implemented using Visual C++ RTTI support. These two approaches can also be implemented using any other C++ compiler that has RTTI support. Smart Singletons presented an idea for object cleanup and showed how it can take care of its own cleanup.
Acknowledgments
Special thanks to my friend Sree Meenakshi for her helpful suggestions in improving the clarity and presentation of this article.
Listing 1 - Message Handler using static variables
class CMsgHndlr
{
public :
static CMsgHndlr * GetMsgHndlr();
protected :
CMsgHndlr(){}
virtual ~CMsgHndlr(){}
static CMsgHndlr m_MsgHndlr;
};
CMsgHndlr CMsgHndlr::m_MsgHndlr;
CMsgHndlr * CMsgHndlr::GetMsgHndlr()
{
return & m_MsgHndlr;
}
Listing 1a - Variation of GetMsgHndlr using Function static members instead of class static
CMsgHndlr * CMsgHndlr::GetMsgHndlr()
{
static CMsgHndlr MsgHndlr;
return & MsgHndlr;
}
Listing 2 - Message Handler on heap
class CMsgHndlr
{
public :
static CMsgHndlr * GetMsgHndlr( LPCSTR );
virtual ~CMsgHndlr(){}
protected :
CMsgHndlr(){}
static CMsgHndlr * m_pMsgHndlr;
};
CMsgHndlr * CMsgHndlr::m_pMsgHndlr = NULL;
CMsgHndlr * CMsgHndlr::GetMsgHndlr( LPCSTR lpszKey )
{
if(m_pMsgHndlr == NULL )
{
if( stricmp( lpszKey, "MSGHNDLR" ) == 0 )
{
m_pMsgHndlr = new CMsgHndlr;
}
else if( stricmp( lpszKey, "SMPHNDLR" ) == 0 )
{
m_pMsgHndlr = new CSmpHndlr;
}
}
return m_pMsgHndlr;
}
class CMsgHndlr : public CObject
{
public :
static CMsgHndlr * GetMsgHndlr( CRuntimeClass * );
DECLARE_DYNCREATE( CMsgHndlr )
virtual ~CMsgHndlr(){}
protected :
CMsgHndlr(){}
static CMsgHndlr * m_pMsgHndlr;
};
IMPLEMENT_DYNCREATE( CMsgHndlr, CObject )
CMsgHndlr * CMsgHndlr::m_pMsgHndlr = NULL;
CMsgHndlr * CMsgHndlr::GetMsgHndlr( CRuntimeClass * pRuntimeClass )
{
ASSERT( pRuntimeClass != NULL );
if( m_pMsgHndlr != NULL )
{
return m_pMsgHndlr;
}
CRuntimeClass * pBaseClass = RUNTIME_CLASS( CMsgHndlr );
if(pRuntimeClass ->IsDerivedFrom( pBaseClass ) == FALSE )
{
return NULL;
}
m_pMsgHndlr = ( CMsgHndlr * ) pMsgHndlrClass->CreateObject();
return m_pMsgHndlr;
}
Listing 4 -Selecting message handler using the key
CMsgHndlr * SelectMsgHndlr( LPCSTR lpszKey )
{
if( stricmp(lpszKey, "MSGHNDLR" ) == 0 )
{
return CMsgHndlr::GetMsgHndlr( RUNTIME_CLASS( CMsgHndlr ) );
}
else if( stricmp( lpszKey, "SMPHNDLR" ) == 0 )
{
return CMsgHndlr::GetMsgHndlr( RUNTIME_CLASS( CSmpHndlr ) );
}
return NULL;
}
class CObjRegistry
{
public :
CObjRegistry( CRuntimeClass * pRuntimeClass, LPCSTR lpszKey )
{
ASSERT( pRuntimeClass != NULL );
ASSERT( lpszKey != NULL );
GetRegistry().SetAt( lpszKey, pRuntimeClass );
}
static BOOL GetRuntimeClass( LPCSTR lpszKey,
CRuntimeClass * & rpRuntimeClass )
{
return GetRegistry().Lookup( lpszKey,
( LPVOID & ) rpRuntimeClass );
}
private :
static CMapStringToPtr & GetRegistry()
{
static CMapStringToPtr Registry;
return Registry;
}
};
template <class T> class CMsgHndlrRegistry : public CObjRegistry
{
public :
CMsgHndlrRegistry( CRuntimeClass * pRuntimeClass, LPCSTR lpszKey ) :
CObjRegistry( pRuntimeClass, lpszKey ) { }
};
static CMsgHndlrRegistry< CMsgHndlr > gMsgHndlr( RUNTIME_CLASS( CMsgHndlr ),
"MSGHNDLR" );
CMsgHndlr * CMsgHndlr::GetMsgHndlr( LPCSTR lpszKey )
{
CRuntimeClass * pRuntimeClass = NULL;
if( CObjRegistry::GetRuntimeClass( lpszKey, pRuntimeClass ) == FALSE )
{
return NULL;
}
return GetMsgHndlr( pRuntimeClass );
}
static CMsgHndlrRegistry< CSmpHndlr > gSmpHndlr( RUNTIME_CLASS( CSmpHndlr ),
"SMPHNDLR" );
CMsgHndlr * SelectMsgHndlr( LPCSTR lpszKey )
{
return CMsgHndlr::GetMsgHndlr( lpszKey );
}
class CMsgHndlr : public CObject
{
protected :
CMsgHndlr()
{
m_SmartCleaner.SetObject( this );
}
static CMsgHndlr * m_pMsgHndlr;
public :
static CMsgHndlr * GetMsgHndlr( CRuntimeClass * );
virtual ~CMsgHndlr()
{
m_SmartCleaner.SetObject( NULL );
CMsgHndlr::m_pMsgHndlr = NULL;
}
private :
static CSmartCleaner m_SmartCleaner;
};
class CSmartCleaner
{
public :
CSmartCleaner( CObject * pObject = NULL ) : m_pObject( pObject ) { }
virtual ~CSmartCleaner()
{
if( m_pObject != NULL )
{
delete m_pObject;
}
}
void SetObject( CObject * pObject )
{
m_pObject = pObject;
}
CObject * GetObject()
{
return m_pObject;
}
private :
CObject * m_pObject;
};
Listing 5 - Self-Registering SingletonsListing 3 - Message Handler using RTTI
| You must Sign In to use this message board. |
|
|
 |
|
 |
When you're using the "Listing 1a" to implement singleton, the object is create only once. That's OK, but the constructor of this object is call each time you call "GetMsgHndlr()" even if the object is create once !!!
It can cause problem to some of you
Francois Chartrand
|
| Sign In·View Thread·PermaLink | 1.00/5 |
|
|
|
 |
|
 |
Hi there, This solution may be okay for all you MFC heads, but for those projects where MFC isn't or cannot be used - you may like to look at this thread-safe implementation - http://www.codeproject.com/useritems/PDESingletonTemplateNoMFC.asp [^].
As the author of this article says, there are n different ways of solving a problem, and if this particular n does not float your boat, then perhaps mine or one of my references might! 
Cheers!
/********************************** Paul Evans, Dorset, UK. Personal Homepage "EnjoySoftware" @ http://www.enjoysoftware.co.uk/ **********************************/
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
I like your smart class for resource deallocation, but wouldn't it be better to use the STL auto_ptr right in your singleton? I mean, why reinvent the wheel? I did some testing of resource deallocation and memory leak both with and without the auto_ptr and everything looks like it is cleaning up when using the auto_ptr as opposed to not using it. Anyway, just wondering if there was some reason I was not aware of.
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
Yeah I like that.
Another alternative (bit C rather than C++ ) is the _onexit function (which is actually how the CRT cleans up global objects as well).
Onexit is a great tool to catch all those bits that other wise can be a real pain.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I noticed you caught the memory leak issue with the singleton class as it stands, so I figured I would pose a question to you. Ok I am using a class that is a Singleton and an AbstractFactory called ThreadFactory. As you can tell by the name I want this factory to handle all things thread related and in a thread-safe manner. I have also defined a couple abstract products for ThreadFactory called Thread and Lock. Depending on the OS that this project is being compiled for ThreadFactory will return an instance of the OS-specific concrete ThreadFactory class. For example:
ThreadFactory | -------------------- | | WinThreadFactory LinuxThreadFactory
Now in my implementation I wanted to set up ThreadFactory to keep a reference count, and when that reference count reaches 0 then the instance is cleaned up. Granted this may cause some overhead due to potential creation/deletion/creation, but I don't want the client to know about the memory issues at all. So this seems to be ok, so far.
Now, we've handle the memory leak issue, but there is another issue. How do we make ThreadFactory thread safe? The intersting thing is that the construction could be thread safe because for two threads to even exist the ThreadFactory must have already been instantiated. So we could say that once we see _instance != 0 that we're gauranteed that it exists and we just increment the reference count. But then what happens if we have two threads, one has a ThreadFactory instance and the other does not. Now the other needs to get the thread instance and it realizes the instance exists so it calls IncrementCount(). At the same time the other thread has released its handle to the ThreadFactory and consequently the instance is deleted. Its making my head spin! 
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
Ok, should have read your complete implementation before I got myself onto the Reference Counting path. What an elegant solution! I must say that I am quite impressed with your adaption of the Singleton class. I think the authors intentionally kept it basic to avoid adding more complexity to these patterns, and its fun to try them out and extend them. Looks like you cover all the obstacles that I can see with actually implementing it though.
Thanks!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
The obvious solution to thread-safe singletons that clean up after themselves is to use someone elses work. Have a look at Alexandrescu's Loki library which has thread safe singletons, with a variety of methods to clean up the memory, including methods to ensure that inter-related singletons destruct in the correct order
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
 | Hmm  Anonymous | 19:57 10 Apr '01 |
|
|
 |
|
 |
Is class CMsgHndlr really singleton ? No.
I think I can create many instances of the class:
CMsgHndlr handler1 = *CMsgHndlr::GetMsgHndlr(); CMsgHndlr handler2 = *CMsgHndlr::GetMsgHndlr(); CMsgHndlr handler3 = *CMsgHndlr::GetMsgHndlr();
Because neither copy constructor nor assignement operator are rewritten they are generated by compiler as public.
Why function GetMsgHndlr() returns pointer instead of reference ? The main difference between pointers and references is that pointers can be uninitialized ( initialized with 0 ). Function GetMsgHndlr() never returns NULL pointer so it seems that it sould return reference. References are safer than pointers and easier to use - you don't have to check it to NULL.
Compare two case first uses pointer second reference: 1. CMsgHndlr* pHndlr = GetMsgHndlr(); if(pHndlr != NULL) pHndlr->Handle( ... ); 2. GetMsgHndlr().Handle( ... );
Also I didn't understand why destructor is declared as virtual ? Is this class polymorphic ?
Better implementation of Singleton with static variable is:
class Singleton { public: ~Singleton() {}
public: static Singleton& instance();
private: Singleton() {} Singleton(const Singleton&) {} void operator=(const Singleton&) {} };
Singleton& Singleton::instance() { static Singleton the_singleton; return the_singleton; }
parti3an
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
To answer your question first, is it really a singelton? Yes, the "article" is focussing on Singletons. As you know, there are "n" different ways of implementing an algorithm or providing a solution to a problem. I tried to explain the concept of Singleton and few ways of implementing it "without much bells and whistles". Of course as a C++ programmer, I do appreciate the use of BIG Three in a C++ class, I mean "the Assignment Operator, the Copy Constructor and Destructor". Also, I understand the differences between pointers, references with their merits and demerits.
Unfortunately, I cannot write a 50 page article in a public domain covering all these concepts. How many source code implementations in Code Project has assignment operator overloaded? If someone wants to screw around things, you can misuse the language to its core. For example, you can write a program to use a block of memory without allocating it. Finally, your better implementation is shown in Listing 1a, again just focussing on Singleton.
In short, the article is focussing on explaining the concept of Singleton and few ways to implement it. It is up to the individual to make it better.
Anyway, thanks for your comments.
|
| Sign In·View Thread·PermaLink | 3.00/5 |
|
|
|
 |
|
 |
If CMsgHndlr or any singleton class were an abstract class and you wanted to only create a singleton instance for a derived concrete class then it would appear that this wouldn't work? It looks like this would only work if CMsgHndlr were a concrete class. Please correct me if I am wrong.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Regarding the virtual destructor and "Is this class polymorphic ?" ; it is always better to declare the destructors of any class virtual. If the class is not polymorphic it doesn't hurt, if it is, at least you're sure you didn't forget it
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I disagree with the "it doesn't hurt". In the general case you shouldn't declare a destructor virtual if it doesn't need one. In a simple class this would add a vtable cost for no gain. This can be very important depending on your problem context. If you have any virtual methods at all then I would generally recommend a virtual destructor as you are carrying the cost of the vtable already and you gain all the benefits of a virtual destructor. Obviously any resource allocation in any level of a hierarchy (even if there are no existing virtual functions) would imply that a virtual dtor is a good thing. Even here a concrete rule doesn't apply 100% of the time.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Making the private/protected copy constructor do nothing could lead to bugs in later versions of your class. Imagine you (or some poor unsuspecting newbie programmer) decide to allow the creation of multiple instances or allow multiple instances of a derived class. An empty copy constructor/assignment operator could lead to a hard to find bug .
A cool 'trick' is to leave out the implementation of the copy constructor and the assignment operator. That is drop the empty code block ('{}') that follows the declaration. In this way, when some future programmer change the code, the linker will pick up that the functions are not implemented. This way, the linker picks up the 'bug' while the compiler is 'fooled' to think the constructor is there and it is protected/private.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Is it really a singleton? Meaning that if I run dozens of instances of my dll, do they all access the same one? If not, how would I do that?
I'm admittedly a bit new to classes and threads, most of the programming I ever did predates such things. 68000 anyone?
I have a dll that works fine with one instance but dies with 2, and I want to keep track of what information is shared and not shared (memory is getting overwritten in the second instance), and a centralized 'global' memory manager might just do the trick, but I don't see where in the example this is any better than static.
The trick is different host apps; apparently some create one dll to save space, while others make full instances of each, so static should behave differently, causing memory headaches one way and inaccessibility of anything I do want to share the other way.
me.upAllNightAgainTryingToFigureThisOut() == true;
Dave Heinemann
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi,
I'm getting errors when compiling listing 1 : error C2248: 'CMsgHndlr::~CMsgHndlr' : cannot access protected member declared in class 'CMsgHndlr' Any clue ?
Regards Kurian
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
After some analysis, this is what I found. Static Class members are treated like global variables in C++, even though they can be declared in public, private or protected scope in the context of the class. As you must be knowing, static data members needs to be intialized before the actual use, this is usually done in the .CPP file with a statement looking like "CMsgHndlr CMsgHndlr::m_MsgHndlr;".
When an application starts, constructor for "globally" and "statically" declared objects is called and when the application terminates, the destructor is getting called by the runtime environment. (You can follow the exact sequence with the debugger).
Coming to the current problem, the compiler finds that the destructor for class CMsgHndlr is declared as "protected" and cannot be called by the runtime environment while exiting and hence reports the error C2248. Solution to this problem is to have the destructor declared in the public scope of the class CMsgHndlr. The point that still remains unanswerd is, why is this not true with the "protected" Constructors? I am still trying to find out why is it so. Any views?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Change the code to this,
class CMsgHndlr { public : static CMsgHndlr * GetMsgHndlr(); protected : CMsgHndlr(){} virtual ~CMsgHndlr(){} static CMsgHndlr* m_pMsgHndlr; // Static instance of Message handler class. };
CMsgHndlr * CMsgHndlr::m_pMsgHndlr = 0;
CMsgHndlr * CMsgHndlr::GetMsgHndlr() { if( !m_pMsgHndlr ) { m_pMsgHndlr = new CMsgHndlr; } return m_pMsgHndlr; }
void main() { CMsgHndlr* pLocal; pLocal = CMsgHndlr::GetMsgHndlr(); }
OK, now your'e creating the object on the heap, but it does compile (Yes I know there's a memory leak, and it's a quick 'hack' to get it to work)
For a more thorough description of implementing Singleton patterns in C++, and the pitfalls thereof, get a copy of Effective C++ by Scott Meyers. (CD version available) Read item 26, "Limiting the number of objects of a class" GoF do theory, Scott's a programmer 
Later doodz 
p.s. anyone know why the code didn't indent 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I think if you move the destructor from protected catagory to public catagory, it should work fine.
Bob Su
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Pardon, maybe there's something I've missed, but I don't understand the need for using a template in the implementation of CMsgHndlrRegistry, I've even tryed to avoid its use, replacing its definition with a simple
#define CMsgHndlrRegistry CObjRegistry
and seems to me that it works exactly the same!
Help me understand, please!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Dear Leo Rosa,
There is no need to use templates for the implementation of "Self-Registering Singletons". I could have used CObjRegistry directly or its descendent CMsgHndlrRegistry directly to store the key and RUNTIME class information.
As I have changed the original implementation of CMsgHndlrRegistry to fit into the article context, moved pieces of code from CMsgHndlr to CMsgHndlrRegistry for maintaining consistency in explanation of Singleton implementation approaches the use of templates for self-registering objects is left unexplained in the current context. Also, getting into possibilities of implementing CMsgHndlrRegistry is clearly not the main focus of the article.
Self-Registration is a very interesting technique that can be used in combination with other Design Patterns like Prototype (Prototype Manager) and Proxies (Smart references or Smart Proxies). I have used few Self-Registration techniques, which I would be happy to share with fellow Code Project developers in a separate article.
I apologize for the confusion caused. By the way, replacing class definitions using #define statements may not be a good idea at all times, even though the same end result is achieved.
Thanks for your question.
Regards,
Sarma
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|