3

I already have some singleton classes which uses pointers:

class Logger
{
public:

    static Logger* Instance()
    {
        if (!m_pInstance) m_pInstance = new Logger;
        return m_pInstance;
    }
private:
    Logger();
    Logger(Logger const&);
    Logger& operator=(Logger const&);
    static Logger* m_pInstance;
};

But, there is a simpler way, using references:

class Logger
{
public:
    static Logger& Instance()
    {
        static Logger theLogger;
        return theLogger;
    }
private:
    Logger();
    Logger(Logger const&);
    Logger& operator=(Logger const&);
    ~Logger();
};

Reading an article C++ Singleton design pattern, it warns about second way:

[Potential Pitfall]: This form of the singleton can present a problem because of the life expectancy of the object. If one singleton is instantiated within another, one must be keenly aware of the destructor call sequence.

But I can not understand it, Can someone show me a bad usage of it that I should avoid it?

Raymond
  • 60
  • 6
  • as far as i know if compiler inlines your function (in this case Instance) and if you include your header in more than one file (in your case it looks likely) you will have more than one instances (as far as i remember). Of course i am not sure if i recall correctly, so better refer to More Effective C++ by Scott Meyers. – Hayri Uğur Koltuk Mar 09 '13 at 15:18
  • The trouble with both of the above is what happens when two threads call Instance() for the first time at the same time. Pre C++11 compilers sometimes would construct theLogger twice. – brian beuning Mar 09 '13 at 15:40
  • No, that won't happen here. This is actually called Mayer' Singleton and is as safe as it can be – Paul Michalik Mar 09 '13 at 15:45
  • @AliVeli: No, you won't have more instances. You have just one inline function with just one static function-local object. – sellibitze Mar 09 '13 at 16:30
  • I think this question was already answered: http://stackoverflow.com/questions/2496918/singleton-pattern-in-c – Maciek B Mar 09 '13 at 17:02

1 Answers1

7

There is, indeed, an issue with both options.

Typical Singleton

class Logger
{
public:

    static Logger* Instance()
    {
        if (!m_pInstance) m_pInstance = new Logger;
        return m_pInstance;
    }
private:
    Logger();
    Logger(Logger const&);
    Logger& operator=(Logger const&);
    static Logger* m_pInstance;
};

This code is not thread-safe, therefore the call to new Logger may occur several times (in different threads). It also leaks the Logger instance (since it is never deleted), which might be an issue if a destructor should be executed.

Meyer's Singleton

class Logger
{
public:
    static Logger& Instance()
    {
        static Logger theLogger;
        return theLogger;
    }
private:
    Logger();
    Logger(Logger const&);
    Logger& operator=(Logger const&);
    ~Logger();
};

The issue with Meyer's Singleton is indeed due to the destruction of objects. After you return from main, the destructor of all globals will run, in the inverse order in which those globals were constructed 1. This is as if those globals had been constructed on a stack.

If an object was constructed prior to your Logger and attempts to use it in its own destructor; then it will used an already destructed object, resulted in undefined behavior.

1 More precisely, in the inverse order their constructor completed.


The simplest alternative is to revisit the Typical Singleton:

class Logger {
public:
    static Logger& Instance() {
        static Logger* L = new Logger;
        return *L;
    }
private:
    ...
};

This is now thread-safe, but still leaks. But actually the leak is here desired as it guarantees that this object remains alive longer than any other whose destructor will be called. Warning: if you ever had something to do in the destructor, it will never run.

There are other variations, for example Alexandrescu's Phoenix Singleton comes back from its ashes should you need it after its death; however getting both thread-safety and safe behavior during destruction is difficult to achieve.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722