Singleton(单例模式)创建型

一、概述

只涉及一个单一的类,该类否则创建自己的对象,同时确保只有单个对象被创建。这个类提供一种方法访问唯一的对象方式,可以直接创建,不需实例化该类的对象。

保证一个类仅有一个实例,并提供一个访问它的全局访问点,主要解决一个全局使用的类频繁地创建与销毁。

1.结构

  1. 单例类声明了一个名为getInstance 获取实例的静态方法来返回其所属类的一个相同实例。

  2. 单例的构造函数必须对客户端代码隐藏。调用获取实例方法必须是获取单例对象的唯一方式。

2.适用场景

  1. 程序中只有一个可用的实例。

  2. 需要更加严格地控制全局变量,可以使用单例模式。

3.优缺点

优点:

  1. 保证一个类只有一个实例。

  2. 获得了一个指向该实例的全局访问节点。

  3. 仅在首次请求单例对象时对其进行初始化。

  4. 没有接口,不能继承。

缺点:

  1. 违反了单一职责原则。该模式同时解决了两个问题。

  2. 单例模式可能掩盖不良设计,比如程序各组件之间相互了解过多等。

  3. 该模式在多线程环境下需要进行特殊处理,避免多个线程多次创建单例对象。

  4. 单例的客户端代码单元测试可能会比较困难,因为许多测试框架以基于继承的方式创建模拟对象。由于单例类的构造函数是私有的,而且绝大部分语言无法重写静态方法,所以你需要想出仔细考虑模拟单例的方法。要么干脆不编写测试代码,或者不使用单例模式。

二、懒汉单例模式

1.线程不安全的懒汉单例模式

类加载时没有生成单例,只有当第一次调用 instance_方法时才去创建这个单例,
注意懒汉模式在不加锁情况下是线程不安全的,只适用于单线程

#include <iostream>
#include<string>


using namespace std;

class Singleton {
private:
    static Singleton* instance_;//实例对象static:保证全局只有一个
  
    Singleton() {}//构造函数私有:即单例模式只能在内部私有化

public:
    //外界通过GetInstance()获取实例对象
    static Singleton* GetInstance() {
        if (instance_ == nullptr)
        {
            instance_ = new Singleton();
        }
        return instance_;
    }
};
// 静态变量instance;静态成员必须在类外定义
Singleton* Singleton::instance_ = nullptr;

int main()
{
    Singleton* s1 = Singleton::GetInstance();
    Singleton* s2 = Singleton::GetInstance();
    cout << "s1地址:" << s1 << endl;
    cout << "s2地址:" << s2 << endl;
    return 0;
}

在这里插入图片描述

2.线程安全的懒汉单例模式

上述代码并不是线程安全的,当多个线程同时调用Singleton::GetInstance(),可能会创建多个实例从而导致内存泄漏(会new多次但我们只能管理唯一的一个instance_),通过互斥锁实现线程安全。

#include <iostream>
#include<string>
#include<mutex>

using namespace std;

class Singleton {
private:
    static Singleton* instance_;//实例对象static:保证全局只有一个
    static mutex m_mutex_;
    Singleton() {}//构造函数私有:即单例模式只能在内部私有化
public:
    //外界通过GetInstance()获取实例对象
    static Singleton* GetInstance() {
       // 加锁保证多个线程并发调用getInstance()时只会创建一个实例
        m_mutex_.lock();
        if (instance_ == nullptr)
        {
            instance_ = new Singleton();
        }
        m_mutex_.lock();
        return instance_;
    }
};

//静态成员必须在类外定义
Singleton* Singleton::instance_ = nullptr;
mutex Singleton::m_mutex_;


int main()
{
    Singleton* s1 = Singleton::GetInstance();
    Singleton* s2 = Singleton::GetInstance();
    cout << "s1地址:" << s1 << endl;
    cout << "s2地址:" << s2 << endl;
    return 0;
}

3.同步锁模式

适用于多线程下的懒汉模式中,每一个线程在判断instance_是否为NLL前,都先要进行加锁操作,如果此时已经有其他线程进行了加锁操作,那么当前线程就进入了阻塞状态,如此以往,就会导致大量资源被浪费,还影响了效率,其实我们只需要小小的改动,就可以解决这个问题。

#include <iostream>
#include<string>
#include<mutex>

using namespace std;

class Singleton {
private:
    static Singleton* instance_;//实例对象static:保证全局只有一个
    static mutex m_mutex_;
    Singleton() {}//构造函数私有:即单例模式只能在内部私有化
public:
    //外界通过GetInstance()获取实例对象
    static Singleton* GetInstance() {
		if(instance_==NULL){
       // 加锁保证多个线程并发调用getInstance()时只会创建一个实例
        m_mutex_.lock();
        if (instance_ == nullptr)
        {
            instance_ = new Singleton();
        }
        m_mutex_.lock();
       }
        return instance_;
    }
};

//静态成员必须在类外定义
Singleton* Singleton::instance_ = nullptr;
mutex Singleton::m_mutex_;


int main()
{
    Singleton* s1 = Singleton::GetInstance();
    Singleton* s2 = Singleton::GetInstance();
    cout << "s1地址:" << s1 << endl;
    cout << "s2地址:" << s2 << endl;
    return 0;
}

将加锁和判断instance_是否为NULL 的顺序对调了一下就解决了这个问题,即当instance==NULL的时候,才去进行加锁,实例化唯一对象,解锁的操作,如果此时instance != NULL,说明唯一的对象已经被实例化出来了,那么其他线程就不需要再进行加锁,解锁操作了,而是可以直接使用这个唯一的对象。

三、饿汉单例模式

所谓的饿汉模式懒汉模式恰恰相反,懒汉模式是只有当我们需要的时候才实例化唯一的对象,而饿汉是无论你需不需要,一开始就将唯一的对象实例化出来。

#include <iostream>
#include<string>
#include<mutex>

using namespace std;

class Singletion {
private:
	static Singletion* instance_;
	Singletion(){}
public:
	static Singletion* GetInstance()
	{
		return instance_;
	}
};
Singletion* Singletion::instance_ = new Singletion();//静态成员必须在类外定义

int main()
{
	Singletion* s1 = Singletion::GetInstance();
	Singletion* s2 = Singletion::GetInstance();
	cout << "s1地址: " << s1 << std::endl;
	cout << "s2地址: " << s2 << std::endl;
	return 0;
}

在这里插入图片描述
饿汉模式的好处,就是不需要再判断instance是否为NULL了,这在某些情况下能起到非常大的作用。

四、Meyers’ Singleton

Meyers’ Singleton是Scott Meyers提出的C++单例的推荐写法。它将单例对象作为局部static对象定义在函数内部:

class Singleton {
 public:
    static Singleton& GetInstance() {
        static Singleton instance;
        return instance;
    }
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

 private:
    Singleton() {}
};

优点:

解决了普通单例模式全局变量初始化依赖(C++只能保证在同一个文件中声明的static遍历初始化顺序和其遍历声明的顺序一致,但是不能保证不同文件中static遍历的初始化顺序)。

缺点:

  1. 需要C11支持(C11保证static成员初始化的线程安全)。

  2. 性能问题(同懒汉模式一样,每次调用GetInstance()方法时需要判断局部static变量是否已经初始化,如果没有初始化就会进行初始化,这个判断逻辑会消耗一点性能)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值