好久不见,又来到了这里。。
博客中断了如此之久,当然是有借口的。首先是单件模式的特殊性,它涉及到多线程!OMG,小弟才疏学浅,还从未写过一个多线程的程序,多进程的倒是写过一个,不过跟该模式就没太大关系了。另外,前一篇工厂模式,费了九牛二虎之力,却只弄清楚一个对象的创建,未免太桑人心了。。再者,寒假一放,就堕落了。。每天睡到中午,整体出去玩,哪有心思看设计模式。。。
凡此种种,都成为我没有更新博客的借口。但饭要吃,博客也还是要写,今天恨下心来,果断先放弃多线程的部分。从延迟初始化,析构等等概念入手,先写它一篇,将来弄清楚了多线程,再写一篇。。
单件模式,都说是最简单的设计模式,其实也是,你要做的事情就是私有化构造函数,赋值函数,设置一个唯一的静态变量,再写一个静态函数来获取这个唯一的静态变量,就成为单件模式了。
如何做呢,先看看Java可以怎么做(再做个声明,以下讨论,没有考虑多线程情况):
public class Singleton { // 声明并初始化一个静态变量,由于是静态的,该变量唯一 private static Singleton single = new Singleton(); // 初始化 private Singleton() { System.out.println("Singleton constructor..."); } // 模拟析构函数实现 public void finalize() { System.out.println("Singleton destructor..."); } // 返回唯一的静态变量 public static Singleton getInstance() { return single; } // 普通方法 public void method() { System.out.println("The method of Singleton..."); } }
main函数如下:
System.out.println("Appliction started..."); Singleton sc1,sc2; System.out.println("Start using instance..."); sc1 = Singleton.getInstance(); sc1.method(); sc2 = Singleton.getInstance(); sc2.method(); System.out.println("Appliction leaved...");
我们看到,这种方法是先声明一个静态变量并初始化为空,获取实例时判断,若为空,说明仍未创建,则实例化该类给静态,并返回该静态变量。其运行结果如下:
Appliction started...
Start using instance...
Singleton constructor...
The method of Singleton...
The method of Singleton...
Appliction leaved...
可以看到,Singleton只被初始化了一次。
至于为什么没有调用finalize 方法。也许是Java一些内部机制决定的,我不继续研究了,毕竟析构也不是Java应该关心的内容。
Java代码实现单例模式,我不做过多介绍了,毕竟不是我熟悉的语言,Head First上的说明也非常详细。
下面就开始考虑如何移植到c++上。
受Java代码影响,首先想到的是使用静态指针:
class Singleton { private: static Singleton* single; Singleton() { std::cout << "Singleton constructor..." << std::endl; } // avoid to use copy constructor function. Singleton(const Singleton&); const Singleton& operator=(const Singleton&); public: ~Singleton() { std::cout << "Singleton destructor..." << std::endl; } // returns an unique instance static Singleton* getInstance() { if (single == NULL) { single = new Singleton(); } return single; } void method() { std::cout << "The method of Singleton..." << std::endl; } }; Singleton* Singleton::single = NULL;
其运行结果与Java相同。
但是,c++没有垃圾回收器!把内存的释放交给OS,让程序运行结束时再释放?根据单件模式的思想,其实这样做也是有一定道理的,因为单件模式中的单件,一定是要等整个程序运行结束才会被释放。所以,new了以后不delete实际上也是可以接受的。
但是心里总有疙瘩,所以查阅了很多资料,发现了智能指针。
智能指针就是被设计出来解决c++没有垃圾收集器这一缺陷的。智能指针能使对象在没有引用时,自动销毁。具体实现我就不详述了,详情可以参阅百度百科——智能指针中的内容。
智能指针在 <memory> 中定义,用法如下:
class Singleton { private: static std::auto_ptr<Singleton> single; Singleton() { std::cout << "Singleton constructor..." << std::endl; } // avoid to use copy constructor function. Singleton(const Singleton&); const Singleton& operator=(const Singleton&); public: ~Singleton() { std::cout << "Singleton destructor..." << std::endl; } // returns an unique instance static Singleton* getInstance() { if (single.get() == NULL) { single.reset( new Singleton() ); } return single.get(); } void method() { std::cout << "The method of Singleton..." << std::endl; } }; std::auto_ptr<Singleton> Singleton::single(NULL);
使用了智能指针以后,运行结果如下:
Appliction started...
Start using instance...
Singleton constructor...
The method of Singleton...
The method of Singleton...
Appliction leaved...
Singleton destructor...
可以看到,在程序执行完成后,单例被析构了,很完美~
受到一些资料的启发,想起c++是可以直接声明静态变量的!
于是有了如下代码:
class Singleton { private: static Singleton single; Singleton() { std::cout << "Singleton constructor..." << std::endl; } // avoid to use copy constructor function. Singleton(const Singleton&); const Singleton& operator=(const Singleton&); public: ~Singleton() { std::cout << "Singleton destructor..." << std::endl; } // returns an unique instance static Singleton* getInstance() { return &single; } void method() { std::cout << "The method of Singleton..." << std::endl; } }; Singleton Singleton::single;
直接声明一个单件,在获取实例时,返回其地址就行了。这样看来,智能指针似乎没有多大的意义了。。
其结果如下:
Singleton constructor...
Appliction started...
Start using instance...
The method of Singleton...
The method of Singleton...
Appliction leaved...
Singleton destructor...
可以看到,与之前唯一的不同是,单件的构造在程序运行之前了。。这样的话,如果这个单件很大,就会造成不必要的浪费,这当然是我们想避免的。
于是我想,局部静态变量能否达到效果呢?
class Singleton { private: // 不再声明静态成员变量 Singleton() { std::cout << "Singleton constructor..." << std::endl; } ... public: // returns an unique instance static Singleton* getInstance() { // 声明为静态局部变量,只在第一次进入函数时初始化 static Singleton single; return &single; } ... };
经测试,结果如下:
Appliction started...
Start using instance...
Singleton constructor...
The method of Singleton...
The method of Singleton...
Appliction leaved...
Singleton destructor...
perfect! 初始化,析构,全都是那么称心如意。也许这就是c++中最简单的实现方案?(不知道多线程会造成什么影响-_-||)
回过头去看Java,就发现原来Java不支持局部静态变量。。。
话说回来,如果多线程中,局部静态变量可以被初始化多次的话,这最简单的方案就无法使用了,而且还不易扩展,也许多线程中,智能指针还能派上用场。
待我了解了多线程以后,再回来研究。。。