单例模式

直接来看代码:

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 编译器,可以解决前面提到的全部问题。

原文链接: https://www.cnblogs.com/tianyajuanke/archive/2013/03/24/2977930.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/81834

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月9日 下午8:16
下一篇 2023年2月9日 下午8:16

相关推荐