Jump to content
  • Advertisement
Sign in to follow this  
Mantear

C++ Singleton questions

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

Greetings! I came across the need for a logger in my program and decided to create my own as a learning exercise. This brought me to start learning about Singletons. I've read enough to get the logger working, but now I have some questions about singletons. 1) Is there an agreed upon style to how singletons are implemented? I've seen a couple different methods, with each example promoting its own method over others. Here's the basics of what I'm using.
// Logger.h
class Logger
{
public:
    static Logger*  GetLoggerHandle();
    void            Destroy();
protected:
    Logger();
    ~Logger();
private:
    static Logger* m_pInstance;
};

// Logger.cpp
Logger* Logger::m_pInstance = NULL;

Logger::Logger()
{
    // Initialize stuff here...
}

Logger::~Logger()
{
    // Does nothing right now...
}

Logger::GetLoggerHandle()
{
    if (m_pInstance == NULL)
    {
        m_pInstance = new Logger;
    }
    return m_pInstance;
}

Logger::Destroy()
{
    if (m_pInstance != NULL)
    {
        delete m_pInstance;
    }
    m_pInstance = NULL;
}

Some other methods have a slightly different GetLoggerHandle() implementation. Some examples make the constructor private, others protected. Most examples don't show a destructor at all. Comments? 2) How do the mechanics of the singleton work? I'm guessing it has to do with the static member function GetLoggerHandle() that makes it all possible, but I don't have a good knowledge of static member functions/variables. What are the implications/rules for static members of a class? 3) Were singletons an intentional design construct when the C++ standard was created? It seems too slick of a mechanism to have been an accident, but it also seems somewhat incomplete to have been done on purpose (mainly, the clean-up of the singleton at the end of the program is messy). Are singletons simply an extention of the intent of static members? 4) What are the pitfalls of using singletons? I'm aware of a couple. The deletion of the singleton at the end of the program needs special attention (the class takes care of new-ing itself, but somebody else has to delete it) and it can be tricky to make sure it is thread-safe (which isn't a big deal atm since I'm only working single-threaded). Are there any other issues of which I should be aware of? 5) Some examples mention making multiple instances of a singleton available. Each call to GetLoggerHandle() would create a new instance (up to a pre-defined number if so desired). What is the advantage/use of doing such a thing? I can't think of a situation where this would be needed where you wouldn't use a standard class. 6) The singleton is my first real look at design patterns. I like it because it is easy to implement, extremely usefull, and easy to integrate into an existing program. Are there any other patterns that fall into that catagory? Thanks in advance! EDIT: I can't spell Destory, er, Destroy.

Share this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by Mantear
1) Is there an agreed upon style to how singletons are implemented?

A lot of knowledgeable people around here agree that singletons shouldn't be implemented at all. [wink]

Quote:
Some examples make the constructor private, others protected. Most examples don't show a destructor at all. Comments?

If you need a custom destructor, you have to write it. Just like with any other class. If you don't need anything special, you can leave it out.

As for the constructor, the point is simply that it should be made unavailable outside the class. If it's made protected, other classes can inherit from your singleton class and then be able to instantiate it, which may or may not be desirable. If you make it private, that's not possible.

Quote:

2) How do the mechanics of the singleton work? I'm guessing it has to do with the static member function GetLoggerHandle() that makes it all possible, but I don't have a good knowledge of static member functions/variables. What are the implications/rules for static members of a class?

They belong to the class as a whole, rather than individual instances. That is, all instances of the class can access the same static members. It can also be accessed directly from the class, without first instantiating it. (That is, you can do something like this: "MyClass::someMember" instead of first creating an instance of the class (MyClass m;) and then accessing the member (m.someMember)

That's part of the trick. The other half is the private (or protected) constructor, which means no one but the class itself can instantiate it.

So normally, a class with a private constructor would be pretty useless, because you'd be unable to call the constructor in the first place.
But if you have a public static member function, that changes matters. It belongs to the class, which means it can access private members. So this function can check if a static pointer is already initialize with an instance of the class, and if it is, return it. Otherwise, it can call the (private) constructor and create an instance to store in the static pointer.

That way you can be sure to only ever have one instance of your class. The first time your GetLoggerHandle() function is called, it constructs an instance of the class. The second time, it sees that m_pInstance is already initialized, so it simply returns that instead of constructing a new instance.

Quote:

4) What are the pitfalls of using singletons? I'm aware of a couple. The deletion of the singleton at the end of the program needs special attention (the class takes care of new-ing itself, but somebody else has to delete it) and it can be tricky to make sure it is thread-safe (which isn't a big deal atm since I'm only working single-threaded). Are there any other issues of which I should be aware of?

