Singleton problems

Started by
21 comments, last by Rebooted 18 years, 7 months ago
Hi, im having a little trouble with writing a simple singleton. My implementation is as follows:

// Singleton.h
#pragma once

template <class T>
class CSingleton
{
  public:
    static T* CreateInstance(void);
    static void DeleteInstance(void);

    static T* GetInstancePtr(void);
    static T& GetInstanceRef(void);
    
  private:
    CSingleton(void);
    CSingleton(const CSingleton& rCopy);
    CSingleton& operator=(const CSingleton& rCopy);
    ~CSingleton(void);

    static T* m_pT;
};

template <class T>
T* CSingleton<T>::m_pT = NULL;

template <class T>
CSingleton<T>::CSingleton(void)
{
}

template <class T>
CSingleton<T>::~CSingleton(void)
{
}

template <class T>
T* CSingleton<T>::CreateInstance(void)
{
  if (m_pT == NULL)
    m_pT = new T();

  return m_pT;
}

template <class T>
void CSingleton<T>::DeleteInstance(void)
{
  if (m_pT != NULL)
  {
    delete m_pT;
    m_pT = NULL;
  }
}

template <class T>
T* CSingleton<T>::GetInstancePtr(void)
{
  return m_pT;
}

template <class T>
T& CSingleton<T>::GetInstanceRef(void)
{
  return *m_pT;
}

But it has lead to problems when trying to make a logging class a singleton:

// Log.h
#pragma once

class CLog : public CSingleton<CLog>
{
  public:
    CLog(void);
    ~CLog(void);
};

// Engine.cpp
...
  CLog* pLog = CLog::GetInstancePtr();
  CLog* pLog2 = new CLog(); // Test to make sure that the user cant make a new object
...

The compiler moans that it cant access the private members __ctor and dtor in the CLogs constructor and destructor. Which obviously makes sense, but if i dont have them as private, the developer is able to call "new CLog()" which means it breaks the singleton rules. Can anyone give me any pointers where im going wrong?
Mark Ingramhttp://www.mark-ingram.com
Advertisement
So basically you took precautions for not allowing the user to create instances of the class using new. Subsequently you tried to create a new instance using new and the compiler complains.

I'd say all is fine! In more detail: you don't want to expose the constructor of the derived class either.

Greetz,

Illco
Yeah thats the thing, i dont really want to mess around with all the child classes, i want to disable the user using new on the class without changing the original implementation except for inheriting from CSingleton.

BTW: The compiler complains from within the CLog() & ~CLog() saying it cant access the CSingleton constructor / destructor. But this *isnt* as a result of the code in Engine.cpp - its just as it tries to compile log.cpp.
Mark Ingramhttp://www.mark-ingram.com
I know. If the parent constructor is private you cannot call it in a derived class. I know see your problem appear: you want to forbid the user to even derive such a class or, on the other hand, make sure its constructors are indeed private. I'm not into templates enough to help you there; though I doubt it is possible to create a nice singleton base class using templates.
You need to hide your constructors, and then make CSingleton<Log> a friend class, so it can access them.
That would work but defeats the purpose as it requires the templated base class to know about its inheritors.
yeah, that would be a real pain having a whole list of friend classes inside the singleton template. ive resorted to using the following technique:

#define CLASS_SINGLETON(CLASS) CLASS : public CSingleton<CLASS>#define IMPLEMENT_SINGLETON(CLASS)   friend CSingleton<CLASS>;   private:     CLASS(void);     CLASS(const CLASS&);     CLASS& operator=(const CLASS&);     ~CLASS(void);


...and...
class CLASS_SINGLETON(CLog){  IMPLEMENT_SINGLETON(CLog)};


But im a bit dubious about using a define to create constructors for a class - it might not be very clear - is it better to just write them all out by hand each time?
Mark Ingramhttp://www.mark-ingram.com
OMG. In my opninion? Definitely.
template<class T>class singleton{public:    static T& instance()    {        if(!_instance)        {            _instance = new T;        }        return _instance;    }private:    static T* _instance;};


Then inherit your derviced class D from singleton<D> to make it a singleton.

However, the derived class must make its constructor private and must make singleton<D> a friend class. There is no way around this. The above is as clean as you're going to get.

NOTE:
The above is just a skeleton. Don't use it as is.
ok, ive rewritten it slightly:

// Singleton.h#pragma oncetemplate <class T>class CSingleton{  public:    static T* GetInstancePtr(void);    static T& GetInstanceRef(void);      protected:    CSingleton(void);    ~CSingleton(void);  private:    CSingleton(const CSingleton& rCopy);    CSingleton& operator=(const CSingleton& rCopy);    static T* CreateInstance(void);    static void DeleteInstance(void);    static T* m_pT;};template <class T>T* CSingleton<T>::m_pT = NULL;template <class T>CSingleton<T>::CSingleton(void){}template <class T>CSingleton<T>::~CSingleton(void){}template <class T>T* CSingleton<T>::CreateInstance(void){  if (m_pT == NULL)  {    m_pT = new T();    atexit(CSingleton<T>::DeleteInstance);  }  return m_pT;}template <class T>void CSingleton<T>::DeleteInstance(void){  if (m_pT != NULL)  {    delete m_pT;    m_pT = NULL;  }}template <class T>T* CSingleton<T>::GetInstancePtr(void){  if (m_pT == NULL)    CSingleton<T>::CreateInstance();  return m_pT;}template <class T>T& CSingleton<T>::GetInstanceRef(void){  if (m_pT == NULL)    CSingleton<T>::CreateInstance();  return *m_pT;}


// Log.h#pragma onceclass CLog : public CSingleton<CLog>{  friend CSingleton<CLog>;  private:    CLog(void);    CLog(const CLog&);    CLog& operator=(const CLog&);    ~CLog(void);};// Engine.cpp...  CLog* pLog = CLog::GetInstancePtr();  //CLog* pLog2 = new CLog(); // This doesnt work - yay!...


Any advances on this? Seems to work, no memory leaks or anything, memory is cleared up during atexit().
Mark Ingramhttp://www.mark-ingram.com

This topic is closed to new replies.

Advertisement