• 单例模式


    直接来看代码:

     1 #include <iostream>
     2  
     3  class Singleton
     4  {
     5      private:
     6          Singleton(){std::cout<<"Singleton()"<<std::endl;};
     7          Singleton(const Singleton&) = delete;
     8          Singleton& operator=(const Singleton&)= delete;
     9          static Singleton* _instance;
    10      
    11      public:
    12          static Singleton* Instance()
    13          {   
    14              if(_instance == nullptr)
    15              {   
    16                  _instance = new Singleton();
    17              }   
    18              return _instance;
    19          }   
    20  
    21          void print()
    22          {   
    23              std::cout<<"print()"<<std::endl;
    24          }   
    25  
    26          void destory()
    27          {   
    28              delete _instance;
    29              _instance = nullptr;
    30          }   
    31  
    32          ~Singleton()
    33          {   
    34              std::cout<<"~Singleton()"<<std::endl;
    35          }   
    36  };
    37  Singleton* Singleton::_instance = nullptr;
    38  
    39  int main()
    40  {
    41      Singleton::Instance()->print();
    42      Singleton::Instance()->destory();   
    43  }

    一般的单例模式Demo是没有 destory() 方法的,就导致一个问题,在程序结束之后,该单例对象没有被delete,导致内存泄露,可以通过 valgrind 来查看。

    优点:采用懒汉模式;确实只能产生一个对象,可以防止 Singleton obj = *Singleton::Instance(); 这样的代码产生额外对象;由于 new 操作是线程安全的,所以在多线程环境中也可以保证只产生一个对象;在程序退出前调用 destory() 方法,可避免内存泄露,另外由于 destory() 方法必须由 Singleton::Instance() 调用,所以当该方法被调用时,可以保证 new 操作已经完成。

    缺点:必须在程序结束前调用 Singleton::Instance()->destory();  方法,显得很麻烦;

    另外,也有人认为上面并不是线程安全的,需要加锁。但我不这么认为,如果你确实不放心的话,可以使用饿汉模式避免这个话题:

     1  class Singleton
     2  {
     3      private:
     4          Singleton(){std::cout<<"Singleton()"<<std::endl;};
     5          Singleton(const Singleton&) = delete;
     6          Singleton& operator=(const Singleton&)= delete;
     7          static Singleton* _instance;
     8      
     9      public:
    10          static Singleton* Instance()
    11          {   
    12              return _instance;
    13          }   
    14  
    15          void destory()
    16          {   
    17              delete _instance;
    18              _instance = nullptr;
    19          }   
    20  
    21          ~Singleton()
    22          {   
    23              std::cout<<"~Singleton()"<<std::endl;
    24          }   
    25  };
    26  Singleton* Singleton::_instance = new Singleton();

    静态初始化实例可以保证线程安全,因为静态实例初始化在程序开始时进入主函数之前,就由主线程以单线程方式完成了初始化。

    缺点:但静态初始化的顺序是不一定的,在很多时候要注意这一点,与单纯的此议题无关,除非……两个类都使用了单例模式,且一个类在构造函数中调用了另一个类的对象,而它可能还没有被构造。

    那如何解决上面的那个必须使用 destory() 方法来手动在程序结束前清理堆内存的问题呢?

     1 class Singleton
     2 {
     3     private:
     4         Singleton(){std::cout<<"Singleton()"<<std::endl;};
     5         Singleton(const Singleton&) = delete;
     6         Singleton& operator=(const Singleton&)= delete;
     7     
     8     public:
     9         static Singleton& Instance()
    10         {   
    11             static Singleton _instance;
    12             return _instance;
    13         }   
    14 
    15         ~Singleton()
    16         {   
    17             std::cout<<"~Singleton()"<<std::endl;
    18         }   
    19 };

    在一个函数中使用静态变量,该静态变量在函数第一次被调用时初始化,如果另一个线程在它初始化完成之前试图调用该函数,它必须等待(仅从 c++0x 开始),所以这里是线程安全的。另外当程序结束时,也不会有内存泄露问题。

    同样的,如果质疑线程安全问题,或者未使用 c++0x 编译器,也可以使用饿汉模式,不过同样的:两个类都使用了单例模式,且一个类在构造函数中调用了另一个类的对象,而它可能还没有被构造。所以,最终还是推荐上面的写法,使用 c++0x 编译器,可以解决前面提到的全部问题。

  • 相关阅读:
    【原创】express3.4.8源码解析之Express结构图
    【原创】backbone1.1.0源码解析之View
    【原创】javascript模板引擎的简单实现
    【原创】backbone1.1.0源码解析之Collection
    【原创】when.js2.7.1源码解析
    【原创】backbone1.1.0源码解析之Model
    【原创】backbone1.1.0源码解析之Events
    企业架构研究总结(35)——TOGAF架构内容框架之构建块(Building Blocks)
    企业架构研究总结(34)——TOGAF架构内容框架之架构制品(下)
    企业架构研究总结(33)——TOGAF架构内容框架之架构制品(上)
  • 原文地址:https://www.cnblogs.com/tianyajuanke/p/2977930.html
Copyright © 2020-2023  润新知