单例模式
只生成一个对象实例,减少了系统的开销;方便数据访问。
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
系统中如果一个类仅有一个实例,可以用来存储数据。全局访问
单例实现
1、将该类的构造方法定义为私有方法,这样其他处代码就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。
2、在该类中提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。
懒汉式模式
不加锁
顾名思义,不到万不得已不去实例化类,也就是说在第一次用到实例的时候才回去实例化。
在访问量较小时,采用懒汉式实现。这是以时间换空间。
class Singleton1
{
public:
static Singleton1 *GetInstance()
{
if (instance == NULL)
{
instance = new Singleton1();
return instance;
}
}
private:
Singleton1(){};
Singleton1(const Singleton1&){};//禁止拷贝
Singleton1& operator = (const Singleton1&){};//禁止赋值
static Singleton1 *instance;
};
Singleton1* Singleton1::instance = NULL;
在懒汉模式下,再定义instance时先等于NULL,在调用GetInstance()方法时,在判断是否要赋值。这种模式下,并非是线程安全的,因为多个线程同时调用GetInstance()方法,就可能导致产生多个实例。要实现线程安全,就必须加锁。
加锁
class Singleton
{
private:
Singleton(){
pthread_mutex_init(&mutex);
};
Singleton(const Singleton&){};
Singleton& operator=(const Singleton&){};
static Singleton* instance;
public:
static pthread_mutex_t mutex;
static Singleton* GetInstance();
};
pthread_mutex_t Singleton::mutex;
Singleton* Singleton::instance = NULL;
Singleton* Singleton::GetInstance(){
if(instance == NULL){
pthread_mutex_lock(&mutex);
if(instance == NULL)
instance = new Singleton();
pthread_mutex_unlock(&mutex);
}
return instance;
}
饿汉式模式
线程安全:还未使用变量时,已经对instance进行赋值,就像很饥饿的感觉。在多线程环境下肯定是线程安全的,因为不存在多线程实例化的问题。
class Singleton
{
public:
static Singleton* GetInstance()
{
return instance;
}
private:
Singleton();
Singleton(const Singleton&){};
Singleton& operator=(const Singleton&){};
static Singleton *instance;
};
Singleton *Singleton::instance = new Singleton();
饿了肯定就会饥不择食,所以在单例类定义的时候就进行实例化。
由于要进行线程同步,所以在访问量比较大或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。
单例模式的优点
1、 在内存中只有一个对象,节省内存空间。
2、 避免频繁的创建销毁对象,可以提高性能。
3、 避免对共享资源的占用。
4、 可以全局访问。
适应的场景
1、 需要频繁实例化然后销毁的对象。
2、 创建对象耗时过多或者耗资源过多,但又经常用到的对象。
3、 有状态的工具类对象。
4、 频繁访问数据库或文件的对象。
5、 以及其他要求只有一个对象的场景。
注意
1、 实例控制 :单例模式会阻止其他对象实例化自己的单例对象的副本,从而确保所有对象都访问唯一实例。
2、 灵活性 :因为类控制实例化过程,所以类可以灵活更改实例化过程。
3、 开销:虽然数量很少,但如果每一次对象请求引用时,都要检查是否存在类的实例,仍然需要一些开销,这个问题可以通过静态初始化解决此问题。定义一个私有的静态指针instance,和一个公有的静态函数GetInstance()。