探讨单件模式(1)

原创 2007年10月14日 21:31:00

有关设计模式的东西很早就听说过一些了,但因为以前的开发经历中对设计模式很少有直接的需求,所以也没花太多心思研究过.前不久公司组织Training,内容是Design Pattern的Singleton(单件模式).原本以为Singleton是一个很simple的模式,这个培训应该也不会有太深入的内容,不过想想自己毕竟没有系统地研究过设计模式,所以还是去参加了,听完training以后才发现原来一个Singleton里面会包括这么丰富的内容,这实在是自己始料不及的.

写出一个基本的Singleton的例子程序是很简单的,有一些基本的编程基础和Object Oriented的概念,读一下有关Singleton的介绍就可以写出来类似于下面的程序.


例子 1:
class SingletonExample {
 private:
   SingletonExample()
   {
   ......
  }
  SingletonExample()
  {
   ......
  }
  SingletonExample * Instance( void )
  {
   return &singleInst;
  }
 private:
  static SingletonExample singleInst;  
};
SingletonExample SingletonExample::singleInst;


这个例子程序虽然不完善, 但基本上体现了Singleton的风貌.每次调用Instance接口都会返回唯一的SingletonExample对象.

但是Singleton是否就止步于此了?

分析一下这个例子程序中可能存在的问题:

无论SingletonExample类的Instance是否会被调用,都会为SingletonExample的静态对象分配内存空间,这就带来无谓的内存消耗.

针对这一点问题我们可能会直观地想到作出如下改进:

例子 2:
class SingletonExample {
 private:
  SingletonExample()
  {
   ......
  }
  ~SingletonExample()
  {
   
  }
 public:
  SingletonExample * Instance(void)
  {
   static SingletonExample singInst;
   return &singInst;
  }
};
为了便于理解,将编译器为这个class可能生成的实际目标码描述如下:

class SingletonExample {
 private:
  SingletonExample()
  {
   ......
  }
  ~SingletonExample()
  {
   
  }
 public:
  SingletonExample * Instance(void)
  {
   static SingletonExample singleInst;
   static bool objInited = false;  // 编译器生成的内部变量
   
   if ( !objInited ) { // 编译器插入的代码,用于完成函数内部静态对象的初始化工作
    objInited = true;
    singleInst.SingletonExample();
    atexit( singleInst.~SingletonExample() ); // 将针对singleInst对象的destructor注册到
                                                                             //程序退出会执行的函数链中,以确保静态对象在程序退
                                                                             //出时会被析构.
                                                                             // 注: 不同的平台下对于函数内部的静态对象
                                                                             //的destructor的调用手段可能会有所不同,这里
                                                                             //只是给出了Linux平台下使用g++可能生成
                                                                             //的目标码!
   }
  
   return &singleInst;
  }
};

与例子1相比,由于 singleInst 是Instance()方法内部的一个静态对象,所以只有在Instance()被调用到时才会
为singleInst对象分配内存资源,这就解决了例子 1 中的问题.

但是例子2是不是就完美地实现了单件模式了呢?

例子2中可能存在Dead Reference 的问题!所谓Dead Reference,简单来说,就是在程序中调用 SingletonExample::Instance()返回
SingletonExample对象的指针时,这个指针指向的是一个已经被析构函数释放了的无效对象!

下面给出一个具体的场景来描述Dead Reference Problem.

现在有两个采用单件模式实现的类:

Logger:日志管理类,用来记录程序运行过程中的日志信息

ErrorHandling: 错误管理类,用来输出错误信息.

在ErrorHandling的析构函数内部会调用Logger将错误信息写入日志文件中.

现在有一个应用程序中使用到了Logger和ErrorHandling,在程序的初始化过程中,试图分配1个G的内存,但内存申请失败,于是程序调用exit()退出,由于Logger和ErrorHandling的Destructor均是通过atexit()完成注册的,因此,Logger和ErrorHandling的单件对象均会被析构,如果Logger在ErrorHandling之后析构,一切正常,但如果在ErrorHandling析构之前Logger就已经被析构了,由于ErrorHandling的析构函数中会访问Logger单件对象时,Dead Reference Problem就产生了!

