Singleton Pattern - Lazy Man, Hungry Man

1. Singleton mode: Singleton mode refers to ensuring that a class can only generate one instance during the entire system life cycle to ensure the uniqueness of the class. (A design pattern that creates objects only once in memory.) 2. Why is there a singleton pattern? The singleton mode is to ensure the thread safety of the program.

What is thread safety? In a program with multiple threads executing shared data in parallel, thread-safe code will ensure that each thread can execute normally and correctly through the synchronization mechanism, and no unexpected situations such as data pollution will occur.

How to ensure thread safety? Add a lock to shared resources to ensure that each resource variable is occupied by at least one thread at a time. Let threads also own resources without sharing resources in the process. For example: Use threadlocal to maintain a private local variable for each thread.

3. Application scenarios: server objects serving multiple clients, database connection pools, multi-thread thread pools, application log applications, operating system file systems, windows (task manager, recycle bin)

4. Features of the singleton pattern: 1) Set the constructor to private so that it cannot create objects externally 2) Delete the copy constructor to prevent external copying and ensure the uniqueness of the instance 3) Delete the assignment operator overloaded function, for singletons A simple type of example pattern (a class type with no pointers, no dynamic resource acquisition, no virtual functions, and no mutexes), which can be int, char, etc., so the object can be constructed through assignment to prevent its external assignment to ensure that the instance uniqueness.

class Object
{
private:
    int value;
private:
    Object(int x = 0) :value(x) { cout << "create object" << endl; }//让其在外部不能创建对象
public:
    /*面试:如果不删除这两个函数会怎样?*/
    /*编译器在编译的时候会产生缺省的拷贝构造函数和赋值函数*/
    /*面试:如果不写这两个函数会怎样?*/
    /*编译器会产生缺省的拷贝构造函数和赋值函数*/
    /*空类型有六个缺省的函数:1.构造 2.析构 3.拷贝构造 4.赋值 5.取地址符 6.const取地址符*/
    Object(const Object&) = delete;//删除拷贝构造,防止对象通过拷贝构造创建对象
    Object& operator=(const Object&) = delete;//删除赋值,防止简单类型通过赋值创建对象
public:
    ~Object() { cout << "destroy object" << endl; }//防止某些情况不能正常释放,所以不设置成私有
}; 

5. Implementation of singleton pattern:

1) Hungry Man Mode: The singleton class object has been created during class loading and is waiting to be used by the program.

2) Lazy mode: Create the singleton class object only when you really need to use the object

The difference between modes

Hungry mode

//饿汉模式(饥汉模式)
1.将构造函数设置为私有的,删除拷贝构造和赋值函数,防止在外部创建对象且保证对象仅被创建一次
2.类内设置静态对象,在类外将此对象实例化(初始化)
3.类中有一个获取实例的静态方法,可以全局访问
class Object
{
private:
    int value;
    //Object objx;//error,是不完整类型,因为类类型中不能出现类类型自身的的对象,在构建对象时会引起无穷递归
    static Object objx;//不是实例对象的成员,因为静态成员只有一份,被所有对象共享,不存在某个对象的独立空间中
private:
    Object(int x = 0) :value(x) { cout << "create object" << endl; }//让其在外部不能创建对象

public:
    Object(const Object&) = delete;//删除拷贝构造,防止对象通过拷贝构造创建对象
    Object& operator=(const Object&) = delete;//删除赋值,防止简单类型通过赋值创建对象
public:
    ~Object() { cout << "destroy object" << endl; }//防止某些情况不能正常释放
    //void SetValue(Object* const this,int x=0)
    void SetValue(int x = 0) { value = x; }
    //int GetValue(const Object* const this)
    int GetValue() const { return value; }
    static Object& GetObject()//此时必须引用返回,因为如果值返回,产生将亡值需要调用拷贝构造,但是我们已经将拷贝构造删除了
    {
        return objx;//获取实例的静态方法
    }
};

Object Object::objx(10);//静态成员在类外初始化

int main()
{
    Object& obja = Object::GetObject();
    Object& objb = Object::GetObject();
    //此时obja和objb是同一个对象 objx.value=10  obja.value=10   objb.value=10
    //这就是单例模式,对象只能被创建一份

    obja.SetValue(100);//objx.value=10  obja.value=100   objb.value=100
    cout << objb.GetValue() << endl;
}

Note: The object of the class type cannot appear in the class type, otherwise it will cause infinite recursion when constructing the object.

