I work in a project where Singletons are usually implemented like this:
class Singleton
{
public:
static Singleton& get();
virtual ~Singleton() = default;
// Used for unit tests
static void reset();
protected:
static std::unique_ptr<Singleton>& instance();
};
unique_ptr<Singleton>& Singleton::instance()
{
static unique_ptr<Singleton> instance;
return instance;
}
Singleton& Singleton::get()
{
auto& instance = instance();
if (!instance) {
// We cannot use make_unique<T>() as constructor is private
instance = unique_ptr<Singleton>(new Singleton());
}
return *instance;
}
void Singleton::reset() { instance().reset(); }
// Private constructor
Singleton::Singleton() {}
No thread safety is required here.
Is there any advantages of using a static unique_ptr
?
What are the consequences of creating the Singleton with unique_ptr<T>(new T())
?
Since our Singletons can carry (some) global state, a public reset() was implemented for testing purposes, is it the only way and can this be improved ?
I have found some examples of C singleton design patterns here. But never implemented with unique_ptr like mine.
CodePudding user response:
There is no need to use std::unique_ptr. Use references
class Singleton
{
public:
static Singleton& instance();
virtual ~Singleton() = default;
// Used for unit tests
void reset();
private:
Singleton();
int stuff;
};
Singleton& Singleton::instance()
{
static Singleton instance;
return instance;
}
void Singleton::reset() { stuff = 0; }
// Private constructor
Singleton::Singleton() {}
CodePudding user response:
Note that this design is dominated by your requirement for a reset
function. This prevents you from using certain Singleton implementations, like e.g. Meyer's Singleton.
In general you would want a singleton implementation to not allow the implementation of a reset
method, because now your singleton is not really a singleton anymore. But it might of course make sense to "water down" the singleton for testing purposes.
If multithreading-safety is not a concern, your singleton implementation is overly complicated and could be reduced to:
class Singleton
{
public:
static Singleton& get() {
if (!mInstance) {
mInstance = std::unique_ptr<Singleton>(new Singleton());
}
return *mInstance;
}
static void reset() { mInstance.reset(); }
private:
static inline std::unique_ptr<Singleton> mInstance{};
};
CodePudding user response:
There are exists two ways to reset Meyer's singleton:
#include <new>
class Singleton
{
public:
static Singleton& instance() {
static Singleton instance;
return instance;
}
virtual ~Singleton() = default;
// Option 1 - easy and convinient if Singleton is copyable or movable
void reset1() {
*this = {};
}
// Option 2 - works always
void reset2() {
this->~Singleton();
new (this) Singleton;
}
private:
Singleton() {}
};
2nd option is basically doing exactly what you are doing with releasing/creating std::unique_ptr
, except without deallocation/allocation of storage.