1、细说Singleton模式 创建、多线程与销毁,3.Singleton销毁在这里,我们就到了Singleton最简单也最复杂的地方了。为什么说它简单?我们根本可以不理睬创建的对象m_pInstance的销毁啊。因为虽然我们一直没有将Singleton对象删除,但不会造成内存泄漏。为什么这样说呢?因为只有当你分配了累积行数据并丢失了对他的所有reference是,内存泄漏才发生。而对Singleton并不属于上面的情况,没有累积性的东东,而且直到结束我们还有它的引用。在现代操作系统中,当一个进程结束后,将自动将该进程所有内存空间完全释放。(可以参考effective C+条款10,里面讲述了内存
2、泄漏)。但有时泄漏还是存在的,那是什么呢?就是资源泄漏。比如说如果该Singleton对象管理的是网络连接,OS互斥量,进程通信的handles等等。这时我们就必须考虑到Singleton的销毁了。谈到销毁,那可是一个复杂的课题(两天三夜也说不完_ 开玩笑的啦,大家轻松一下嘛)。,我们需要在恰当的地点,恰当的时机删除Singleton对象,并且还要在恰当的时机创建或者重新创建Singleton对象。在我们的“解二”中,在程序结束时会自动调用Singleton的析构函数,那么也将自动释放所获取的资源。在大多数情况下,它都能够有效运作。那特殊情况是什么呢?我们以KDL(keyboard,displ
3、ay,log)模型为例,其中K,D,L均使用Singleton模式。只要keyboard或者display出现异常,我们就必须调用log将其写入日志中,否则log对象不应该创建。对后面一条,我们的Singleton创建时就可以满足。,在前面我们已经说到,在产生一个对象时(非用new产生的对象),由编译器自动调用了atexit(_DestroyObject)函数来实现该对象的析构操作。而C+对象析构是LIFO进行的,即先产生的对象后摧毁。如果在一般情况下调用了log对象,然后开始销毁对象。按照“后创建的先销毁”原则:log对象将被销毁,然后display对象开始销毁。此时display在销毁发现
4、出现异常,于是调用log对象进行记录。但事实上,log对象已经被销毁,那么调用log对象将产生不可预期的后果,此问题我们称为Dead Reference。所以前面的解决方案不能解决目前我们遇到的问题。Andrei Alexandrescu提出了解决方案,称为Phoenix Singleton(取自凤凰涅磐典故),88./*解四*/89.class Singleton90.91.public:92.static Singleton ,98.else99.Create();100.101.UnLock(m_mutex);102.103.return *m_pInstance;104.105.pri
5、vate:106.static volatitle Singleton *m_pInstatnce;,107.static bool m_destroyed;108.private:109.Singleton();110.Singleton(const Singleton,114.m_destroyed = true;115.116.static void Create()117.static Singleton sInstance;118.m_pInstanace = ,122.new (m_pInstance) Singleton;123.atexit(KillPhoenixSinglet
6、on);124.m_destroyed = false;125.126.void KillPhoenixSingleton()127.m_pInstance-Singleton();128.129.,130.Singleton *Singleton:m_pInstatnce = NULL;131.bool m_destroyed =false;132.请注意此处OnDeadReference()中所使用的new操作符的用法:是所谓的placement new操作,它并不分配内存,而是在某个地址上构造一个新对象。,这是解决Dead Reference方法之一。如果此时keyboard或者disp
7、lay对象也需要处理Dead Reference问题时,那么上面的OnDeadReference将被频繁调用,效率将会很低。即该问题为:需要提供一种解决方案,用于处理对象的建立过程可以不按照“先创建会销毁”的原则,而应该为其指定一个销毁顺序。 java培训 http:/ 聪明的Andrei Alexandrescu提出了一个“带寿命的Singleton”解决方案。该方案的思想是:利用atexit()的特性;在每次创建一个对象后,将该对象放入到一个链表中(该链表是按照销毁顺序排训的),并同时调用atexit()注册一个销毁函数;该销毁函数从链表中获取最需要销毁的对象进行销毁。(懒病又犯了L。该模式的实现请各位看官自行实现,可以参考C+设计新思维一书,Andrei Alexandrescu着),