单例模式——C++实现线程安全的单例

本文详细介绍了C++中实现线程安全的单例模式,包括懒汉模式和饿汉模式。懒汉模式通过双检锁解决线程安全问题,但在大量数据时可能成为性能瓶颈。饿汉模式在程序开始时即创建实例,确保线程安全。文章还提出了一种在程序结束时自动删除单例实例的方法,利用静态成员的析构函数来释放资源,实现了优雅的内存管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、懒汉模式:即第一次调用该类实例的时候才产生一个新的该类实例,并在以后仅返回此实例。

需要用锁,来保证其线程安全性:原因:多个线程可能进入判断是否已经存在实例的if语句,从而non thread safety.

使用double-check来保证thread safety.但是如果处理大量数据时,该锁才成为严重的性能瓶颈。

1、静态成员实例的懒汉模式:

class Singleton
  {
  private:
      static Singleton* m_instance;
      Singleton(){}
  public:
      static Singleton* getInstance();
  };
  
 Singleton* Singleton::getInstance()
 {
     if(NULL == m_instance)
     {
         Lock();//借用其它类来实现,如boost
         if(NULL == m_instance)
         {
             m_instance = new Singleton;
         }
         UnLock();
     }
     return m_instance;
 }

2、内部静态实例的懒汉模式

这里需要注意的是,C++0X以后,要求编译器保证内部静态变量的线程安全性,可以不加锁。但C++ 0X以前,仍需要加锁。

class SingletonInside
 {
  private:
      SingletonInside(){}
  public:
      static SingletonInside* getInstance()
      {
          Lock(); // not needed after C++0x
          static SingletonInside instance;
         UnLock(); // not needed after C++0x
         return instance; 
     }
 };

二、饿汉模式:即无论是否调用该类的实例,在程序开始时就会产生一个该类的实例,并在以后仅返回此实例。

由静态初始化实例保证其线程安全性,WHY?因为静态实例初始化在程序开始时进入主函数之前就由主线程以单线程方式完成了初始化,不必担心多线程问题。

故在性能需求较高时,应使用这种模式,避免频繁的锁争夺。

 class SingletonStatic
  {
  private:
      static const SingletonStatic* m_instance;
      SingletonStatic(){}
  public:
      static SingletonStatic* getInstance()
      {
          return m_instance;
     }
 };
 
 //外部初始化 before invoke main
 const SingletonStatic* SingletonStatic::m_instance = new SingletonStatic;



m_pInstance指向的空间什么时候释放呢?更严重的问题是,该实例的析构函数什么时候执行?

如果在类的析构行为中有必须的操作,比如关闭文件,释放外部资源,那么上面的代码无法实现这个要求。我们需要一种方法,正常的删除该实例。

可以在程序结束时调用GetInstance(),并对返回的指针掉用delete操作。这样做可以实现功能,但不仅很丑陋,而且容易出错。因为这样的附加代码很容易被忘记,而且也很难保证在delete之后,没有代码再调用GetInstance函数。

一个妥善的方法是让这个类自己知道在合适的时候把自己删除,或者说把删除自己的操作挂在操作系统中的某个合适的点上,使其在恰当的时候被自动执行。

我们知道,程序在结束的时候,系统会自动析构所有的全局变量。事实上,系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。利用这个特征,我们可以在单例类中定义一个这样的静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。如下面的代码中的CGarbo类(Garbo意为垃圾工人):

class CSingleton
{
//其他成员
public:
static CSingleton* GetInstance();
private:
    CSingleton(){};
    static CSingleton * m_pInstance;
class CGarbo //它的唯一工作就是在析构函数中删除CSingleton的实例
{
        public:
            ~CGarbo()
            {
                if( CSingleton::m_pInstance )
                  delete CSingleton::m_pInstance;
           }
 }
        Static CGabor Garbo; //定义一个静态成员,程序结束时,系统会自动调用它的析构函数
};

类CGarbo被定义为CSingleton的私有内嵌类,以防该类被在其他地方滥用。

