Jump to content
  • Advertisement
Sign in to follow this  
Skute

Singleton problems

This topic is 4828 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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?

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
That would work but defeats the purpose as it requires the templated base class to know about its inheritors.

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites
ok, ive rewritten it slightly:


// Singleton.h
#pragma once

template <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 once

class 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().

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!