单例模式产生的原因:
每个类都能产生很多对象,而在某些场景下,我们仅需要一个实例,所有的操作都针对这个单例来进行。
实际构建中,我们需要保证一个类仅有一个实例,并提供一个访问它的全局访问点。
常用两种:
懒汉式:
class single
{
public:
~single()
{
if(mInstance)
{
delete mInstance;
}
};
static single* getInstance()
{
if(mInstance == NULL)
{
mInstance = new single();
}
return mInstance;
}
void printArr()
{
printf("%p ",this);
}
private:
single(){};
single(const single&);
single& operator = (const single&);
static single* mInstance;
};
single* single::mInstance = NULL;
void main()
{
single::getInstance()->printArr();
single::getInstance()->printArr();
cin.get();
}
得到结果: 00785D70 00785D70
我们的到这个实例后任何的操作都是基于这单个的对象进行。
先讲一下为什么是static的数据成员mInstance和static的函数成员getInstance
是为了把这个对象存放在全局的存储区内,供所有需要调用的地方调用。
另外补充一下,静态数据成员(非静态常量成员)不能在类中初始化,因为静态数据成员为所有的该类对象所共有,没有必要在每个类构建对象时去重新赋值,这与所有该类维护同一份static变量本身也是违背的。
另外为了保证单例的唯一性,我把它的构造函数定义在了private部分,另外将它的拷贝构造函数和赋值函数也声明为私有,这样避免了除了getIntance()得到类的对象其他所有通过隐式构造产生对象的可能。
这种懒汉式的单例模式在单线程中是毫无问题的,但在多线程中仍存在问题,那就是两个线程可能同时判断为NULL,这样就new了两个对象。
饿汉式:
class single
{
public:
~single()
{
if(mInstance)
{
delete mInstance;
}
};
static single* getInstance()
{
return mInstance;
}
void printArr()
{
printf("%p ",this);
}
private:
single(){};
single(const single&);
single& operator = (const single&);
static single* mInstance;
};
single* single::mInstance = new single();
在程序启动时即去new这个实例的指针,后续调用时也就不存在同时为NULL的这种情景。懒汉式虽然存在线程安全的问题,确可以节省空间。工作中为了避免线程安全问题和懒得加锁,一般还是饿汉式的省心一点。不过在linux平台上,跑的时候偶尔发现main函数调用到
getInstance()了,全局外部的new还没有new出来导致使用了空指针,至今还没搞明白,可能cpp文件比较多,初始化跑的顺序比单个文件复杂一些。
因此在此基础上还有一种加锁的懒汉式单例写法:
方式一:两次判空,getInstance()中,第一次判空的时候加锁,第二次判空new 对象,new 完之后解锁
方式二:利用静态变量只初始化一次的特性构建单例,懒得写了,都差不多,copy一份
这里仍然要注意的是局部变量初始化的线程安全性问题,在C++0X以后,要求编译器保证静态变量初始化的线程安全性,可以不加锁。但C++ 0X以前,仍需要加锁。
class Singleton
{
public:
static Singleton* GetInstance()
{
Lock(); // not needed after C++0x
static Singleton instance;
UnLock(); // not needed after C++0x
return &instance;
}
private:
Singleton() {};
Singleton(const Singleton &);
Singleton & operator = (const Singleton &);
};
其中lock()为线程锁锁,UnLock为线程锁解锁,两个线程同时在跑时,当一边lock住时,另一线程将处于等待状态,unlock时,结束等待,这样就避免了同时new这种情况的产生
单例是各种设计模式中经常用到的且非常好用的类对象的调用模式。