C++ 单例模式之-懒汉、饿汉、线程安全和局部静态变量法***

目录

概述

懒汉式:

优点:

缺点:

实例:

饿汉式:

优点:

缺点:

实例:

局部静态变量方法:

线程安全模式:

#1 静态成员指针成员:

#2 静态成员指针成员(改进型):

#3 静态成员对象

#4 静态局部对象

#5 静态成员指针成员(动态释放)


概述

单例模式是一个创建型设计模式,一个类只会创建一个对象。

由于只有一个对象,所以不能够通过new来创建对象,因此类的构造函数得是private的
由于不能够new出对象,所以类内部需要提供一个函数来获取对象,这个函数和对象都是属于类的,而不是对象,因此这个函数和唯一 的对象都得是static的,并且对象是private类型的,函数是public类型的

创建单例的基本步骤:

1. 构造函数私有化,避免外部使用构造函数来创建对象
2. 增加静态私有的当前类的指针变量
3. 提供静态的public接口,可以让用户获取单例

使用场景:

一个类只需要一个对象实例

UML图

懒汉式:

优点:

第一次调用的时候才初始化,避免内存浪费

缺点:

必须加锁才能够保证单例,加锁会影响效率

实例:

class SingletonLazy{
private:
       SingletonLazy(){
              std::cout << "single lazy!" << std::endl;
       }
public:
       static SingletonLazy* getInstatce(){
              if( NULL == lazy ){
                     lazy = new SingletonLazy;
              }
              return lazy;
       }
private:
       static SingletonLazy* lazy;
};
//类外初始化
SingletonLazy* SingletonLazy::lazy = NULL;



饿汉式:

优点:

不需要加锁,执行效率高,线程安全的

缺点:

不管是不是用都会初始化,浪费内存

实例:

    

class SingletonHungry{
private:
       SingletonHungry(){
              std::cout << "single hungry!" << std::endl;
       }
public:
       static SingletonHungry* getInstatce(){
              return hungry;
       }
       //SingletonHungry类退出以后会自动析构free_single,
       //从而调用FreeSingle的析构函数来释放单例
       //其实类退出时会自动释放单例不需要该类
       class FreeSingle{
              ~FreeSingle(){
                     if( NULL != hungry ){
                           delete hungry;
                           hungry = NULL;
                     }
              }
       };
private:
       static SingletonHungry* hungry;
       static FreeSingle free_single;
};
//类外初始化
SingletonHungry* SingletonHungry::hungry = new SingletonHungry;


局部静态变量方法:

// 局部静态变量
class Singleton{
public:
       // 使用指针而不是引用是为了避免拷贝构造函数进行拷贝
       // Singleton single = Singleton::getInstance();
       static Singleton* getInstance(){
              static Singleton instance;
              return instance;
       }
private:
       Singleton(){
              std::cout << "局部静态方式" << std::endl;
       }
       // 如果需要getInstance 返回引用,
       // 也可以通过重载赋值运算符和拷贝构造函数,这两个函数不需要具体的实现
       Singleton(Singleton const & single);
       Singleton& operator = (const Singleton& single);
};



线程安全模式:

class SingletonMulThread{
private:
       SingletonMulThread(){
              std::cout << "single MulThread!" << std::endl;
       }
public:
       static SingletonMulThread* getInstatce(){
              if( NULL == single_multhread ){
                     std::lock_guard<std::mutex> lock(mutex);
                     if( NULL == single_multhread ){
                           single_multhread = new SingletonMulThread;
                     }
              }
              return single_multhread;
       }
private:
       static SingletonMulThread* single_multhread;
       static mutex mutex;
};
//类外初始化
SingletonMulThread* SingletonMulThread::single_multhread = NULL;
mutex SingletonMulThread::mutex;

 

一般情况下,为了实现单例我们都会想到使用 static 成员,下面#1是最基本的方式;

#1 静态成员指针成员:

class Singleton{  
        private:  
            Singleton(){};  
            virtual ~Singleton(){};  
        public:  
            Singleton *Instance();  
        protect:  
            static Singleton *_instance;   
    };  
    //Singleton.h  
      
      
    Singleton *Singleton::instance()  
    {  
      
        if(NULL == _instance){  
            _instance = new Singleton();  
        }  
        return _instance;  
    }  
    //Singleton.cpp  
/*
构造时机: 运行时生成; 

对象位置:   堆  

资源释放: new的单例对象,没有时机去释放;

线程安全: 否;  ---在单例构造过程可能重复,造成内存泄露;
*/

#2 静态成员指针成员(改进型):

在#1的基础上解决存在的两个问题;

如果单例对象的构造实在运行时之前(也就是程序静态变量初始化时完成)就可以避免线程安全的问题;

    class Singleton{  
        public:  
            static Singleton *instance();  
        private:  
            Singleton();  
            virtual ~Singleton();  
            Singleton(const Singleton&){};  
            Singleton& operator=(const Singleton&){};  
        private:  
            class CGarbo{  
                public:  
                    ~CGarbo()  
                    {    
                        if(Singleton::m_pInstance){  
                            delete m_pInstance;  
                        }    
                    }    
            };   
        private:  
            static Singleton *m_pInstance;  
            static CGarbo Garbo;  
    };  
    //Singleton.h  
      
      
    Singleton::CGarbo Singleton::Garbo;  
    Singleton* Singleton::m_pInstance = new Singleton();  
      
    Singleton::Singleton()  
    {  
        printf("contructure funcation\n");  
    }  
      
    Singleton::~Singleton()  
    {  
        printf("deconstructure funcation\n");  
    }  
      
    Singleton* Singleton::instance()  
    {  
        return m_pInstance;  
    }  
    //Singleton.cpp