class Object
{
private:
    int value;
    Object objx;//error,是不完整类型,因为类类型中不能出现类类型自身的的对象,在构建对象时会引起无穷递归
private:
    Object(int x = 0) :value(x) { cout << "create object" << endl; }
public:
    Object(const Object&) = delete;
    Object& operator=(const Object&) = delete;
public:
    ~Object() { cout << "destroy object" << endl; }
};

int main()
{
    Object obja;    //Obja.value
                        .objx  .value
                               .objx   .value
                                       .objx 
  obja中的objx还有objx,会引起无穷递归
} 

Half hungry man mode (half lazy man mode)

//半个懒汉模式(半个饿汉模式) 
1.将静态对象的创建放在获取实例静态方法中,在获取实例函数被调用的时候才创建对象
单线程中只创建了一个对象
class Object
{
private:
    int value;
private:
    Object(int x = 0) :value(x) { cout << "create object" << endl; }//让其在外部不能创建对象

public:
    Object(const Object&) = delete;//删除拷贝构造,防止对象通过拷贝构造创建对象
    Object& operator=(const Object&) = delete;//删除赋值,防止简单类型通过赋值创建对象
public:
    ~Object() { cout << "destroy object" << endl; }//防止某些情况不能正常释放
    //void SetValue(Object* const this,int x=0)
    void SetValue(int x = 0) { value = x; }
    //int GetValue(const Object* const this)
    int GetValue() const { return value; }
    static Object& GetObject()//获取实例的静态方法
    {
        static Object objx;
        return objx;
    }
};

int main()
{
    Object& obja = Object::GetObject();//调用GetObject(),创建静态对象static Object objx(调用构造函数)
    Object& objb = Object::GetObject();//调用GetObject(),不再创建objx对象,直接return objx,因为静态对象只被创建一次则不会调用构造函数
    //此时obja和objb是同一个对象,构造函数只调用了一次 objx.value=0  obja.value=0   objb.value=0
} 

//Half lazy mode (half hungry mode) disadvantages:
 //If this design mode is single-threaded, there is no problem, but if it is multi-threaded, it will be thread unsafe
//1. In multi-threading, if there are two There are thread functions funa and funb. It is possible that the static object objx is created twice at the same address.
//2. It is possible that the execution of obja.SetValue(100); is interrupted. The returned objx is not 100 but the default value 0. It is not thread safe
 


void funa()//执行此线程调用GetObject(),创建static Object objx
{
    Object& obja = Object::GetObject();
    obja.SetValue(100);
}

void funb()
{
    Object& objb = Object::GetObject();//执行此线程调用GetObject(),创建static Object objx
    cout << objb.GetValue() << endl;
}

 

lazy mode

//懒汉模式
1.将构造函数设置为私有的,删除拷贝构造和赋值函数,防止在外部创建对象且保证对象仅被创建一次
2.类内设置静态本类类型指针,类外初始化
3.定义创建对象的静态方法,在真正使用时调用此静态方法创建对象
#include<iostream>
#include<thread>//C++11的线程库
using namespace std;
class Object{
private:
    int value;
    static Object* pobj;
private:
    Object(int x = 0) :value(x) { cout << "create object" << endl; }//让其在外部不能创建对象

public:
    Object(const Object&) = delete;//删除拷贝构造,防止对象通过拷贝构造创建对象
    Object& operator=(const Object&) = delete;//删除赋值,防止简单类型通过赋值创建对象
public:
    ~Object() { cout << "destroy object" << endl; }//防止某些情况不能正常释放
    //void SetValue(Object* const this,int x=0)
    void SetValue(int x = 0) { value = x; }
    //int GetValue(const Object* const this)
    int GetValue() const { return value; }
    static Object& GetRefObject()//获取实例的静态方法
    {
        if (nullptr == pobj)//在GetRefObject()函数被调用时才创建对象
        {
            pobj = new Object(10);
        }
        return *pobj;
    }
};

Object* Object::pobj = nullptr;//静态成员在类外初始化

//懒汉模式的缺点:
//1.线程不安全
void funa()
{
    Object& obja = Object::GetRefObject();
    obja.SetValue(100);
    cout << "&obja  " << &obja << endl;
}

void funb()
{
    Object& objb = Object::GetRefObject();
    cout << objb.GetValue() << endl;
    cout << "&objb  " << &objb << endl;
}

int main()
{
    std::thread tha(funa);
    std::thread thb(funb);
    tha.join();//等待线程结束
    thb.join();//等待线程结束
}

In the above mode, the object is created twice in multi-threading (the addresses of obja and objb are different, and the Object constructor is called twice), and when the funb thread executes, the value 100 set by the funa thread is overwritten and changed to The initial value when created is 10. The running result is:

 

Guess you like

Origin blog.csdn.net/weixin_53472334/article/details/131989186