找回密码
 立即注册
首页 业界区 业界 设计模式学习(一)单例模式补充——单例模式析构 ...

设计模式学习(一)单例模式补充——单例模式析构

秦晓曼 2025-6-9 08:58:57
目录

  • 前言
  • 无法调用析构函数的原因
  • 改进方法

    • 内嵌回收类
    • 智能指针
    • 局部静态变量

  • 参考文章

前言

在《单例模式学习》中提到了,在单例对象是通过new关键字动态分配在堆上的情况下,当程序退出时,不会通过C++的RAII机制自动调用其析构函数。本文讨论一下这种现象的原因以及解决方法。
无法调用析构函数的原因

在DCLP(双检查锁模式)中,CSingleton中的instance是一个静态指针变量,被分配在全局/静态存储区。而instance所指向的CSingleton实例是通过new创建在堆上的,只能手动调用delete来释放相关资源(对于单例模式这是无法实现的,因为析构函数私有),无法通过RAII释放相关资源。
在程序结束时,instance这个指针变量被销毁了,但它所指向的内存空间中的CSingleton对象并没有被显式销毁,而是由操作系统去回收这一块内存(不会调用其析构函数)。然而依赖操作系统来清理资源并不是一个优雅的结束方式,可能会造成文件句柄未关闭、网络连接未断开等资源泄漏。
  1. class CSingleton
  2. {
  3. public:
  4.     static CSingleton* getInstance();
  5.     static std::mutex mtx;
  6. private:
  7.     CSingleton(){}
  8.     ~CSingleton(){}
  9.     CSingleton(const CSingleton&)                         = delete;
  10.     CSingleton& operator=(const CSingleton&) = delete;
  11.     static CSingleton* instance;
  12. };
  13. CSingleton* CSingleton::instance;
  14. CSingleton* CSingleton::getInstance()
  15. {
  16.     if(nullptr == instance)
  17.     {
  18.         mtx.lock();
  19.         if(nullptr == instance)
  20.         {
  21.             instance = new CSingleton();
  22.         }
  23.         mtx.unlock();
  24.     }
  25.     return instance;
  26. }
复制代码
改进方法

在讨论改进方法时,我们还是倾向于利用C++的RAII机制,而不是手动去控制释放的时机。
内嵌回收类

我们的单例类对象生命周期的开始是在第一次调用时,结束是在程序结束时。
而且我们知道①静态成员变量的生命周期是从程序启动到结束②在静态成员变量被销毁时会调用其析构函数
因此我们可以在单例类中定义一个用于释放单例类资源的内嵌类,将其析构函数定义为显式删除单例对象的操作,然后在单例类中添加一个内嵌类类型的静态成员变量garbo。
这样的话,在程序结束时garbo就会被销毁,而RAII机制确保了在销毁时会调用内嵌类CGarbo的析构函数。
因为在~CGarbo()中delete了CSingleton::instance,所以~CSingleton()就会被调用,相关资源得以释放。
[code]class CSingleton{public:    static CSingleton* getInstance();private:    CSingleton(){std::cout
您需要登录后才可以回帖 登录 | 立即注册