Singleton Pattern in C++

Posted on 11 Jul 2018 in C++ • 3 min read


In 2008 I've learned about singleton during my undergraduate studies. At that time I tried to learn all the design patterns of the software development techniques. The reference book was the "Design Patterns: Elements of Reuseable" by Erich Gamma, John Vlissides, Ralph Johnson, and Richard Helm. The book describes about the motivation, the concept and provides some general example codes. Even though I did several exercises, it was very difficult to fully understand and to keep it in my brain. After involving in several professional projects in the last 8 years, I can understand why the design patterns are significant. This time, after I wrote a singleton codes for a big project, I would like to write and document my experience regarding the singleton design pattern.

Beginner Stage

In my beginner phase as a student and a newbie software engineer, it was hard to imagine the connection between the practical implementation and the concept of the design patterns. Just like mastering other stuffs, it needs time and process.

So lets we look at the singleton tutorial from sourcemaking: https://sourcemaking.com/design_patterns/singleton/cpp/1. The tutorial gives us a very good example and the impact of applying singleton pattern.

Advance Stage

The main issue of the simple singleton implementation is the deletion step in the destructor. Most of the tutorials ignore this issue which is very very crusial for a production code. As we can check from the above link from sourcemaking.com, a heap allocation with new is always conducted by the static singleton function. All C++ newbie know that an object created by new has to be deleted later, otherwise we will cause a memory leak in our system. We can read a posted question regarding this issue in this stackoverflow question.

There are two advance singleton patterns that can deal this issue: the Gamma based and the Mayers based singleton pattern.

Gamma Singleton

The Gamma singleton approach modifies the member pointer variable to become a member variable which is created in the construction. This approach allows If the instance function is called, it returns the object reference.

An example of the Gamma singleton:

// SingletonHandler.hpp
class SingletonHandler
{
 public:
    // Singleton instance func
    static SingletonHandler* instance();
 private:
    // Hiding the constructor and the assignment operator
    SingletonHandler();
    SingletonHandler(const SingletonHandler&);
    SingletonHandler& operator=(const SingletonHandler&);
    ~SingletonHandler();

    static SingletonHandler m_instance;
}

// SingletonHandler.cpp
static SingletonHandler* SingletonHandler::instance()
{
    return &m_instance;
}

The above code is not yet optimal. We observe that the singleton variable m_instance is always created if the class is initialized. The singleton variable shall be created only if another component calls the instance().

Mayers Singleton

The main difference of the Mayers approach compared Gamma is that the Mayers singleton puts the declaration of the singleton variable in the static instance() function, not in the private class declaration area. Below is an example code of this approach:

// SingletonHandler.hpp
class SingletonHandler
{
 public:
    // Singleton instance
    static SingletonHandler& instance();
 private:
    // Hiding the constructor and the assignment operator
    SingletonHandler();
    SingletonHandler(const SingletonHandler&);
    SingletonHandler& operator=(const SingletonHandler&);
    ~SingletonHandler();
}

// SingletonHandler.cpp
SingletonHandler& SingletonHandler::instance()
{
    static SingletonHandler sh_instance;
    return sh_instance;
}

As long as the instance() function is not called in the process, the static singleton object will not be created after the class is declared.

References

  1. sourcemaking.com: 1 2
  2. stackoverflow
  3. devarticles

c++