I’m sure that by now, you’ve heard about Design Patterns in C++ and probably also about Singleton.
This article covers what a singleton is and possible implementations with some standard uses for it.
For detailed information and if you want to learn more about design patterns, you should check out Modern C++ Design: Generic Programming and Design Patterns Applied by Andrei Alexandrescu (which is one of the co-founders of boost).
What is a Singleton?
The Singleton might be one of the best known design patterns and probably the simplest.
It is a creational design pattern and it ensures that only a single instance of a C++ entity is present in a given program and that instance has only one entry point. Basically, you cannot have 2 or more instances of the same Singleton class in the application. It will be initialized exactly once in the entire application.
The Singleton is mostly used for loggers, for example.
Pros and cons
The Singleton has mostly the same pros and cons as a global variable.
Pros:
- It is very handy
- Can get rid of duplicated code
Cons:
- Breaks modularity
- Hard to test and/or untestable
There are also some arguments that Singleton is actually an anti pattern because of the cons above and some other reasons.
Naive Singleton implementation
So, implementing a “naive” Singleton is quite easy. Basically, what you have to is to have a static creation method and forbid the creation of it from external sources.
class NaiveSingleton
{
private:
static NaiveSingleton* singleton_;
// Instance should not be constructible by external means
NaiveSingleton() = default;
public:
// Instance should not be copyable
NaiveSingleton (const NaiveSingleton&) = delete;
NaiveSingleton& operator=(const NaiveSingleton&) = delete;
// Provide a function to retrieve the singleton
static NaiveSingleton* instance()
{
if (singleton_ == nullptr)
singleton_ = new NaiveSingleton();
return singleton_;
}
void doSomething()
{
// doSomething here
}
};
// Static variable should be initialized outside the class
NaiveSingleton* NaiveSingleton::singleton_ = nullptr;
// Usage NaiveSingleton
NaiveSingleton* naiveSingleton = NaiveSingleton::instance();
naiveSingleton->doSomething();
Biggest problem of the “Naive” Singleton is that it is not thread safe. There is no mechanism whatsoever to protect our implementation in case of concurrent access.
One way of doing this, is to add a mutex, but we won’t cover that in this article. It’s pretty simple, just add a std::mutex and lock/unlock it when using the resource(the Singleton instance).
Scott Meyer’s Singleton implementation
This is the proper way of implementing a singleton and it’s also thread safe.
There is a very good explanation given here:
Attributed to Scott Meyers, this singleton pattern exploits three important properties:
1.Static function objects are initialized when control flow hits the function for the first time.
2.The lifetime of function static variables begins the first time the program flow encounters the declaration and ends at program termination.
3.If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.
class SMSingleton
{
private:
// Instance should not be constructible by external means
SMSingleton() = default;
public:
// Instance should not be copyable
SMSingleton (const SMSingleton&) = delete;
SMSingleton& operator=(const SMSingleton&) = delete;
// Provide a function to retrieve the singleton
static SMSingleton& instance()
{
static SMSingleton inst;
return inst;
}
void doSomething()
{
// doSomething here
}
};
// Usage SMSingleton - Scott Meyer's Singleton
SMSingleton& smSingleton = SMSingleton::instance();
smSingleton.doSomething();
Source Code
You can find the full source code on my github here.
I will probably update the repository further with more design patterns in C++, in the future.
You can find information on how to build the project in the README file of the github repository.
Pingback: Observer Design Pattern in C++ - cppdev