- 单例模式概念
单例模式事比较常用的设计模式之一。一般情况下,我们建立的一些类是属于工具性质的,基本不用存储太多的跟自身有关的数据,在这种情况下,每次都去new一个对象,即增加了开销,也使得代码更加臃肿。其实,我们只需要一个实例对象就可以。如果采用全局或者静态变量的方式,会影响封装性,难以保证别的代码不会对全局变量造成影响,而且这样的代码显的很不优雅。 使用全局对象能够保证方便地访问实例,但是不能保证只声明一个对象——也就是说除了一个全局实例外,仍然能创建相同类的本地实例。
我们将默认的构造函数声明为私有的,这样就不会被外部所new了,甚至可以将析构函数也声明为私有的,这样就只有自己能够删除自己了。 - 单例模式的懒汉模式和饿汉模式
懒汉模式:就是不到万不得已是不会去实例化类的,也就是说在第一次用到类实例的时候才会去实例化。
饿汉模式:在单例类定义的时候就实例化了。
优点和缺点:
(1)在访问量比较小的时候,采用懒汉模式。以时间换空间。
(2)因为要进行线程同步,所以在访问量较多的时候,采用饿汉模式,可以实现更好的性能。以空间换时间。
实现:把构造函数定义为私有的或者保护的,再定义一个类的私有的静态指针,指向这个类的唯一实例,再提供一个公有的静态的方法获取该实例。 - 代码实现
《一》饿汉模式
(1)饿汉模式实现一
class SiglenTon //懒汉模式(线程不安全)
{
public:
static SiglenTon *GetInstance()
{
if( ptr == NULL)
{
ptr = new SiglenTon();
}
return ptr;
}
private:
SiglenTon()
{
cout<<"SiglenTon()"<<endl;
}
static SiglenTon *ptr;
};
SiglenTon *SiglenTon::ptr = NULL;
int main()
{
SiglenTon *ptr = SiglenTon::GetInstance();
return 0;
}
该方式实现的单例模式是不安全的,假如有两个线程同时调用GetInstance的时候,同时检测到ptr指针为空,同时创建一个相同的对象,这是错误的。
(2)饿汉模式实现二(加一层互斥锁)
class SigleTon
{
public:
static SigleTon *GetInstance()
{
cout<<"static SigleTon *GetInstance()"<<endl;
pthread_mutex_lock(&mutex);
if( ptr == NULL)
{
ptr = new SigleTon();
}
pthread_mutex_unlock(&mutex);
return ptr;
}
private:
SigleTon()
{
cout<<"SigleTon()"<<endl;
}
static SigleTon *ptr;
};
SigleTon *SigleTon::ptr = NULL;
该方式加了一对互斥锁,线程是安全的,但是对于单线程来说,每次都要加锁解锁,效率不高。而且如果每次判断是否为空都需要被锁定,如果有很多线程的话,就会造成大量线程的阻塞。
(3)饿汉模式实现三(两层互斥锁)
class SigleTon //加双重互斥锁
{
public:
static SigleTon *GetInstance()
{
cout<<"static SigleTon *GetInstance()"<<endl;
pthread_mutex_lock(&mutex);
if( ptr == NULL)
{
pthread_mutex_lock(&mutex);
ptr = new SigleTon();
pthread_mutex_lock(&mutex);
}
pthread_mutex_unlock(&mutex);
return ptr;
}
private:
SigleTon()
{
cout<<"SigleTon()"<<endl;
}
static SigleTon *ptr;
};
SigleTon *SigleTon::ptr = NULL;
(4)饿汉模式实现四(用一个静态内部变量实现懒汉模式)
在Instance函数里定义一个静态的实例,也可以保证拥有唯一实例,在返回时只需要返回其指针就可以了。推荐这种实现方法,真得非常简单。
class SiglenTon
{
public:
static SiglenTon *Instance()
{
cout<<"static SiglenTon Instance()"<<endl;
pthread_mutex_lock(&mutex);
static SiglenTon msigleton;
pthread_mutex_lock(&mutex);
return &msigleton;
}
private:
SiglenTon()
{
cout<<"SiglenTon()"<<endl;
}
static SiglenTon *ptr;
};
SiglenTon *SiglenTon::ptr = NULL;
《二》懒汉模式
在类外初始化指针的时候就创建一个对象。懒汉模式是线程安全的,不用加互斥锁,因为在当类定义的时候定义了一个对象,对类进行了初始化。后面不管哪个线程调用Instance函数,都只不过是返回了一个对象的指针而已。
class SigleTon
{
public:
static SigleTon *GetInstance()
{
cout<<"static SigleTon *GetInstance()"<<endl;
if( ptr == NULL)
{
return ptr;
}
}
private:
SigleTon()
{
cout<<"SigleTon()"<<endl;
}
static SigleTon *ptr;
};
SigleTon *SigleTon::ptr = new SigleTon();