[学习][笔记]设计模式(基于C/C++实现)<一>单例模式

基于王桂林老师的设计模式 视频课程地址


前言

单例模式

保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。

使用场景

1.程序运行过程,都是只需要一个对象的情况,如全局配置Configure

基本思路

  1. 构造函数声明为private或protect防止被外部函数实例化。
  2. 提供一个全局的静态方法(全局访问点)。
  3. 内部保存一个private static的类指针保存唯一的实例,实例的动作由一个public的类方法代劳,该方法也返回单例类唯一的实例。

例1.0

#include <iostream>

using namespace std;


class SingletonBase
{
protected:
    SingletonBase(){
        cout<<"SingletonBase created"<<endl;
    };
    ~SingletonBase(){
        cout<<"SingletonBase destoryed"<<endl;
    };
private:
    static SingletonBase* ptr;
public:
    static SingletonBase* GetInstance(){
        if (ptr== NULL){
            ptr = new  SingletonBase();
        }
        return ptr;
    }

};

SingletonBase* SingletonBase::ptr = NULL;

如果不考虑线程安全这样是可以的。

解释线程安全:
线程安全,指多线程并发调用时,对同一信号量进行操作时,存在幻读,脏读等线程问题。

例如:
线程A 调用GetInstance(),线程B
也同时调用GetInstance(),同一时刻,由于没有加锁,导致从内存读入的数据都是ptr=NULL,导致同一时间都会new一个,极大浪费了空间,且线程A,B最后操作的对象极有可能不是一个对象。

所以考虑线程安全这样是不行的。所以有了以下两种

1.懒汉式

顾名思义:懒人,第一次用到的时候才会想到去创建,跟懒加载概念类似。以时间换空间,用到的时候,会需要时间去创建实体,以减少不用的时候的堆空间。

例1.0就是最基本的懒汉式。

为了解决线程安全问题,我们选择一种方法

加锁

例1.1

#include <iostream>
#include <mutex>

using namespace std;

/**
 * use lock
 */

class SingletonLazy1
{
protected:
    SingletonLazy1(){
        cout<<"SingletonBase created"<<endl;
    };
    ~SingletonLazy1(){
        cout<<"SingletonBase destoryed"<<endl;
    };
private:
    static SingletonLazy1* ptr;

public:
    static mutex singleton_lock;
    static SingletonLazy1* GetInstance(){

        singleton_lock.lock();
        if (ptr== NULL){
            ptr = new  SingletonLazy1();
        }
        singleton_lock.unlock();

        return ptr;
    }

};
mutex SingletonLazy1::singleton_lock;
SingletonLazy1* SingletonLazy1::ptr=NULL;

同理也可以使用智能指针 lock_guard,shared_lock,unique_lock

2.饿汉式

顾名思义:饿汉,饥不择食,要吃现成的,必须类一加载,就创建,必须及时。以空间换时间,省去用到时加载的时间。

优点:

  • 不需要加锁
  • 提前创建好实例对象

缺点:

  • 空间换时间,如果不需要实例,也会默认创建实例

例1.2

#include <iostream>
#include <mutex>

using namespace std;

/**
 * hungry mode
 */

class SingletonHungry
{
protected:
    SingletonHungry(){
        cout<<"SingletonBase created"<<endl;
    };
    ~SingletonHungry(){
        cout<<"SingletonBase destoryed"<<endl;
    };
private:
    static SingletonHungry* ptr;

public:
    static SingletonHungry* GetInstance(){
       return ptr;
    }

};

SingletonHungry* SingletonHungry::ptr=new SingletonHungry;
关于饿汉式的线程安全问题

饿汉式内 变量线程安全我持怀疑态度

实例 多线程情况下使用单例

问题:开三个线程,使之变量A = 0,累加到100,顺序执行?

/*
 *
 * 问题:开三个线程,使之变量A = 0,累加到100,顺序执行?
 *
 */

//void ThreadFunc1(){
//    while(1){
//        if(SingletonBase::GetInstance()->counter >100){
//            return;
//        }
//        cout<<(SingletonBase::GetInstance()->counter++)<<endl;
//    }
//}

void ThreadFunc2(){
    while(1){
        MyLock.lock();
        if(SingletonLazy1::GetInstance()->counter >100){
            MyLock.unlock();
            return;
        }
        cout<<(SingletonLazy1::GetInstance()->counter++)<<endl;
        MyLock.unlock();
    }
}

void ThreadFunc3(){
    while(1){
        MyLock.lock();
        if(SingletonHungry::GetInstance()->counter >100){
            MyLock.unlock();
            return;
        }
        cout<<(SingletonHungry::GetInstance()->counter++)<<endl;
        MyLock.unlock();
    }
}



int main()
{

   use Base
//    thread th1(ThreadFunc1);
//    thread th2(ThreadFunc1);
//    thread th3(ThreadFunc1);

//    th1.join();
//    th2.join();
//    th3.join();

 use Lazy
//    thread th1(ThreadFunc2);
//    thread th2(ThreadFunc2);
//    thread th3(ThreadFunc2);

//    th1.join();
//    th2.join();
//    th3.join();


    thread th1(ThreadFunc3);
    thread th2(ThreadFunc3);
    thread th3(ThreadFunc3);

    th1.join();
    th2.join();
    th3.join();

    return 0;
}

补充一点:
Qt项目可以直接使用std::thread
No-Qt项目中,需要CONFIG += thread

总结

demo地址

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二进制怪兽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值