C++ Singletons
The singleton design pattern is one of the original and oldest patterns from the mighty design patterns book, and also one of the most commonly used design patterns in the wild. The purpose of the Singleton pattern is to enforce that there can only be one instance of a particular type T. While this sounds simple enough, in practice there are various traps, especially in C++. We will present the Singleton in Modern C++ and show all the various traps along the way.
So without further ado here it is:
` So lets look at some of the obvious design decisions made to the Singleton class. Firstly all of the copy and move constructors are marked as deleted. This is fairly obvious as a we want to have a single instance only. The constructor is also made private, but why? If the constructor was public we could then create instances of the object so it has to be private. The destructor must be public so that later we can delete the object.
So if the constructor is private, how do we create instances of the Singleton class. This is done through exposing a static method instance which will create the Singleton. The instance itself is stored in a static class member instance_.
So what is the std::call_once and std::once_flag? Before going into to detail, what happens if to threads try to call
Singleton::instance? Well anything. We could end up with two objects being created and one of them will leak, or maybe
just one instance. std::call_once is from the header
The is one not so obvious problem with the current program above. Lets revist that code.
When this program runs its output is
Where is the call to ~Singleton()? Well we only have a pointer on the stack so no destructor is called. We need to use
our old friend RAII to help us out. Lets put a class together to help us out, scoped_singleton_deleter
Lets look at the code.
Now lets update main and see how this all hangs togther.
Lets now look at the output and see if this all works.
So here we have it. It all works. But what are some of the downsides of the Singleton design pattern. Well firstly once the Singleton is created, we can access the object anywhere in the program with Singleton::instance(). This makes it nice and easy for us but all makes it a nightmare to test code that uses the Singleton. We have a hidden dependency that is rampant throughout the codebase. Also it is essentially a global variable and it makes code much harder to reason about.
In future articles we will look at variations of this pattern.
Let me know what you think of this article in the comment section below!