Singleton mode C++ implementation and observer mode C++ implementation

Table of contents

1. Introduction to singleton mode

2. Singleton code implementation

2.1 static introduction

2.2 Three usages of static in C++:

(1) Static local variables

(2) Static member variables

(3) Static member functions

3. Introduction to Observer Mode

4. Observer code implementation


1. Introduction to singleton mode

      The singleton pattern belongs to the category of creational patterns in the design pattern (separating the creation and use of objects from each other).

      The scenario used by the singleton pattern is: in a process, there is only one instance object. The instance constructor is generally declared as a private class and cannot be accessed outside the class. Access the instance object by providing public functions that access the instance object.

      Advantages of the singleton mode: ensure that there is only one instance class in the entire program, which not only ensures the uniqueness of data, but also saves space.

Singleton implementations are:

(1) Lazy style. (Instantiated when used. There are thread safety issues)

(2) Static local variables. (Instantiated when used. No thread safety issues)

(3) Hungry Chinese style. (The singleton class is instantiated when it is created. There is no thread safety issue)

2. Singleton code implementation

It is recommended to use static local variables to implement singletons. The specific code is as follows:

#include <iostream>

class SingletInstance
{
public:
    static SingletInstance* getInstance()
    {
        static SingletInstance ins;
        return &ins;
    }
private:
    SingletInstance(){std::cout << "SingletInstance Constructot " << std::endl;};
};

int main()
{
    std::cout << "this addr is  " << SingletInstance::getInstance() << std::endl;
    std::cout << "this addr is " << SingletInstance::getInstance() << std::endl;

    return 0;
}

The result of running the code is as follows:

Various implementations of singletons can refer to the following links:

Summarize the C++ singleton mode_does the c++ proto class need to be released by itself_Fa Ruxue Jay's Blog-CSDN Blog

2.1 static introduction

        C++ memory partition can be roughly divided into: stack, heap, global data area (static area), code area , the content modified by staitc belongs to the global data area (static area), and the variables existing in the global data area include: global variables and Static variable storage , generation cycle is released at the end of the program.

2.2 Three usages of static in C++:

(1) Static local variables

      Definition: Use static to modify the type of variable, such as static int a. The life cycle is the program running cycle.

      Advantages: The scope of static local variables is not accessible outside the scope, which has better security.

       Note: It is only initialized once. If it is not assigned, it will be automatically assigned a value of 0. When entering this function next time, the initialization statement will be automatically ignored.

code show as below:

void func()
{
    static int a = 10;
    a++;
    std::cout << "a " << a << std::endl;
}

int main()
{

    int cnt = 1;
    while(cnt <= 3)
    {
        func();
        cnt++;
    }
    return 0;
}

The result of the operation is as follows:

(2) Static member variables

Definition: A class member variable decorated with static, whose life cycle is the program running cycle. 

Note: The static member variables of objects of each class point to the same block address area, that is, the data content is the same as the address.

Must be initialized before the class object is used.

code show as below:

#include <iostream>

class A
{
public:
    static int a;
};


int A::a = 10; //必须使用前初始化!
int main()
{
    std::cout << "A::a  is " << A::a <<",addr is " <<  &(A::a) << std::endl;
    
    / 下面访问静态成员变量的方法不使用,只为了说明,一般使用A::a访问静态成员变量。 //
    A obj1;
    A obj2;
    A obj3;
    std::cout << "A::a  is " << obj1.a << ",addr is " << &(obj1.a) <<std::endl;
    std::cout << "A::a  is " << obj2.a << ",addr is " << &(obj2.a) <<std::endl;
    std::cout << "A::a  is " << obj3.a << ",addr is " << &(obj3.a) <<std::endl;
    //
    return 0;
}

The result of the operation is as follows:

(3) Static member functions

  Definition: Add a static modifier in front of the member function, such as: static void fun(){};

  Access: access with class name::function name

 The difference between static member functions and non-static member functions: non-static member functions can access static member functions and static data members arbitrarily, and static member functions cannot access non-static member functions and non-static data members.

