Click here to Skip to main content
Click here to Skip to main content

Creating Singleton Objects using Visual C++

, 8 Jan 2001 CPOL
Rate this:
Please Sign up or sign in to vote.
This article discusses a Creational Pattern called Singleton and explains different approaches for implementing Singleton pattern using a Visual C++ example.

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

  1. Ensures only one message handler object is present all the time and eliminates the need for a global variable.
  2. 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

  1. CMsgHndlr class should be aware of all its subclasses and include their header files, to create their objects.
  2. 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.
  3. 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

  1. 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.
  2. 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

  1. Singleton ensures that only one object of the class type is created and provides a global access to this object.
  2. 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.
  3. 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.

  1. An approach to use MFC’s RTTI support for dynamic object creation.
  2. An approach for self-registering objects.
  3. An approach for object cleanup ( smart cleaner ), that can be used by a class to make sure that its instances are cleaned up properly.
  4. 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;    // Static instance of Message handler
                                        // class.
};

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;    // Create object, when called for the first
                                  //  time
    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;
}

// Object Registry class to store Runtime class information
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;
        }
};

// Message handler registry class, to which Message handlers get registered
template <class T> class CMsgHndlrRegistry : public CObjRegistry
{
    public :
        CMsgHndlrRegistry( CRuntimeClass * pRuntimeClass, LPCSTR lpszKey ) : 
        CObjRegistry( pRuntimeClass, lpszKey ) {    }
};

// Message handler registration and GetMsgHndlr method in CMsgHndlr
static CMsgHndlrRegistry< CMsgHndlr > gMsgHndlr( RUNTIME_CLASS( CMsgHndlr ), 
    "MSGHNDLR" );
CMsgHndlr * CMsgHndlr::GetMsgHndlr( LPCSTR lpszKey )
{
    CRuntimeClass * pRuntimeClass = NULL;
    if( CObjRegistry::GetRuntimeClass( lpszKey, pRuntimeClass ) == FALSE )
    {
        return NULL;
    }
    // Call GetMsgHndlr in listing 3 with runtime class information
    return GetMsgHndlr( pRuntimeClass );
}

// Message handler registration code in CSmpHndlr
static CMsgHndlrRegistry< CSmpHndlr > gSmpHndlr( RUNTIME_CLASS( CSmpHndlr ), 
    "SMPHNDLR" );
// Application code to select the message handler
CMsgHndlr * SelectMsgHndlr( LPCSTR lpszKey )
{
    return CMsgHndlr::GetMsgHndlr( lpszKey );
}

Listing 6 - Smart Singletons

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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

T. Kulathu Sarma

Switzerland Switzerland
Kulathu Sarma is working as a Technology Manager for GoldAvenue, a company based in Geneva, Switzerland and responsible for supporting e-business initiatives of GoldAvenue in B2B and B2C Sectors. He has been programming in C/C++ for 9 years and actively working on Patterns for the past 5 years.

Comments and Discussions

 
GeneralProblem with &quot;Listing 1a&quot; PinmemberFrancois C6-Nov-03 8:15 
GeneralThread SafeSingleton PinmemberPaul Evans20-Dec-02 23:02 
QuestionOther way? PinmemberAdrian Bacaianu22-May-02 2:14 
QuestionBetter Smart Singleton? PinmemberAnonymous5-Sep-01 6:25 
AnswerRe: Better Smart Singleton? PinmemberHal Angseesing19-Dec-05 2:42 
GeneralCatch 22 question PinmemberAnonymous15-Aug-01 10:58 
GeneralRe: Catch 22 question PinmemberAnonymous15-Aug-01 11:54 
GeneralRe: Catch 22 question PinmemberDave Handley8-Nov-04 5:49 
GeneralHmm PinmemberAnonymous10-Apr-01 19:57 
Is this text copied verbatim right from the book?
Questionis it realy singleton ? Pinmemberparti3an12-Mar-01 4:38 
AnswerRe: is it realy singleton ? PinmemberKulathu Sarma13-Mar-01 1:03 
AnswerRe: is it realy singleton ? PinmemberAnonymous15-Aug-01 12:26 
AnswerRe: is it realy singleton ? PinmemberEastMohican3-Oct-05 20:09 
GeneralRe: is it realy singleton ? PinmemberHal Angseesing19-Dec-05 2:37 
AnswerRe: is it realy singleton ? PinmemberWillem.D.4-Dec-06 10:49 
QuestionAnother side of "is it realy singleton ?" PinmemberDave Heinemann4-Dec-06 22:36 
Questionwhat is going wrong with destructors ? PinmemberKurian26-Jan-01 11:14 
AnswerRe: what is going wrong with destructors ? PinmemberKulathu Sarma29-Jan-01 2:46 
GeneralRe: what is going wrong with destructors ? PinmemberMick16-Feb-01 6:21 
GeneralRe: what is going wrong with destructors ? Pinmemberbosu12-Apr-01 15:05 
General:confused: Why templates???:confused: PinmemberLeo Rosa16-Jan-01 23:55 
GeneralRe: :confused: Why templates???:confused: PinmemberKulathu Sarma18-Jan-01 22:47 
GeneralRe: :confused: Why templates???:confused: PinmemberLeo Rosa18-Jan-01 23:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141223.1 | Last Updated 9 Jan 2001
Article Copyright 2001 by T. Kulathu Sarma
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid