最近学习了下《head first设计模式》,先对单例模式做个简单的总结。
为什么不用全局变量?
单例模式用一句话概括就是只产生一个对象,那为啥不用全局变量来代替单例模式呢?
全局变量是程序在一开始就创建好对象,如果这个对象非常耗费资源,程序的执行又用不到它,那就太浪费了。而单例可以在需要它的时候再创建它。
使用场景
线程池,缓存,处理偏好设置,注册表的对象,日志对象,充当打印机、显卡等设备的驱动程序的对象。
实现
一句话,构造函数为私有就可以实现了。
看下c++中的一个简单实现:
singleton.h
//2013-4-10 10:07 //单例设计模式 class MySingleton { public: ~MySingleton(); static MySingleton* GetInstance(); private: MySingleton(); };
singleton.cpp
#include "singleton.h" MySingleton::~MySingleton() { } MySingleton::MySingleton() { } MySingleton* MySingleton::GetInstance() { static MySingleton* pInstance = new MySingleton(); return pInstance; }
问题1
实现很简单,这样就保证只会产生一个实例了。但是这样会产生一个问题。什么问题呢???
可以写个客户端的代码验证:
int main(int argc,char* argv[]) { MySingleton* pMySingleton = MySingleton::GetInstance(); MySingleton* pMySingleton1 = MySingleton::GetInstance(); delete pMySingleton; delete pMySingleton1; return 0; }
是的,释放内存就会出问题了。
这样不得不考虑重新实现了,怎么才能保证释放一次呢?
有一个技巧:
参考了http://www.cnblogs.com/binxindoudou/p/3261082.html
我的实现如下:
//singleton.h #pragma once #include <iostream> using namespace std; class MySingleton { private: static MySingleton* pInstance; public: static MySingleton* GetInstance(); class SingletonGarbo // 它的唯一工作就是在析构函数中删除CSingleton的实例 { public: ~SingletonGarbo() { //测试用 cout<<"SingletonGarbo::~SingletonGarbo()"<<endl; if (0 != MySingleton::pInstance) { delete MySingleton::pInstance; MySingleton::pInstance = 0; } } }; private: MySingleton(); ~MySingleton();//防止在不合适的地方释放了 };
//singleton.cpp #include "singleton.h" MySingleton* MySingleton::pInstance = 0; MySingleton::SingletonGarbo garbo; MySingleton::~MySingleton() { } MySingleton::MySingleton() { } MySingleton* MySingleton::GetInstance() { if ( pInstance == 0) { pInstance = new MySingleton(); } return pInstance; }
这样就保证在程序结束的时候释放单例对象了。应该还有别的比较好的实现,类中类总感觉不好,还没想到。如果有了补充。
问题2
还会不会有问题呢?
多线程。是的,GetInstance()方法中多线程可能会出问题。
if ( pInstance == 0) //线程1访问,刚好到此时间片轮换 { pInstance = new MySingleton(); } return pInstance;
//线程2开始执行 if ( pInstance == 0) { pInstance = new MySingleton(); } return pInstance;
这样就产生两个对象了。
解决办法是同步即可,伪代码:
MySingleton* MySingleton::GetInstance() { lock(); if ( pInstance == 0) { pInstance = new MySingleton(); } unlock(); return pInstance; }
但是这样效率太低了,每次获取对象的时候都要同步,一个同步会使效率低100倍。
可以第一次进行同步,下次就不用了。伪代码:
MySingleton* MySingleton::GetInstance() { if ( pInstance == 0) { lock(); if ( pInstance == 0) { pInstance = new MySingleton(); } unlock();} return pInstance; }