设计模式之单例模式

    xiaoxiao2025-04-04  5

    一、设计模式     设计模式代表了最佳实践,是软件开发过程中面临一般问题的解决方案    设计模式是一套被反复使用、经过分类、代码设计总结的经验二、单例模式     单例模式也叫单件模式。Singleton是一个非常常用的设计模式,几乎所有稍微大一些的程序都会使用到它,所以构建一个线程安全并且高效的Singleton很重要   1. 单例类保证全局只有一个唯一实例对象。

       2. 单例类提供获取这个唯一实例的接口。

    以下先介绍下面试问的比较多的两个题:

        1.只能在栈上生成的对象的类

        2。只能在堆上生成对象的类

    //只能在栈上生成对象的类 class Singleton { public: <span style="white-space:pre"> </span>statci Singleton* GetSingletong() <span style="white-space:pre"> </span>{ <span style="white-space:pre"> </span>if(_sInstance == NULL) <span style="white-space:pre"> </span>_sInstance = new Singleton; <span style="white-space:pre"> </span>return _sInstance; <span style="white-space:pre"> </span>} private: <span style="white-space:pre"> </span>int _a; <span style="white-space:pre"> </span>static Singleton* _sInstance; <span style="white-space:pre"> </span>Singletong() <span style="white-space:pre"> </span>:_a(5) <span style="white-space:pre"> </span>{} <span style="white-space:pre"> </span>Singletong(const Singleton&); <span style="white-space:pre"> </span>Singletong& operator=(const Singleton&); }; </pre><pre name="code" class="html">//只能在栈上生成对象的单例模式 class Singleton { public: <span style="white-space:pre"> </span>static Singletong* GetSingletong() <span style="white-space:pre"> </span>{ <span style="white-space:pre"> </span>static Singletong s; <span style="white-space:pre"> </span>return &s; <span style="white-space:pre"> </span>} private: <span style="white-space:pre"> </span>int _a; <span style="white-space:pre"> </span>Singletong() <span style="white-space:pre"> </span>:_a(5) <span style="white-space:pre"> </span>{} <span style="white-space:pre"> </span>Singleton(const Singletong&); <span style="white-space:pre"> </span>Singletong& operator=(const Singletong&); }

        上面两个的区分就是一个是用了栈空间,一个用了堆空间,两个都是单列模式,但是要实现一个既高效又是线程安全的单例模式,上面的两种都还达不到标准。下面从三个方面来实现这个神秘的单例模式

    第一、为了实现线程安全,加锁!!!

    第二、因为每次获取单例对象都需要做加锁解锁操作,而实际上只有第一次产生单例模式需要这么做,所以使用双重判断来提高效率。

    第三、为了防止系统优化进行指令重排,使用栅栏,防止得到的对象为随机值。

    就具体实现如下:

    //高效、线程安全的单例模式 #include <mutex> #include <Windows.h> <pre name="code" class="cpp" style="font-size: 16px;">class Singleton { public: <span> </span>statci Singleton* GetSingletong() <span> </span>{ <span style="white-space:pre"> </span>if(_sInstance == NULL)//双重判断提高效率 <span style="white-space:pre"> </span>{ <span style="white-space:pre"> </span>lock_guard<mutex> lck(_mtx);//加锁线程安全 <span> </span>if(_sInstance == NULL) <span style="white-space:pre"> </span>{ <span style="white-space:pre"> </span>Singletong* tmp = new Singletong; <span style="white-space:pre"> </span>memoryBarrier();//使用栅栏防止编译器优化使,使指令重排 </pre><pre name="code" class="cpp" style="font-size: 16px;"><span style="white-space:pre"> <span style="font-size: 12pt; color: rgb(0, 176, 240);">//<span style="font-size: 12pt;">1.分配空间 2.调用构造函数 3.赋值 <span style="font-size: 12pt;"><span style="white-space:pre"> </span>//<span style="font-size: 12pt;">编译器编译优化可能会把2<span style="font-size: 12pt;">和3进行指令重排,这样可能会导致 <span style="font-size: 12pt;"><span style="white-space:pre"> </span>//<span style="font-size: 12pt;">高并发场<span style="font-size: 12pt;">景下<span style="font-size: 12pt;">,其他线程获取到未调用构造函数初始化的对象 <span style="font-size: 12pt;"><span style="white-space:pre"> </span>//<span style="font-size: 12pt;">以下加入<span style="font-size: 12pt;">内存栅栏进行处理,防止编译器重排栅栏后面的赋值 <span style="font-size: 12pt;"><span style="white-space:pre"> </span>//<span style="font-size: 12pt;">到内存栅栏之前</span></span></span></span></span></span></span></span></span></span></span></span></span><br style="text-align: -webkit-auto;" /></span> </span><pre name="code" class="cpp" style="font-size: 16px;"><span style="white-space:pre"> </span>_sInstance = tmp;

    <span style="white-space:pre"> </span>} <span> </span> <span style="white-space:pre"> </span>} <span> </span>return _sInstance; <span> </span>} private: <span> </span>int _a; <span> </span>static Singleton* _sInstance; <span style="white-space:pre"> </span>static mutex _mtx;//互斥锁<span style="white-space:pre"> </span> <span> </span>Singletong() <span> </span>:_a(5) <span> </span>{} </pre><pre name="code" class="cpp" style="font-size: 16px;"><span> </span>Singletong(const Singleton&); <span> </span>Singletong& operator=(const Singleton&); }; 上面实现的模式叫做懒汉模式,下面讲解下饿汉模式的单例模式

    线程安全(饿汉模式--简洁、高效、不用加锁、但是在某些场景下会有缺陷) //方式一 class Singleton {public: //获取唯一对象实例的接口函数 static Singleton* GetInstance() { <span style="white-space:pre"> </span>static Singleton sInstance; <span style="white-space:pre"> </span>return &sInstance; } void Print() { <span style="white-space:pre"> </span>cout<<_data<<endl; } private: //构造函数定义为私有,限制只能在类内创建对象 Singleton() :_data(0) {} Singleton(const Singleton&); Singleton& operator=(const Singleton&); //单例类里面的数据 int _data; }; void TestSingleton() { <span style="white-space:pre"> </span>Singleton::GetInstance()->Print(); } // 方式二 class Singleton { public: //获取唯一对象实例的接口函数 static Singleton* GetInstance() { <span style="white-space:pre"> </span>assert(_sInstance); <span style="white-space:pre"> </span>return _sInstance; } //删除实例对象 static void DelInstance() { <span style="white-space:pre"> </span>if (_sInstance) <span style="white-space:pre"> </span>{ <span style="white-space:pre"> </span>delete _sInstance; <span style="white-space:pre"> </span>_sInstance = NULL; <span style="white-space:pre"> </span>} } void Print() { <span style="white-space:pre"> </span>cout << _data << endl; } private: //构造函数定义为私有,限制只能在类内创建对象 Singleton() :_data(0) {} Singleton(const Singleton&); Singleton& operator=(const Singleton&); //指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例 static Singleton* _sInstance; //单例类里面的数据 int _data; }; Singleton* Singleton::_sInstance = new Singleton; void TestSingleton() { <span style="white-space:pre"> </span>Singleton::GetInstance()->Print(); <span style="white-space:pre"> </span>Singleton::DelInstance(); } 带RAII GC 自动回收实例对象的方式 class Singleton {

    public: //获取唯一对象实例的接口函数 static Singleton* GetInstance() { assert(_sInstance); return _sInstance;

    //删除实例对象 static void DelInstance() { if (_sInstance) { delete _sInstance; _sInstance = NULL; }

    void Print() { cout << _data << endl;

    class GC {

    public: ~GC() { cout << "DelInstance()"<<endl; DelInstance(); } }; private: //构造函数定义为私有,限制只能在类内创建对象 Singleton() :_data(0) {} //指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例 static Singleton* _sInstance; //单例类里面的数据 int _data; }; //静态对象在main函数之前初始化,这时只有主线程运行,所以是线程安全的。 Singleton* Singleton::_sInstance = new Singleton; //使用RAII,定义全局的GC对象释放对象实例Singleton::GC gc; void TestSingleton() { Singleton::GetInstance()->Print(); }

    转载请注明原文地址: https://ju.6miu.com/read-1297713.html
    最新回复(0)