[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;
    };
};