单例模式
理论
单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式的好处:
单例模式因为 Singleton 类封装它的唯一实例,这样它可以严格地控制客户怎样访问它以及何时访问它,简单地来说就是对唯一实例地受控访问。
单例模式实现方法:
1. 声明自己的静态私有对象
2. 将创造对象的构造函数设置为私有,那么外界就无法创建对象了
3. 提供公有的访问对象的静态函数
预备知识:静态成员和静态变量:
1. 将成员或函数声明为静态的,那么这些成员和变量在类的所有对象之间是共享的,相当于类内全局变量。其中一个用途用来记录该类实例了多少对象。
2. 静态成员函数是没有this指针的,C++中调用方式 <类名>::<成员>。
3. 一般类在实例化对象的时候会为类成员分配内存空间。但是静态成员函数和静态成员变量在实例化对象的时候不会被分配内存空间,因为在对象创建之前(编译阶段)就已经被分配了内存。
4. 静态成员变量不能在类内初始化,这样可以防止每个对象都包含静态成员。
预备知识:互斥锁
1. 头文件#include《mutex》
2. 实例化:std::mutex mtx
3. 上锁:mtx.lock(), mtx.try_lock(); 解锁mtx.unlock()
实例
单线程单例模式
#include <iostream> using namespace std; //单例模式--懒汉式 class SingletonLazy { private: SingletonLazy(){} static SingletonLazy* instance; //静态成员变量应在类外初始化,且不能带static关键字 public: static SingletonLazy* GetInstance() { //提供单例的访问接口 if (instance == nullptr) { //第一次调用时,分配内存空间 instance = new SingletonLazy(); } return instance; } }; SingletonLazy* SingletonLazy::instance = nullptr; //在类外初始化静态成员变量 //单例模式--饿汉式 class SingletonHungry { private: SingletonHungry() {} static SingletonHungry* instance; public: static SingletonHungry* GetInstance() { return instance; } }; //在主函数之前就实例化了 SingletonHungry* SingletonHungry::instance = new SingletonHungry(); int main() { SingletonLazy* s1 = SingletonLazy::GetInstance(); SingletonLazy* s2 = SingletonLazy::GetInstance(); if (s1 == s2){ cout << "s1与s2为同一实例" << endl; } SingletonHungry* s3 = SingletonHungry::GetInstance(); SingletonHungry* s4 = SingletonHungry::GetInstance(); if (s3 == s4){ cout << "s3与s4为同一实例" << endl; } system("pause"); return 0; }
饿汉式单例类:这种静态初始化的方式是在自己被加载时就将自己实例化
懒汉式单例类:是在第一次被引用时,才会将自己实例化
区别:饿汉式单例模式是类一加载就实例化的对象,要提前占用系统资源。而懒汉式,面临多线程访问的安全性问题,需要做双重锁定这样的处理才可以保证安全。
多线程单例模式
上述单线程单例模式在多个线程同时检测到 instance == nullptr 时,会创建多个 Singleton ;所以需要进行上锁
public: static Singleton* GetInstance() { //提供单例的访问接口 mtx.lock(); if (instance == nullptr) { //第一次调用时,分配内存空间 instance = new Singleton(); } mtx.unlock(); return instance; }
这里每次运行 GetInstance 的时候都要进行互斥锁的操作,影响效率。所以人们发明了双锁
public: static Singleton* GetInstance() { //提供单例的访问接口 if (instance == nullptr){ mtx.lock(); if (instance == nullptr) { //第一次调用时,分配内存空间 instance = new Singleton(); } mtx.unlock(); } return instance; }
参考博客: