单例模式的要点有三个;一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。具体实现角度来说,就是以下三点:一是单例模式的类只提供私有的构造函数,二是类定义中含有一个该类的静态私有对象,三是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。
《设计模式》一书中给出了一种很不错的实现,定义一个单例类,使用类的私有静态指针变量指向类的唯一实例,并用一个公有的静态方法获取该实例。
C++代码:
class Singleton
{
private:
Singleton() //构造函数是私有的
{
}
static Singleton *m_pInstance;
public:
static Singleton * GetInstance()
{
if(m_pInstance == NULL) //判断是否第一次调用
m_pInstance = new Singleton();
return m_pInstance;
}
};
不知你有没有发现这样的实现都不会出现问题。m_pInstance指向的空间什么时候释放呢?
可以在程序结束时调用GetInstance(),并对返回的指针掉用delete操作。这样做可以实现功能,但容易出错。因为这样的附加代码很容易被忘记,而且也很难保证在delete之后,没有代码再调用GetInstance函数。
一个妥善的方法是让这个类自己知道在合适的时候把自己删除,使其在恰当的时候被自动执行。
我们知道,程序在结束的时候,系统会自动析构所有的全局变量。系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。利用这个特征,我们可以在单例类中定义一个这样的静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。如下面的代码中的Garbo类:
class Singleton
{
private:
Singleton()
{
}
static Singleton *m_pInstance;
class Garbo //它的唯一工作就是在析构函数中删除CSingleton的实例
{
public:
~Garbo()
{
if(Singleton::m_pInstance)
delete Singleton::m_pInstance;
}
};
static Garbo Garbo; //定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数
public:
static Singleton * GetInstance()
{
if(m_pInstance == NULL)
m_pInstance = new Singleton();
return m_pInstance;
}
};
程序运行结束时,系统会调用CSingleton的静态成员Garbo的析构函数,该析构函数会删除单例的唯一实例。
考虑到线程安全、异常安全,还需要进行优化:
C++代码:
class Lock
{
private:
CCriticalSection m_cs;
public:
Lock(CCriticalSection cs) : m_cs(cs)
{
m_cs.Lock();
}
~Lock()
{
m_cs.Unlock();
}
};
class Singleton
{
private:
Singleton();
Singleton(const Singleton &);
Singleton& operator = (const Singleton &);
public:
static Singleton *Instantialize();
static Singleton *pInstance;
static CCriticalSection cs;
};
Singleton* Singleton::pInstance = 0;
Singleton* Singleton::Instantialize()
{
if(pInstance == NULL)
{ //double check
Lock lock(cs); //用lock实现线程安全,用资源管理类,实现异常安全
//使用资源管理类,在抛出异常的时候,资源管理类对象会被析构,析构总是发生的无论是因为异常抛出还是语句块结束。
if(pInstance == NULL)
{
pInstance = new Singleton();
}
}
return pInstance;
}
之所以在Instantialize函数里面对pInstance 是否为空做了两次判断,因为该方法调用一次就产生了对象,pInstance == NULL大部分情况下都为false,如果按照原来的方法,每次获取实例都需要加锁,效率太低。而改进的方法只需要在第一次 调用的时候加锁,可大大提高效率。
使用单例的优点:
1、对于那些比较耗内存的类,只实例化一次可以大大提高性能,尤其是在移动开发中。
2、保持程序运行的时候该中始终只有一个实例存在内存中
Android使用场景:
1、在Android中使用可以管理activity的工具类。
2、用单例模式创建一个Activity管理对象,进而管理Activity退出。
Cocos2d使用场景:
CCDirector类是单例的,它以栈的方式处理scenes的调用,并且知道当前哪个scene是激活状态。
拓展链接:
http://devbean.blog.51cto.com/448512/203501/
http://cantellow.iteye.com/blog/838473