c + + correct singleton mode

1. Singleton mode

Singleton mode is the most common mode. It is often used in the development process. The purpose is:
There is only one instance. This instance can only hold one globally shared data, or only hold a pile of interfaces, etc. many singleton modes can be used.

Recently, I found some common errors / problems in the process of reviewing other people's and my own code. Here are some examples

2. Specification of singleton mode

2.1 interface type of singleton mode:

1. Return reference type

Singleton &Singleton::GetInstance()
{
    static Singleton instance;
    return instance;
}

2. Return pointer type

Singleton *Singleton::GetInstance()
{
    static Singleton instance;
    return &instance;
}

Common errors:
For c + + beginners / users who have just cut into c + + language, the concept of reference / pointer may not be clear, which may be written as:

Singleton Singleton::GetInstance()
{
    static Singleton instance;
    return instance;
}

In this way, the single instance effect cannot be achieved, and a copy structure will be generated every time

I'm used to using quotation. I'll take quotation as an example later

2.2 the constructor of singleton mode must be explicitly declared as non public (preferably private)

as everyone knows. If the constructor is public, it will not achieve the purpose of singleton
Common error: forget the explicit declaration (generally not stupid enough to explicitly declare the constructor and use the singleton)

class Singleton {
public:
	static Singleton &GetInstance()
	{
	    static Singleton instance;
	    return instance;
	}
};

Because there is no declaration, others can create it externally

2.3 in singleton mode, copy construction, copy assignment, move construction and move assignment must be disabled

I once checked the code of a colleague and wrote a single example

class Singleton {
public:
	static Singleton &GetInstance()
	{
	    static Singleton instance;
	    return instance;
	}
private:
    Singleton() = default; // The constructor is explicitly declared private
};

void Func() {
    auto instance = Singleton::GetInstance();
}

Note:
auto does not automatically derive references
So what we do here is equivalent to:

auto &instanceRef = Singleton::GetInstance();
auto instance = instanceRef;

A copy structure is generated here

How to completely avoid this behavior from the perspective of code?
You need to explicitly prohibit all copying and moving related

The correct approach is as follows:

#define DISABLE_COPY_AND_MOVE(_CLASS_NAME) \
    _CLASS_NAME(const _CLASS_NAME&) = delete; \
    _CLASS_NAME& operator=(const _CLASS_NAME &) = delete; \
    _CLASS_NAME(_CLASS_NAME &&) = delete; \
    _CLASS_NAME& operator=(_CLASS_NAME &&) = delete;

class Singleton {
public:
	static Singleton &GetInstance()
	{
	    static Singleton instance;
	    return instance;
	}
private:
    Singleton() = default; // The constructor is explicitly declared private
    DISABLE_COPY_AND_MOVE(Singleton);
};

void Func() {
    auto instance = Singleton::GetInstance();
}

DISABLE_COPY_AND_MOVE explicitly deletes all four of the above
At this time, you will find
auto instance = Singleton::GetInstance();
Compile but

In this way, you can ensure that no copy or movement will occur to a single instance
Written by others
std::move / auto instance =... And other behaviors are not allowed

2.3 the destructor of a singleton class must be explicitly declared private

#define DISABLE_COPY_AND_MOVE(_CLASS_NAME) \
    _CLASS_NAME(const _CLASS_NAME&) = delete; \
    _CLASS_NAME& operator=(const _CLASS_NAME &) = delete; \
    _CLASS_NAME(_CLASS_NAME &&) = delete; \
    _CLASS_NAME& operator=(_CLASS_NAME &&) = delete;

class Singleton {
public:
	static Singleton &GetInstance()
	{
	    static Singleton instance;
	    return instance;
	}
private:
    Singleton() = default; // The constructor is explicitly declared private
    DISABLE_COPY_AND_MOVE(Singleton);
};

void Func() {
    auto &instance = Singleton::GetInstance();
    auto ptr = &instance;
    delete ptr;
}

If the pointer of a singleton is held in some way in the code usage scenario (especially unique_ptr), the pointer will be released when it is destructed

As described above, it is possible to delete this pointer maliciously.
After deleting the pointer, the single instance reference / pointer obtained by others will cause the program to crash

Correct practice:

#define DISABLE_COPY_AND_MOVE(_CLASS_NAME) \
    _CLASS_NAME(const _CLASS_NAME&) = delete; \
    _CLASS_NAME& operator=(const _CLASS_NAME &) = delete; \
    _CLASS_NAME(_CLASS_NAME &&) = delete; \
    _CLASS_NAME& operator=(_CLASS_NAME &&) = delete;

class Singleton {
public:
	static Singleton &GetInstance()
	{
	    static Singleton instance;
	    return instance;
	}
private:
    Singleton() = default; // The constructor is explicitly declared private
    ~Singleton() = default; // The destructor is explicitly declared private
    DISABLE_COPY_AND_MOVE(Singleton); // Prohibit moving and copying
};

Keywords: C++ Singleton pattern

Added by glc650 on Thu, 10 Mar 2022 15:18:33 +0200