3. Introduction to Observer Mode

 The observer pattern belongs to the behavioral pattern classification in the design pattern (focusing on the behavior or interaction of objects).

The usage scenario of the observer mode is to describe multiple observers (Observer) subscribing to an object state of an observed object ; when the state of the observed object changes, the observed object will notify all subscribed observer objects to receive the state change information.

Application: The communication mechanism between data in the Qt framework is the signal slot, and the signal slot mechanism is the embodiment of the observer mode_Baidu Encyclopedia (baidu.com) application.

The Observer pattern is also known as the Publisher-Subscribe pattern. The observer pattern is a one-to-many relationship description between objects, similar to broadcasting.

4. Observer code implementation

The observation objects of code design are as follows:

(1) Define an abstract observer (AbsTarget) class, which has interfaces for subscription, unsubscription, notification of property changes, and data setting.

(2) The observed object (Target1) inherits from AbsTarget, and the corresponding observer objects include Observer1 and Observer2.

(3) The observed object (Target2) inherits from AbsTarget, and the corresponding observer object Observer3.

(4) Define an abstract observer (AbsObserver) class, which has an interface for receiving data changes. In order to cope with different data types, the received data type is defined as void*.

(5) Observer 1 (Observer1) inherits from the AbsObserver class; Observer 2 (Observer2) inherits from the AbsObserver class. Observer1 and Observer2 receive the same data structure for data changes.

(6) Observer 3 (Observer3) inherits from the AbsObserver class, and the data structure received by Observer3 is one.

The specific code is implemented as follows:

#include <iostream>
#include <list>
#include <string>
struct Data1
{
    int n1;
    float f1;
};

struct Data2
{
    int n1;
    std::string strname;
};
//观察者
class AbsObserver
{
public:
    AbsObserver()=default;
    virtual ~AbsObserver(){};
    virtual void receiveData(void* pThis)=0;    //使用void*来适应不同的数据类型
};

//具体观察者1
class Observer1:public AbsObserver
{
public:
    Observer1()=default;
    ~Observer1(){};

    virtual void receiveData(void* pThis)override{
        Data1* data1 = (Data1*)pThis;
        std::cout << "  Observer1 receive ";
        std::cout << "f1: " <<  data1->f1 <<  ", n1: " <<data1->n1 << std::endl;
    }
};

//具体观察者2
class Observer2:public AbsObserver
{
public:
    Observer2()=default;
    ~Observer2(){};

    virtual void receiveData(void* pThis)override{
        Data1* data1 = (Data1*)pThis;
        std::cout << "  Observer2 receive ";
        std::cout << "f1: " <<  data1->f1 <<  ", n1: " <<data1->n1 << std::endl;
    }
};

class Observer3:public AbsObserver
{
public:
    Observer3()=default;
    ~Observer3(){};

    virtual void receiveData(void* pThis)override{
        Data2* data1 = (Data2*)pThis;
        std::cout << "  Observer3 receive ";
        std::cout << "strname: " <<  data1->strname <<  ", n1: " <<data1->n1 << std::endl;
    }
};


//被观察目标
class AbsTarget{
public:
    AbsTarget()=default;
    virtual ~AbsTarget(){
        std::cout << "~AbsTarget" << std::endl;
    };

    virtual void Attach(AbsObserver* obj)=0;
    virtual void Detach(AbsObserver* obj)=0;
    virtual void NotifyData()=0;
    virtual void setData(void* data)=0;

};

//具体被观察目标类型1
class Target1:public AbsTarget
{
public:
    Target1()=default;
    virtual ~Target1(){
        std::cout << "~Target1" << std::endl;
        if(m_pObservers.size() >0)
        {
            m_pObservers.clear();
            std::cout << "Target1 clear Data" << std::endl;
        }
    };

