软件开发常用设计模式—单例模式总结

发表于:2015-4-08 10:43

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:木棉和木槿    来源:51Testing软件测试网采编

分享:
  再看饿汉式的单例模式,之前看了懒汉式的单例类,是线程不安全的,通过加锁(双重锁),实现线程安全
  回忆饿汉式单例类:直接在静态区初始化 instance,然后通过 get 方法返回,这样这个类每次直接先生成一个对象,好像好久没吃饭的饿汉子,急着吃饭一样,急切的 new 对象,这叫做饿汉式单例类。
1 class Singleton
2 {
3 public:
4     //get 方法
5     static Singleton * getInstance(){
6         //返回一个实例化的对象
7         return instance;
8     }
9 private:
10     //声明一个静态的实例
11     static Singleton *instance;
12     //私有构造函数
13     Singleton(){
14
15     }
16 };
17 //每次先直接实例化instance,get 方法直接返回这个实例
18 Singleton * Singleton::instance = new Singleton();
  注意:静态初始化实例可以保证线程安全,因为静态实例初始化在程序开始时进入主函数之前,就由主线程以单线程方式完成了初始化!饿汉式的单例类,也就是静态初始化实例保证其线程安全性,故在性能需求较高时,应使用这种模式,避免频繁的锁争夺。
  继续看单例模式
  上面的单例模式没有 destory() 方法,也就是说,貌似上面的单例类没有主动析构这个唯一实例!然而这就导致了一个问题,在程序结束之后,该单例对象没有delete,导致内存泄露!下面是一些大神的方法:一个妥善的方法是让这个类自己知道在合适的时候把自己删除,或者说把删除自己的操作挂在操作系统中的某个合适的点上,使其在恰当的时候被自动执行。
  我们知道,程序在结束的时候,系统会自动析构所有的全局变量。事实上,系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。如果在类的析构行为中有必须的操作,比如关闭文件,释放外部资源,那么上面的代码无法实现这个要求。我们需要一种方法,正常的删除该实例。利用这些特征,我们可以在单例类中定义一个这样的静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。如下面的代码中的Garbage类:
1 class Singleton
2 {
3 public:
4     //get 方法
5     static Singleton * getInstance(){
6         //判断单例否
7         if (NULL == instance) {
8             instance = new Singleton();
9         }
10         //返回一个实例化的对象
11         return instance;
12     }
13     //c++ 嵌套的内部类,作用是删除单例类对象,Garbage被定义为Singleton的内嵌类,以防该类被在其他地方滥用。
14     class Garbage
15     {
16     public:
17         ~Garbage(){
18             if (Singleton::instance != NULL) {
19                 cout << "单例类的唯一实例被析构了" << endl;
20                 delete Singleton::instance;
21             }
22         }
23     };
24
25 private:
26     //单例类中声明一个触发垃圾回收类的静态成员变量,它的唯一工作就是在析构函数中删除单例类的实例,利用程序在结束时析构全局变量的特性,选择最终的释放时机;
27     static Garbage garbage;
28     //声明一个静态的实例
29     static Singleton *instance;
30     //单例类的私有构造函数
31     Singleton(){
32         cout << "调用了单例类的构造函数" << endl;
33     }
34     //单例类的私有析构函数
35     ~Singleton(){
36         cout << "调用了单例类的析构函数" << endl;
37     }
38 };
39 //初始化内部的静态变量,目睹是启动删除的析构函数,如果不初始化,就不会被析构
40 //内部类可以访问外部类的私有成员,外部类不能访问内部类的私有成员!
41 Singleton::Garbage Singleton::garbage;
42 //初始化instance为 null
43 Singleton * Singleton::instance = NULL;
44
45 int main(void)
46 {
47     Singleton *a = Singleton::getInstance();
48     Singleton *b = Singleton::getInstance();
49     Singleton *c = Singleton::getInstance();
50
51     if (a == b) {
52         cout << "a = b" << endl;
53     }
54
55     return 0;
56 }
  调用了单例类的构造函数
  a = b
  单例类的唯一实例被析构了
  调用了单例类的析构函数
  Program ended with exit code: 0
  类Garbage被定义为Singleton的内嵌类,以防该类在其他地方滥用,程序运行结束时,系统会调用Singleton的静态成员garbage的析构函数,该析构函数会删除单例的唯一实例,使用这种方法释放单例对象有以下特征:
  1、在单例类内部定义专有的嵌套类;
  2、在单例类内定义私有的专门用于释放的静态成员;
  3、利用程序在结束时析构全局变量的特性,选择最终的释放时机;
  4、使用单例的代码不需要任何操作,不必关心对象的释放。
  其实,继续想单例类的实现,有的人会这样做:
  在程序结束时调一个专门的方法,这个方法里判断实例对象是否为 null,如果不为 null,就对返回的指针掉用delete操作。这样做可以实现删除单例的功能,但不仅很丑陋,而且容易出错。因为这样的附加代码很容易被忘记,而且也很难保证在delete之后,没有代码再调用GetInstance函数。不推荐直接的删除方法。
  继续查看单例模式:单例模式在实际开发过程中是很有用的,单例模式的特征总结:
  1、一个类只有一个实例
  2、提供一个全局访问点
  3、禁止拷贝
  逐个分析:
  1、实现只有一个实例,需要做的事情:将构造函数声明为私有
  2、提供一个全局访问点,需要做的事情:类中创建静态成员和静态成员方法
  3、禁止拷贝:把拷贝构造函数声明为私有,并且不提供实现,将赋值运算符声明为私有,防止对象的赋值
43/4<1234>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号