构造时机: 初始化时生成; 

对象位置:   堆  

资源释放: 通过一个成员的析构函数来释放单例对象,;  非常巧妙, 但是m_pInstance指针和 Garbo两个成员的析构是否有先后顺序,如果指针先被释放(指针变量变成NULL?)那么单例对象还是没有机会被释放;  ---找时间确认一下,然后更新一下这个结果;

线程安全: 是;  ---初始化过程,没有线程竞争;

 

对比以上两种方式,可以看出静态初始化的时候构造单例对象,能够比较好的解决线程安全的问题; 但是资源释放的需要通过曲线救国的方式来解决;

那么能不能把单例对象也生成在静态区呢?这样释放的问题就可以由操作系统自动完成;

#3 静态成员对象

    class Singleton{  
        public:  
            static Singleton *instance();  
        private:  
            Singleton();  
            virtual ~Singleton();  
            Singleton(const Singleton&){};  
            Singleton& operator=(const Singleton&){};  
        private:  
            static Singleton m_instance;  
    };  
    //Singleton.h  
      
    Singleton Singleton::m_instance;  
      
    Singleton::Singleton()  
    {  
        printf("contructure funcation\n");  
    }  
      
    Singleton::~Singleton()  
    {  
        printf("deconstructure funcation\n");  
    }  
      
    Singleton *Singleton::instance()  
    {  
        return &m_instance;  
    }  
    //Singleton.cpp  

构造时机: 初始化时生成; 

对象位置:  静态区

资源释放: 在程序结束时 自动释放静态区的 成员变量

线程安全: 是; 

注意: 静态成员的初始化需要放到类外完成;  那么是否可以把静态对象在成员函数instance()内部生成呢?

#4 静态局部对象

 class Singleton{  
        public:  
            static Singleton *instance();  
        private:  
            Singleton();  
            virtual ~Singleton();  
            Singleton(const Singleton&){};  
            Singleton& operator=(const Singleton&){};  
 
    };  
    //Singleton.h   
      
    Singleton::Singleton()  
    {  
        printf("contructure funcation\n");  
    }  
      
    Singleton::~Singleton()  
    {  
        printf("deconstructure funcation\n");  
    }  
      
    Singleton *Singleton::instance()  
    {  
        static Singleton ins;
        return &ins;  
 
    }  
 
 //Singleton.cpp

构造时机: 运行时生成; ---参考链接2 ;  --对于C++的局部类对象,是在函数第一次调用时生成;

对象位置:  静态区

资源释放: 在程序结束时 自动释放静态区的 成员变量
线程安全: 是(C++11);  ---参考链接2,, C++11标准针对局部静态对象的构造能保证线程安全(待查);

综合以上4中方式,单例对象的释放都是在程序结束时释放,

如果要求能够提供接口随时释放对象,那么就必须构造在堆上,然后提供显式的destroy接口;


#5 静态成员指针成员(动态释放)
 

    #ifndef __MUTEX_H__  
    #define __MUTEX_h__  
    #include <pthread.h>  
      
    class Mutex{    
    private:  
            pthread_mutex_t m_mutex;  
        public:  
            Mutex();  
            virtual ~Mutex();  
        private:  
            Mutex(const Mutex&){};  
            Mutex& operator=(const Mutex&){};  
        public:  
            int lock();  
            int unlock();  
            int trylock();  
      
    };  
    #endif //__MUTEX_H__  
    //Mutex.h  
      
      
    #include "Mutex.h"  
      
    Mutex::Mutex()  
    {  
        pthread_mutex_init(&m_mutex, NULL);  
    }  
      
    Mutex::~Mutex()  
    {  
        pthread_mutex_destroy(&m_mutex);  
    }  
      
    int Mutex::lock()  
    {  
        return  pthread_mutex_lock(&m_mutex);  
    }  
      
    int Mutex::unlock()  
    {  
        return pthread_mutex_unlock(&m_mutex);  
    }  
      
    int Mutex::trylock()  
    {  
        return pthread_mutex_trylock(&m_mutex);  
    }  
    //Mutex.cpp  
      
    //单例类  
    #ifndef __SINGLETON_H__  
    #defile __SINGLETON_H__  
    #include "Mutex.h"  
    class Singleton{  
      
    public:  
        static Singleton *instance();  
        static void destroy();  
      
    private:  
        Singleton();  
        virtual ~Singleton();  
         Singleton(const Singleton&){};  
         Singleton& operator=(const Singleton&){};  
          
    private:  
        static Singleton *m_pInstance;  
        static Mutex m_Mutex;  
    };  
    #endif //__SINGLETON_H__  
    //Singleton.h  
      
      
    Singleton* Singleton::m_pInstance = NULL;  
    Mutex Singleton::m_Mutex;  
      
    Singleton::Singleton()  
    {  
    //do something  
    }  
      
    Singleton::~Singleton()  
    {  
    //do something  
    }  
      
    Singleton* Singleton::instance()  
    {  
         if(NULL == m_pInstance){  
              m_Mutex.lock();  
              if(NULL == m_pInstance){  
                   m_pInstance = new Singleton();  
              }  
              m_Mutex.unlock();  
         }  
         return m_pInstance;  
    }  
      
    void Singleton::destroy()  
    {  
         if(m_pInstance){  
              m_Mutex.lock();  
              if(m_pInstance){  
                   delete m_pInstance;  
                   m_pInstance = NULL;  
              }  
              m_Mutex.unlock();  
         }  
    }  
    //Singleton.cpp  

当然 也可以吧 静态成员指针放到接口函数里面作为局部静态变量 和局部静态对象;

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

隨意的風

如果你觉得有帮助,期待你的打赏

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

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

打赏作者

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

抵扣说明:

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

余额充值