    virtual void Attach(AbsObserver* obj)override
    {
        m_pObservers.push_back(obj);
    };
    virtual void Detach(AbsObserver* obj)override{
        m_pObservers.remove(obj);
    };
    virtual void NotifyData()override
    {
        for(auto it:m_pObservers)
        {
            it->receiveData(&m_data1);
        }
    }

    void setData(void* data)override
    {
        Data1* tmp = (Data1*)data;
        m_data1.f1 =tmp->f1;
        m_data1.n1 =tmp->n1;
        std::cout << "Target1 NotifyData************" << std::endl;
        NotifyData();
    }
private:
    std::list<AbsObserver*>m_pObservers;
    Data1 m_data1;
};

//具体被观察目标类型2
class Target2:public AbsTarget
{
public:
    Target2()=default;
    virtual ~Target2(){
        std::cout << "~Target2" << std::endl;
        if(m_pObservers.size() >0)
        {
            m_pObservers.clear();
            std::cout << "Target2 clear Data" << std::endl;
        }
    };

    virtual void Attach(AbsObserver* obj)override
    {
        m_pObservers.push_back(obj);
    };
    virtual void Detach(AbsObserver* obj)override{
        m_pObservers.remove(obj);
    };
    virtual void NotifyData()override
    {
        for(auto it:m_pObservers)
        {
            it->receiveData(&m_data1);
        }
    }

    void setData(void* data)override
    {
        Data2* tmp = (Data2*)data;
        m_data1.strname =tmp->strname;
        m_data1.n1 =tmp->n1;
        std::cout << "Target2 NotifyData************" << std::endl;
        NotifyData();
    }
private:
    std::list<AbsObserver*>m_pObservers;
    Data2 m_data1;
};

void UseTarget1()
{
    AbsTarget *pTarger1 = new Target1();

    AbsObserver* pObserve1= new Observer1();
    pTarger1->Attach(pObserve1);
    AbsObserver* pObserve2= new Observer2();
    pTarger1->Attach(pObserve2);
    Data1 tmp;
    tmp.f1 = 30.06;
    tmp.n1 = 60;
    pTarger1->setData(&tmp);
    pTarger1->Detach(pObserve1);
    pTarger1->setData(&tmp);

    pTarger1->Detach(pObserve2);
    delete  pTarger1;
    pTarger1 = nullptr;
    delete  pObserve1;
    pObserve1 = nullptr;
    delete  pObserve2;
    pObserve2 = nullptr;
}

void UseTarget2()
{
    AbsTarget *pTarger2 = new Target2();

    AbsObserver* pObserve3= new Observer3();
    pTarger2->Attach(pObserve3);
    Data2 tmp;
    tmp.strname = "hello";
    tmp.n1 = 60;
    pTarger2->setData(&tmp);
    pTarger2->Detach(pObserve3);

    delete  pTarger2;
    pTarger2 = nullptr;
    delete  pObserve3;
    pObserve3 = nullptr;

}
int main()
{
    std::cout << "*********** Use Target1, Observer1, Observer2 ***********" << std::endl;
    UseTarget1();
    std::cout << "\n*********** Use Target2, Observer3 ***********" << std::endl;
    UseTarget2();

    return 0;
}

The result of the program running is as follows:

 For an introduction to the observer pattern, please refer to:

C++ Behavioral Pattern - Realize Observer Pattern_ Observer Pattern C++ Implementation_herryone123's Blog-CSDN Blog

Additional:

1. For the introduction and classification of design patterns, please refer to:

Introduction and Classification of C++ Design Patterns

2. The usage of static can refer to:

Detailed Explanation of the Usage of Static in C++_The Function and Usage of Static in C++

The role of C language static - modified variables (global variables/local variables), functions_c language static modified local variables_Jieer__er's Blog-CSDN Blog

3. Links for those interested in the signal slot mechanism:

Signal Slot Mechanism

Guess you like

Origin blog.csdn.net/hanxiaoyong_/article/details/131034717