怎样解决 Dead Reference Problem呢?一个不算优雅但比较直接的方式如例子4所示.


例子 3:
class SingletonExample {
 private:
  SingletonExample()
  {
   ......
  }
  ~SingletonExample()
  {
   // This destructor would never be called!
  }
 public:
  SingletonExample * Instance(void)
  {
   if (NULL == pInstance) {
    pInstance = new Singleton;
   }
   return pInstance;
  }
 private:
  static SingletonExample * pInstance;  
};


可以注意到例子3中SingletonExample的单件对象对应的只有一个new操作符,没有delete操作符,所以这个单件对象不会被释放,因此Dead Reference Problem不会出现.但例子3的解决方案也并非完美.


例3最主要的问题是:
 首先,new操作没有对应的delete操作.这不是一种良好的编程风范.
 而更重要的问题则是虽然由于单件模式保证了对于Singleton的Instance接口来说,在应用程序的生存期限里只会有一份SingletonExample对象存在,不会有memory leak存在.但是由于Instance()内部调用new操作符所分配出来的对象不会执行destructor,会存在资源泄漏的隐患.想像一下在Singleton对象的构造函数内部执行了一些申请操作系统资源(诸如信号量,网络套接字之类)的动作,但相应的释放操作却不会被执行,这样就很可能在应用程序退出时带来系统资源的泄漏.

可以看出一个看似简单的Singleton模式在具体实现中会涉及到如此多的问题,说设计模式是前人智慧的结晶实在是此言不虚,关于Singleton模式的进一步探讨会在后面的blog中继续.

探讨Android6.0及以上系统APP常驻内存(保活)实现-复活篇

随着AlarmManager唤醒、native进程拉起等方式的失效,APP常驻内存的时代将不复存在,尤其是当APP进程被杀死后,基本很难将其拉起。从用户的角度来讲,这是一种很好的发展,而这一切应该归功...
  • AndrExpert
  • AndrExpert
  • 2017年07月15日 17:02
  • 8437

Android平台Camera实时滤镜实现方法探讨(七)--滤镜基本制作方法(一)

本篇简单介绍一些滤镜的制作方法
  • oShunz
  • oShunz
  • 2015年12月08日 09:13
  • 5743

Android平台Camera实时滤镜实现方法探讨(八)--滤镜基本制作方法(二)简单美颜滤镜

美白+简单磨皮
  • oShunz
  • oShunz
  • 2015年12月16日 09:22
  • 7052

C#面向对象设计模式纵横谈-1.Singleton 单件(创建型模式)

  • 2008年05月09日 11:13
  • 8.2MB
  • 下载

深入浅出设计模式(1)——单件模式(Singleton Pattern)

1          模式简介 singleton是设计模式家族中的小弟弟,重量轻,没有复杂的经历,很单纯,所以叫单件(just a joke)。因为简单,容易被人洞悉,所以经常被人唤来唤去,sin...
  • xxq_2011
  • xxq_2011
  • 2012年08月08日 21:57
  • 500

【设计模式基础】创建型模式 - 1 - 单件(Singleton)

1. 模式意图 保证类仅有一个实例,并提供一个访问它的全局访问点。 2. 模式定义 Singleton: 定义一个Instance操作,允许客户访问它的唯一实例。Instance是一个类操作;...
  • robinjwong
  • robinjwong
  • 2013年12月30日 09:38
  • 655

单件模式操作access

  • 2011年04月29日 18:36
  • 58KB
  • 下载

单件模式源代码

  • 2013年11月06日 10:57
  • 6KB
  • 下载

单件模式厂类

  • 2013年09月11日 16:56
  • 2KB
  • 下载

单件模式下用的加锁(互斥)

  • 2013年12月27日 16:20
  • 29KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:探讨单件模式(1)
举报原因:
原因补充:

(最多只允许输入30个字)