[TOC]
单例模式作为最常用的设计模式之一,用来保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
1、单例模式的实现思路
单例模式单例模式的实现思路一般有如下两步:
(1)私有化类的构造函数,以防止外界创建单例类的对象;
(2)使用类的私有静态指针变量指向类的唯一实例,并用一个公有静态方法获取该实例。
根据类的唯一实例初始化的时机,可以将单例模式的实现分为:懒汉模式和饿汉模式。
懒汉模式:非常懒,不用的时候不去初始化,只在第一次被调用时才进行初始化;
饿汉模式:迫不及待,即使还没有程序调用它,其已提前初始化等待被调用
2、单线程的单例模式
我们实现一个经典的懒汉模式的单例模式如下:
#include<iostream>
using namespace std;
class Singleton{
protected:
//构造函数被保护,不能被外界访问
Singleton(){cout<<"构造函数执行成功"<<endl;};
private:
//设置一个私有的静态类指针,用来保存全局唯一的实例对象
static Singleton* _instance;//静态成员变量
public:
//设置一个公有的静态成员函数,用来作为外界唯一的创建接口
static Singleton* Instance(){
if(_instance==nullptr)_instance=new Singleton();
return _instance;
};
};
// 全局变量类外声明
Singleton* Singleton::_instance=NULL;
我们实现一个经典的饿汉模式的单例模式如下:
#include<iostream>
using namespace std;
class Singleton{
protected:
//构造函数被保护,不能被外界访问
Singleton(){cout<<"构造函数执行成功"<<endl;};
private:
//设置一个私有的静态类指针,用来保存全局唯一的实例对象
static Singleton* _instance;//静态成员变量
public:
//设置一个公有的静态成员函数,用来作为外界唯一的创建接口
static Singleton* Instance(){return _instance;};
};
// 全局变量类外声明,直接实例化
Singleton* Singleton::_instance=new Singleton;
可以看到,所谓的懒汉模式和饿汉模式的区别非常容易理解。
3、多线程的单例模式
在多线程中,需要考虑共享资源的安全性,使用互斥锁
(1)经典的线程安全懒汉模式
单例模式有两种实现方法,分别是懒汉模式和饿汉模式。顾名思义,。
#include<iostream>
#include<pthread.h>
using namespace std;
class Singleton{
protected:
//构造函数被保护,不能被外界访问
Singleton(){
pthread_mutex_init(&lock, NULL);
cout<<"构造函数执行成功"<<endl;
};
private:
//设置一个私有的静态类指针,用来保存全局唯一的实例对象
static Singleton* _instance;//静态成员变量
static pthread_mutex_t lock;//静态锁,是由于静态函数只能访问静态成员
public:
//设置一个公有的静态成员函数,用来作为外界唯一的创建接口
static Singleton* Instance(){
if (_instance==NULL){
pthread_mutex_lock(&lock);
if (_instance==NULL){
_instance = new Singleton();
}
pthread_mutex_unlock(&lock);
}
return _instance;
};
};
// 全局变量类外声明,直接实例化
Singleton* Singleton::_instance=NULL;
pthread_mutex_t Singleton::lock;
为什么要用双检测,只检测一次不行吗?
如果只检测一次,在每次调用获取实例的方法时,都需要加锁,这将严重影响程序性能。双层检测可以有效避免这种情况,仅在第一次创建单例的时候加锁,其他时候都不再符合NULL == p的情况,直接返回已创建好的实例。
(2)局部静态变量之线程安全懒汉模式
前面的双检测锁模式,写起来不太优雅,《Effective C++》(Item 04)中的提出另一种更优雅的单例模式实现,使用函数内的局部静态对象,这种方法不用加锁和解锁操作。它不用加锁是因为在C++11之后,编译器会保证局部静态变量的线程安全性,所以不需要程序员额外为局部静态变量设置线程安全性。
class Singleton{
protected:
//构造函数被保护,不能被外界访问
Singleton(){cout<<"构造函数执行成功"<<endl;};
public:
//设置一个公有的静态成员函数,用来作为外界唯一的创建接口
static Singleton* Instance(){
static Singleton _instance;//静态成员变量
return &_instance;
};
};