一、设计模式概念
设计模式是被广泛使用的软件开发中的一种解决方案,它提供了一套被验证过的、可重用的设计思想,帮助开发人员更加高效地开发出可维护、易扩展的软件系统。
设计模式可以分为三类:创建型模式、结构型模式和行为型模式。
1.1 创建型模式
创建型模式用于描述对象的创建过程,它的目的是封装对象的创建过程,从而降低系统的耦合度,并且能够更加灵活地创建对象。创建型模式包括单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式等。
1.2 结构型模式
结构型模式用于描述如何将类或对象按某种布局组成更大的结构,它涉及到接口和类的组合。结构型模式包括适配器模式、桥接模式、组合模式、装饰者模式、外观模式、享元模式和代理模式等。
1.3 行为型模式
结构型模式用于描述如何将类或对象按某种布局组成更大的结构,它涉及到接口和类的组合。结构型模式包括适配器模式、桥接模式、组合模式、装饰者模式、外观模式、享元模式和代理模式等。
二、单例模式概念
单例模式是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点。
1.将构造函数私有化:将类的构造函数设为私有,这样外部就无法通过构造函数来创建该类的对象。
2.提供一个静态成员变量来保存唯一实例:在该类中定义一个静态成员变量,用于保存该类的唯一实例。
3.提供一个静态方法来获取唯一实例:在该类中定义一个静态方法,用于获取该类的唯一实例。在该方法中,判断静态成员变量是否为空,如果为空,则创建一个新的对象并赋值给静态成员变量,然后返回该静态成员变量。
4.禁止复制构造函数和赋值运算符:为了避免从已有对象派生出新的对象,需要将复制构造函数和赋值运算符定义为私有或删除函数。
通过上述方法可以使单例类只有一个对象,全局只有一个实例对象存在,通过静态接口来获取该实例
2.1 饿汉模式:
饿汉模式就像有种饿叫做你妈妈/奶奶觉得你饿,不管你真饿假饿 我都把饭给你做好,饿汉模式也一样,我直接把单例对象给你初始化好,完了你直接用就可以了
代码示例:
class Singleton {
public:
static Singleton* getInstance() { //获取单例对象的静态接口
return instance;
}
private:
static Singleton* instance;//静态对象
Singleton() {}//构造、拷贝构造、赋值设置为私有
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
Singleton* Singleton::instance = new Singleton(); //类加载之后就直接初始化,饿汉模式
2.2 懒汉模式:
懒汉模式就是比较懒,有人要获取单例对象我才来实例化,踢一脚走一步的那种
代码示例:
class Singleton {
public:
static Singleton* getInstance() { //获取单例对象的静态接口
if (instance == nullptr)//如果单例对象没有初始化,则进行初始化
{
instance = new Singleton();
}
return instance;
}
private:
static Singleton* instance;//静态对象
Singleton() {}//构造、拷贝构造、赋值设置为私有
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
Singleton*Singleton::instance = nullptr; //初始化为 nullptr
有了代码,那当然要试一试单例模式怎么玩,验证一下是不是真的只有一个单例对象:
可以看到,单例对象是唯一的
2.3 单例模式验证测试
2.3.1 饿汉模式
代码示例:
#include<iostream>
using namespace std;
class Singleton {
public:
static Singleton* getInstance() { //获取单例对象的静态接口
return instance;
}
void Print()
{
cout << "hello world" << endl;
}
private:
static Singleton* instance;//静态对象
Singleton() {}//构造、拷贝构造、赋值设置为私有
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
Singleton* Singleton::instance = new Singleton(); //类加载之后就直接初始化,饿汉模式
int main()
{
//1.构造对象试试
Singleton sin;//报错构造函数私有化,无法在类外实例化出对象
return 0;
}
那么如何去调用这个单例类里面的方法呢?
//调用静态方法获取单例对象(单例对象指针) 然后调用单例类中的成员函数
Singleton::getInstance()->Print();
运行结果:
三、单例模式是线程安全的吗?
我们考虑这样一个场景,两个线程,懒汉模式下,同时去获取这个单例对象,那么在获取单例对象之前,都是nullptr ,那么都会同时去 new 一个单例对象,导致会创建多个单例对象,那么就无法实现单例模式单例对象唯一的特点
我们可以通过加锁来解决该问题:
#include <mutex>
class Singleton {
private:
static Singleton* instance;
static std::mutex mtx;
Singleton() {}
public:
static Singleton* getInstance() {
if (instance == nullptr) { // 第一次检查是否为空
std::lock_guard<std::mutex> lock(mtx); // 加锁保证线程安全
if (instance == nullptr) { // 第二次检查是否为空
instance = new Singleton();
}
}
return instance;
}
// 禁止拷贝构造函数和赋值运算符
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;
当然了 饿汉模式是线程安全的,因为单例类对象在类加载完就进行了实例化,通过静态接口获取单例对象时直接获取即可,无需初始化。