程序运行结束时,系统会调用CSingleton的静态成员Garbo的析构函数,该析构函数会删除单例的唯一实例。

使用这种方法释放单例对象有以下特征:

在单例类内部定义专有的嵌套类;

在单例类内定义私有的专门用于释放的静态成员;

利用程序在结束时析构全局变量的特性,选择最终的释放时机;

使用单例的代码不需要任何操作,不必关心对象的释放。

具体代码如下:

[cpp]  view plain copy
  1. #include <iostream>>  
  2.   
  3. using namespace std;  
  4.   
  5. class Singleton  
  6.   
  7. {  
  8.   
  9. public:  
  10.   
  11.     static Singleton *GetInstance();  
  12.   
  13. private:  
  14.   
  15.     Singleton()  
  16.   
  17.     {  
  18.   
  19.         cout << "Singleton ctor" << endl;  
  20.   
  21.     }  
  22.   
  23.     ~Singleton()  
  24.   
  25.     {  
  26.   
  27.         cout << "Singleton dtor" << endl;  
  28.   
  29.     }  
  30.   
  31.     static Singleton *m_pInstance;  
  32.   
  33.     class Garbo  
  34.   
  35.     {  
  36.   
  37.     public:  
  38.   
  39.         ~Garbo()  
  40.   
  41.         {  
  42.   
  43.             if (Singleton::m_pInstance)  
  44.   
  45.             {  
  46.   
  47.                 cout << "Garbo dtor" << endl;  
  48.   
  49.                 delete Singleton::m_pInstance;  
  50.   
  51.             }  
  52.   
  53.         }  
  54.   
  55.     };  
  56.   
  57.     static Garbo garbo;  
  58.   
  59. };  
  60.   
  61. Singleton::Garbo Singleton::garbo;  // 一定要初始化,不然程序结束时不会析构garbo  
  62.   
  63. Singleton *Singleton::m_pInstance = NULL;  
  64.   
  65. Singleton *Singleton::GetInstance()  
  66.   
  67. {  
  68.   
  69.     if (m_pInstance == NULL)  
  70.   
  71.         m_pInstance = new Singleton;  
  72.   
  73.     return m_pInstance;  
  74.   
  75. }  
  76.   
  77. int main()  
  78.   
  79. {  
  80.   
  81.     Singleton *p1 = Singleton::GetInstance();  
  82.   
  83.     Singleton *p2 = Singleton::GetInstance();  
  84.   
  85.     if (p1 == p2)  
  86.   
  87.         cout << "p1 == p2" << endl;  
  88.   
  89.     return 0;  
  90.   
  91. }  


输出结果如下:

Singleton ctor

p1 == p2

Garbo dtor

Singleton dtor

单例模式是一种设计模式,它确保一个类只有一个,并提供一个全局访问点来获取该实。在C++中,有多种实现单例模式的方式。 引用\[1\]中的代码演示了一种基于局部静态对象实现单例模式。在这个子中,Singleton类的构造函数和析构函数都是私有的,这样就禁止了用户自己声明和定义实。通过getInstance()函数获取唯一的实。这种实现方式具有线程安全性。 引用\[2\]中提到了C++实现的几种方式。其中,最推荐的是懒汉式(magic static)——局部静态变量。这种方式的代码简洁,没有智能指针调用和双重检查锁定模式的风险。 引用\[3\]中的代码展示了使用局部静态变量实现的懒汉式。SingletonPattern_V3类的构造函数和析构函数都是私有的,通过Instance()函数获取唯一的实。 综上所述,C++中可以使用局部静态变量来实现单例模式,确保全局只有一个,并提供全局访问点。这种方式具有线程安全性,代码简洁。 #### 引用[.reference_title] - *1* [设计模式之单例模式(C++)](https://blog.youkuaiyun.com/zhaitianbao/article/details/128946441)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [C++ 单例模式](https://blog.youkuaiyun.com/u011718663/article/details/115922357)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值