Jump to content
  • Advertisement

Archived

This topic is now archived and is closed to further replies.

ANSI2000

Making Singleton pattern clearer.

This topic is 6189 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

Ok, I understand the Singleton pattern when it should be used and how to implement it, but am not to sure ho wto implement it within the rest of my application and classes. Let''s say I have a class called Logs. This class will write to file for loging purposes.
  
class Logs
{
   public:
      static Logs getInstance(); // If Logs is NULL, create new instance and return it, if not return already created instance.


      void init(char *pLogsLocation, char *pFileName); // Set the location and file name where the logs should be set.


      void writeLog(char *pMessage); // Write a message to the log


      ~Logs() // Destructor


   private:
      Logs(); // Constructor


      static Logs myLogs;
      char logsLocation[128], fileName[128]; 
}
  
Do init(), logsLocation[128], fileName[128] have to be static? Also how would I instanciate, intilaize the logs class to be used within my application and other classes. Woudl I have to declare and Logs object per class and instatiate each one by using getInstance? And will I have to call init() over and over again, am asuming init has to be called only once...
  

void main()
{
    Logs myLogs = Logs::getInstance()
    myLogs.init("c:\\logs", "logfile.txt");

    MyClass myClass;

    myClass.init();
}



class MyClass
{
   public:
      init()
      {
         myLogs = Logs::getInstance();
         myLogs.init("bla", "bla"); // Should this be done?

      }

   private:
      Logs myLogs;
}


  

Share this post


Link to post
Share on other sites
Advertisement
This is how I do a singleton:

    
class Logs
{
public:
static Logs& getInstance ()
{
assert(singleton != 0);
return *singleton;
}
Logs ()
{
assert(singleton == 0);
singleton = this;
}
~Logs ()
{
singleton = 0;
}
void init (char *pLogsLocation, char *pFileName);
void writeLog (char *pMessage);
void destroy();
private:
static Logs* singleton;
};
// cpp file

Logs* Logs::singleton = 0;


Then, I create & initialize the singleton before entering the program loop

  
int main ()
{
Logs logs();
logs.init("c:\\logs", "logfile.txt");
while (programRunning)
{
// ...Do cool stuff...

someFunc();
// ...Do more cool stuff...

}
logs.destroy();
return 0;
}
//

void someFunc ()
{
Logs::getInstance().writeLog("In someFunc()");
}


