Home > other >  Is Singleton with static unique_ptr a good practice
Is Singleton with static unique_ptr a good practice

Time:01-27

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.

  • Related