创建型模式——单例模式

单例模式

单例模式是一种创建型设计模式, 让你能够保证一个类只有一个实例并提供一个访问该实例的全局节点

在这里插入图片描述

基础要点
  1. 全局只有一个实例:static 特性,同时禁止用户自己声明并定义实例(把构造函数设为 private);

  2. 线程安全;

  3. 禁止赋值和拷贝;

  4. 用户通过接口获取实例:使用 static 类成员函数。

实现思路

私有化它的构造函数,以防止外界创建单例类的对象;使用类的私有静态指针变量指向类的唯一实例,并用一个公有的静态方法获取该实例。单例模式有两种实现方法,分别是懒汉和饿汉模式。顾名思义,懒汉模式,即非常懒,不用的时候不去初始化,所以在第一次被使用时才进行初始化;饿汉模式,即迫不及待,在程序运行时立即初始化。

懒汉式
1. 双检测锁模式
class Singleton
{
	private:	
    	static Singleton *p; // 私有静态指针变量指向唯一实例
    	static pthread_mutex_t lock; // 静态锁,是由于静态函数只能访问静态成员
    	Singleton() { pthread_mutex_init(&lock, NULL);} // 私有化构造函数
    	~Singleton(){}

	public:	
    	static Singleton* getInstance(); // 公有静态方法获取实例
};

pthread_mutex_t Singleton::lock;
Singleton* Singleton::p = NULL;

Singleton* Singleton::getInstance(){
    if (NULL == p){
        pthread_mutex_lock(&lock);
        if (NULL == p){
            p = new Singleton;
        }
        pthread_mutex_unlock(&lock);
    }
    return p;
}

为什么要用双检测,只检测一次不行吗?

如果只检测一次,在每次调用获取实例的方法时,都需要加锁,这将严重影响程序性能。双层检测可以有效避免这种情况,仅在第一次创建单例的时候加锁,其他时候都不再符合NULL == p的情况,直接返回已创建好的实例。

不足之处在于: 使用智能指针会要求用户也得使用智能指针,非必要不应该提出这种约束; 使用锁也有开销; 同时代码量也增多了,实现上我们希望越简单越好。

还有更加严重的问题,在某些平台(与编译器和指令集架构有关),双检锁会失效!具体可以看这篇文章,解释了为什么会发生这样的事情。

2. 局部静态变量之线程安全 (C++11, GCC > 4.3, VS2015支持该特性)
class Singleton
{
	private:
    	Singleton(){}
    	~Singleton(){}

	public:
    	static Singleton& getInstance();
};

static Singleton* getInstance()
{
    static Singleton instance;
    return &instance;
}
Singleton& Singleton::getInstance()
{
    static Singleton obj;
    return obj;
}

这种方法又叫做 Meyers’ SingletonMeyer’s的单例, 是著名的写出《Effective C++》系列书籍的作者 Meyers 提出的。所用到的特性是在C++11标准中的Magic Static特性:

If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.

如果当变量在初始化的时候,并发同时进入声明语句,并发线程将会阻塞等待初始化结束。这样保证了并发线程在获取静态局部变量的时候一定是初始化过的,所以具有线程安全性。C++静态变量的生存期 是从声明到程序结束,这也是一种懒汉式。这是最推荐的一种单例实现方式:通过局部静态变量的特性保证了线程安全 (C++11, GCC > 4.3, VS2015支持该特性);不需要使用共享指针,代码简洁;注意在使用的时候需要声明单例的引用 Single& 才能获取对象。另外网上有人的实现返回指针而不是返回引用

static Singleton* getInstance()
{
    static Singleton instance;
    return &instance;
}

这样做并不好,理由主要是无法避免用户使用delete instance导致对象被提前销毁。还是建议大家使用返回引用的方式。

饿汉式

饿汉模式不需要用锁,就可以实现线程安全。原因在于,在程序运行时就定义了对象,并对其初始化。之后,不管哪个线程调用成员函数getInstance(),都只不过是返回一个对象的指针而已。所以是线程安全的,不需要在获取实例的成员函数中加锁。

class Singleton
{
	private:
    	static Singleton* p;
    	Singleton(){}
    	~Singleton(){}

	public:
    	static Singleton* getInstance();
};

Singleton* Singleton::p = new Singleton();
Singleton* Singleton::getInstance()
{
	return p;
}

//测试方法
int main(){

    Singleton *p1 = Singleton::getInstance();
    Singleton *p2 = Singleton::getInstance();

    if (p1 == p2)
        cout << "same" << endl;

    system("pause");
    return 0;
}

饿汉模式虽好,但其存在隐藏的问题,在于非静态对象(函数外的static对象)在不同编译单元中的初始化顺序是未定义的。如果在初始化完成之前调用 getInstance() 方法会返回一个未定义的实例。

3、c++11下call_once的单例模式

4、使用pthread_once实现单例模式

pthread_once实现单例模式,《Linux多线程服务端编程》陈硕,P49

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晴你不懂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值