I like doing it this way because it clears up who actually owns the singleton, when it gets created, initialized, and destroyed. You create it, initialize it, and from then on use Logs::getInstance() like it was a global object (you can even #define g_Logs Logs::getInstance() and use that). Nothing besides Logs::singleton & Logs::getInstance() should be static.

Just remember that you're going to want to return a pointer or a reference to your singleton from getInstance(), otherwise you will actually copy the singleton when you return it, which doesn't make it very single.

FYI: Game Programming Gems II has a good chapter on this and how to implement automatic singletons with a template base class.

"So crucify the ego, before it's far too late. To leave behind this place so negative and blind and cynical, and you will come to find that we are all one mind. Capable of all that's imagined and all conceivable."
- Tool
------------------------------


Edited by - wayfarerx on December 26, 2001 1:13:32 PM

Share this post


Link to post
Share on other sites
Right, so a better way is to make the constructor private, ensuring compile-time error over run-time error. The instance is static in the getInstance(), ensuring lazy initialization, and no new/deletes.
      
class Logs
{
public:
static Logs& getInstance ()
{
static Logs instance;
return instance;
}
void init (char *pLogsLocation, char *pFileName);
void writeLog (char *pMessage);
void destroy();
private:
Logs () { }
~Logs () { }
};



Mike

Share this post


Link to post
Share on other sites
The way I know it is like this....

    
class Logs
{
public:
static Logs &getInstanc()
{
if(myLogs == NULL)
myLogs = new Logs();

return(myLogs);
}

void init(char *, char *);
void writeLog(char *);

/* Is this ok?*/
void destroy()
{
if(myLogs != NULL)
delete myLogs;
}

private:
Logs();
~Logs();

static Logs *myLogs;
}


Am not tu sure about destroy though....

Edited by - ANSI2000 on December 26, 2001 6:36:06 PM

Share this post


Link to post
Share on other sites
If you make the [dtor] private, then you need to have the sigleton suicide on a reference count release (otherwise, no one can destroy it).

Instead of Init and destroy, try something like GetInstance and ReleaseInstance.

[meant destructor not constructor]

Edited by - Magmai Kai Holmlor on December 28, 2001 10:54:27 AM

Share this post


Link to post
Share on other sites
I thought that for the singleton pattern the constructor must be private. If not anyone can make an instance. You sure you wherent talking about the destructor?

Share this post


Link to post
Share on other sites
One thing you can do to make your design clearer, is to move the Singleton code out of the class and put it in a template. Most implementations look like so (minus the reference counting Magmai suggested, for clarity sake):

      
template<class ClassType>
class Singleton
{
public:
static ClassType &GetInstance()
{
if (!m_instance)
m_instance = new ClassType;

return *m_instance;
}

protected:
static ClassType *m_instance;
};

template<class ClassType>
ClassType *Singleton<ClassType>::m_instance = NULL;

You'd then just derive your new class from this Singleton class like so:


class Log : public Singleton<Log>
{
protected:
Log() {}
~Log() {}
public:
// Insert interface here

}


This solves the problem of having to retype all that same code for every class you want to be a Singleton. However, this class suffers from the same thing the original implementation does, that you can't derive a new class from Log because you can't re-implement the static GetInstance function to return your new derived class.

However, if you make your Log class a friend of the Singleton rather than deriving from Singleton, you keep all the previous advantages and gain the ability to inherit since your Log class will no longer has static functions. The only change from the previous example would be to the Log class itself:

  
class LogSingleton
{
friend Singleton<Log>;

protected:
LogSingleton() {}
~LogSingleton() {}
public:
// Insert interface here

}

You can typedef your class like so:


typedef Singleton<LogSingleton> Log;


As far as the reference counting that Magmai suggested, I'm not so sure about. If everytime you want to log something you must call GetInterface then ReleaseInterface. This may not be a large issue in some places, but if you use a singleton in a tight loop it will kill performance. You'd have to be aware of this and code around it by getting a pointer to the object before the loop, use it, then release it after the loop. Too much hassle, imo. Also, every time your class was destroyed you'd lose any state information the class previously contained.

I personally create a Destroy function in my Singleton class and specifically call Destroy() on all singleton objects just before the exit of my program. This way everything gets deleted in the order I want and I don't lose state information.

It's not a perfect solution, but the best I can come up with.


- Houdini

Edited by - Houdini on December 26, 2001 12:03:59 AM

[edit: the board garbles multiple source tags on edits, fixed them]

Edited by - Magmai Kai Holmlor on December 28, 2001 6:07:50 AM

Share this post


Link to post
Share on other sites
You dont really need to derive a singleton class anyways.....

I still havent gotten an answer why the constructor must be public, every article I have read, says to keep the constructor, protected or private. The destructor should remain public so you can delete all references.

So if we have...
  
class Logs
{
public:
Logs &getInstance();
void init(char *, char *);
void writeToLog(char *);
void destroy()
{
delete instance;
}
~Logs();

private:
Logs();
static Logs *instance;
}

class ClassA
{
/*
In some method...
Logs myLog = Logs::getInstance();
*/
}

class ClassB
{
/*
In some method...
Logs myLog = Logs::getInstance();
*/
}

void main()
{
Logs logs = Logs::getInstance();
logs.init("c:\temo\", "myLog.txt");

/* Instanciate and create classes A & B */
/* Call a few methods of classes A & B */

/*
Shoudl this not free up all references and pointers?
Or will this cause dangling references or pointers?
*/

logs.destroy();

}




Share this post


Link to post
Share on other sites
The ctor and dtor of singleton''s are normally made private.

The reason is this - there is one instance and you must control access to the construction and destruction of that one object.

consider the following -

  

class Singleton
{
public:

static Singleton* Instance();
static void Destroy();


Singleton();
~Singleton();

private:
static Singleton* siInstance;
};

// someone can then do the following:


delete Instance();



but the siInstance will now be pointing to a dead object. asking for disaster

consider that even if it was ''static Singleton& Instance()''

someone could write ''delete &(Instance())''

evil, yes, but making the dtor private will give a compile time error.

A compile time error is always easier to fix than a run time error. ( well, usually )

an alternative for cleanup issues is to use a simple template which calls a static Destroy method

eg

  

template <class SINGLETON>
class SingletonDestroyer
{
public:
SingletonDestroyer() { }
~SingletonDestroyer()
{
SINGLETON::Destroy();
}
};

// then put an instance of one at file scope of the singleton

// eg.


SingletonDestroyer<MySingleton> destroyIt;



That template requires the api of the singleton to have a static method Destroy()

this then ensures that the Destroy() method gets called at app exit.

Beware that the order of static destruction ( the destroyIt instance ) of objects is not guaranteed. I have seen it behave flakey when doing abnormal application exit with DLLs

But most of the time this technique works without a hitch.


Share this post


Link to post
Share on other sites
quote:
Original post by ANSI2000
You dont really need to derive a singleton class anyways.....



How can you assume that no future singletons you create will ever be derived from? That's
a mighty big assumption.

Heck, if I was using your library I might not like that your Log class doesn't timestamp events.
If all I have is a .lib and header files then I must derive a new instance to add
timestamping.

quote:
Original post by ANSI2000
I still havent gotten an answer why the constructor must be public, every article I have read,
says to keep the constructor, protected or private.



You should not make the constructor public otherwise anyone can make instances of the class.

quote:
Original post by ANSI2000
The destructor should remain public so you can delete all references.



I disagree. As a general rule, whichever class or module created the object should also
delete it. Make another static function that will delete the pointer, and call that before
your application exits.

Leaving your destructor public
allows someone to delete the pointer to your singleton class, as SteveC stated already.


BTW, forgot about making private constructor and destructor myself in my Singleton template
(it was late and I was tired =). It should look like this:

      
template<class ClassType>
class Singleton
{
private
Singleton() {}
~Singleton() {}

public:
static ClassType &GetInstance()
{
if (!m_instance)
m_instance = new ClassType;

return *m_instance;
}

static void DestroyInstance()
{
if (m_instance) {
delete m_instance;
m_instance = NULL;
}

return *m_instance;
}

protected:
static ClassType *m_instance;
};

template<class ClassType>ClassType *Singleton<ClassType>::m_instance = NULL;



- Houdini

Edited by - Houdini on December 27, 2001 6:43:30 PM

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

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

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!