Mostly design issues. They're basically little more than global variables, and are bad for the same reasons. Allowing every part of your program to access a variable is really sloppy, and makes it almost impossible to refactor or change your design later. (Because by now, all your code is filled with Logger::GetLoggerHandle() calls). They're very hard to test separately, because you have so little control over when and how they're instantiated.
And usually the "advantage" they offer isn't really an advantage at all.
How often do you *need* exactly one instance of a class to exist? How often can you be *absolutely sure* that you'll never need two? And how often will all hell break loose if two instances were constructed? (And how hard is it really to manually make sure that you only instantiate your class once anyway?)
And how often do you *need* global access to a variable? (Well, quite often, possibly, but then the reason is bad design, rather than an actual need)

You might want to check Washu's journal here for a couple of articles about the problems with "singletonitis"

Share this post


Link to post
Share on other sites
Singleton is (suprisingly) very difficult to implement in C++ without bugs.

Your implementation has a number of problems:
- it isn't thread safe (which, to be fair, you may not care about)
- it suffers from static construction order issues (that is m_pInstance may not get initialized to 0 before GetLoggerHandle() is first called, if other globals/singletons/statics use the logger)
- the logger can be used and recreated after it's destroyed - so it doesn't correctly implement the one-instance property of Singleton
- the logger leaks memory if Destroy() isn't explicitly called, which isn't a good idea

Quote:

Is there an agreed upon style to how singletons are implemented? I've seen a couple different methods, with each example promoting its own method over others. Here's the basics of what I'm using.

It's usually implemented by having the class that's to be a singleton inherit from a singleton base template:

class logger : public singleton<logger>
{
friend class singleton<logger>;
};




Quote:

3) Were singletons an intentional design construct when the C++ standard was created? It seems too slick of a mechanism to have been an accident, but it also seems somewhat incomplete to have been done on purpose (mainly, the clean-up of the singleton at the end of the program is messy). Are singletons simply an extention of the intent of static members?

They weren't considered during the creation of C++ afaik - the singleton mechanism seems to have been arranged around C++ syntax as the two singleton concepts (single intstance and global access point) really have very little to do with each other, apart from the fact that they match up nicely with statics.


Quote:

4) What are the pitfalls of using singletons? I'm aware of a couple. The deletion of the singleton at the end of the program needs special attention (the class takes care of new-ing itself, but somebody else has to delete it) and it can be tricky to make sure it is thread-safe (which isn't a big deal atm since I'm only working single-threaded). Are there any other issues of which I should be aware of?

Singletons are global variables with an added guarentee that only one instance of the type can be created. They suffer from all the problems that globals suffer from.

Quote:

5) Some examples mention making multiple instances of a singleton available. Each call to GetLoggerHandle() would create a new instance (up to a pre-defined number if so desired). What is the advantage/use of doing such a thing? I can't think of a situation where this would be needed where you wouldn't use a standard class.


It's not really a singleotn if there's more than one. A multiple instance singleton gives you nothing a global variable doesn't.

Quote:

6) The singleton is my first real look at design patterns. I like it because it is easy to implement, extremely useful, and easy to integrate into an existing program. Are there any other patterns that fall into that catagory?

It's widely considered an anti-pattern. IMO, a good pattern to start with is Iterator, because it's simple, used throughout the standard library and very powerful. It's also a design pattern that many languages (not just C++) use.


Think carefully about your design - is your singleton really doing anything that a global variable isn't? Or does it just 'feel' more OO?

Share this post


Link to post
Share on other sites
Quote:
It's also a design pattern that many languages (not just C++) use.

Singletons are widely used in most OOP languages (in particular, Java and C# programmers seem to love them)
Of course that's not meant to defend singletons at all. As you said, it's often (and rightfully so) considered an antipattern.

Share this post


Link to post
Share on other sites
Alright, I never comment in these things, because I always come in halfway. However...

Be wary of people that tell you something is to be avoided at all costs. Now also be wary of Singletons. They are a plague. There is a single and unyielding case where a Singleton is the correct choice. That is where you want: To store state information, global access, and one, and only one, like, really sure you want only one, instance of this. I've only ever used a Singleton as the entry point to the Operating System, as it satisfies all three points. Anything that doesn't satisfy all three should not be a Singleton. I've never used a logging system, so I would first research existing implementations (hint), but if I were to, off the bat, I probably would use a Singleton, at least as an entry point, and allow multiple logging targets through that (as people's arguments against a Singleton for a logging system have tended to point out limitation of only one target - a fear you can put to rest if you plan at the outset). However, you could also not create it as a Singleton, and just have one global variable.

My main point is, nothing is black and white. Except, like, black and white. You should design your system first no matter what, though. Most people abuse Singletons, leading to a general disdain amongst experienced programmers (ie, programmers who've fixed other's mess). As I've said, I've never found a proper use save for an OS entry-point - but I've never written a logging system either. I'd first research it, and then design accordingly.

Share this post


Link to post
Share on other sites
Quote:
Original post by Spoonbender
Quote:
Original post by Mantear
1) Is there an agreed upon style to how singletons are implemented?

A lot of knowledgeable people around here agree that singletons shouldn't be implemented at all. [wink]

In the case of a logger, where it would be ideal to have it be accessable from every location within my code, how is this best accomplished without using a singleton?

Quote:
Nitage
Think carefully about your design - is your singleton really doing anything that a global variable isn't? Or does it just 'feel' more OO?

I'm not denying that using the singleton is basically using a global variable. If using a singleton is determined to be such an awefull thing, what's the best alternative? I understand the points made so far as to why they could be dangerous/bad to use, but those issues don't seem very difficult to overcome as long as the situations where they are used are limited and well defined.

One advantage I can see right now for using a singleton logger is that it can easily drop into another project without much hastle. Obviously, the output method would need to be modified as needed (writing to a file? to an output stream? etc), but all the message capturing methods could remain the same.

I guess the bottom line is, if singletons are really so horrible, how should I implement my logger instead?

Share this post


Link to post
Share on other sites
Quote:
Original post by Mantear
Quote:
Original post by Spoonbender
Quote:
Original post by Mantear
1) Is there an agreed upon style to how singletons are implemented?

A lot of knowledgeable people around here agree that singletons shouldn't be implemented at all. [wink]

In the case of a logger, where it would be ideal to have it be accessable from every location within my code, how is this best accomplished without using a singleton?

Quote:
Nitage
Think carefully about your design - is your singleton really doing anything that a global variable isn't? Or does it just 'feel' more OO?

I'm not denying that using the singleton is basically using a global variable. If using a singleton is determined to be such an awefull thing, what's the best alternative? I understand the points made so far as to why they could be dangerous/bad to use, but those issues don't seem very difficult to overcome as long as the situations where they are used are limited and well defined.

One advantage I can see right now for using a singleton logger is that it can easily drop into another project without much hastle. Obviously, the output method would need to be modified as needed (writing to a file? to an output stream? etc), but all the message capturing methods could remain the same.

I guess the bottom line is, if singletons are really so horrible, how should I implement my logger instead?


There's a C++ standard library variable called std::clog that you could use. You can redirect clog to any std::ostream (including a custom class that's derived from ostream).

If that doesn't meet your needs, then a simple global variable would be preferable to the singleton.

Share this post


Link to post
Share on other sites
Quote:
Original post by Mantear

In the case of a logger, where it would be ideal to have it be accessable from every location within my code, how is this best accomplished without using a singleton?


No, that's not ideal. That means that every single little module of your code now needs that logger in every single project you'll ever use it in.

Use clog/cerr or provide each module with a logger as a configurable parameter (use the strategy pattern rather than the singleton).

Share this post


Link to post
Share on other sites
Quote:
Original post by _goat
Be wary of people that tell you something is to be avoided at all costs. Now also be wary of Singletons. They are a plague. There is a single and unyielding case where a Singleton is the correct choice. That is where you want: To store state information, global access, and one, and only one, like, really sure you want only one, instance of this. I've only ever used a Singleton as the entry point to the Operating System, as it satisfies all three points.

Does it? Care to explain why on earth you'd want *global* access to the OS?

Quote:

My main point is, nothing is black and white.

But in some cases, it can be a good idea to pretend. [wink]
I'd rather say "Singletons are evil", and then, if I one day end up needing one, just accept that I have to do something ugly, than start out saying "Singletons are sort of most of the time a kinda bad idea", because that leaves the door open to way too much abuse.

I think it's a cleaner solution to start out saying "Singletons are bad. Avoid them". Of course, if you can't avoid them, then you can consider using one, but at least, you're starting out with a healthy dose of skepticism [grin]

Share this post


Link to post
Share on other sites
I do also have a question concerning singletons:
I usually use them when I have some kind of "Manager" implementation like a texture manager or a state manager or whatever. This is convenient, because I can access my textures from 'everywhere'. If singletons are so bad, what would be the alternative to it, concerning my texture manager problem, for example?

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.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!