【C++】单例模式「详尽版」


欢迎来到 破晓的历程的 博客

⛺️不负时光,不负己✈️


文章目录

什么是单例模式

C++单例模式是一种非常重要的设计模式,它只允许一个类实例化出一个对象来,并提供一个全局访问点来获取该实例。 这个模式的主要目的是控制某个类的实例化过程,以避免产生多个实例对象而导致的资源消耗或数据不一致等问题。

如何实现单例模式

实现一个单例模式的类,要做到以下几点:

  • 私有化构造函数,防止在外部通过构造函数直接创建出对象。
  • 禁用拷贝构造和赋值运算符,防止在外部通过拷贝构造和赋值直接创建出对象。
  • 定义一个静态指针或者引用,用于指向类的唯一实例。
  • 提供一个静态公有成员函数,于返回类的唯一实例的指针或引用。这个函数通常被称为getInstance或类似的名称。「调用非静态成员函数需要一个对象,所以我们需要把该成员函数设置为私有」。
    如下所示就是一个单例模式下的类
cpp 复制代码
#include <iostream>  
#include <memory>  

class Singleton {
public:
	// 静态公有成员函数,返回类的唯一实例  
	static Singleton& getInstance() {
		// 静态局部变量在第一次调用时初始化,且只初始化一次  
		return only;
	}
	// 示例成员函数  
	void doSomething() {
		std::cout << "Doing something in Singleton!" << std::endl;
	}

private:
	// 私有化构造函数,防止外部直接创建实例  
	Singleton() {};

	// 私有化析构函数
	
	~Singleton() {};
	// 禁止拷贝构造函数和赋值运算符  
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
	//定义一个静态指针或者引用
	static Singleton only;
};
Singleton Singleton::only;
int main() {
	// 获取单例实例并调用成员函数  
	Singleton& singleton = Singleton::getInstance();
	singleton.doSomething();

	// 尝试再次获取单例实例(应为同一个实例)  
	Singleton& anotherSingleton = Singleton::getInstance();
	anotherSingleton.doSomething();
	return 0;
}

单例模式的设计思路有很多,但是我们都需要满足上面的几点。

饿汉模式和懒汉模式

在单例模式下,又细分为经典的饿汉模式和懒汉模式,我们一起来了解一下:

饿汉模式

什么是饿汉模式?

饿汉模式,这个名词很形象,大家可以想象为很"饥饿",实例在类加载的时候就被创建,所以一开始还没有进入到main函数中就要创建实例。
如何实现饿汉模式?

我们刚刚的代码实际上就是饿汉模式的一种。我们需要定义一个类的静态成员变量【如刚刚代码中的static Singleton only】。

代码如下:

cpp 复制代码
#include <iostream>  
#include <memory>  

class Singleton {
public:
	// 静态公有成员函数,返回类的唯一实例  
	static Singleton& getInstance() {
		// 静态局部变量在第一次调用时初始化,且只初始化一次  
		return only;
	}
	// 示例成员函数  
	void doSomething() {
		std::cout << "Doing something in Singleton!" << std::endl;
	}

private:
	// 私有化构造函数,防止外部直接创建实例  
	Singleton() {};

	// 私有化析构函数
	
	~Singleton() {};
	// 禁止拷贝构造函数和赋值运算符  
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
	//定义一个静态指针或者引用
	static Singleton only;
};
Singleton Singleton::only;
int main() {
	// 获取单例实例并调用成员函数  
	Singleton& singleton = Singleton::getInstance();
	singleton.doSomething();

	// 尝试再次获取单例实例(应为同一个实例)  
	Singleton& anotherSingleton = Singleton::getInstance();
	anotherSingleton.doSomething();
	return 0;
}

懒汉模式

什么是懒汉模式?

懒汉模式,顾名思义在.指的是类对象在使用时才会被创建。
如何实现懒汉模式

我们将饿汉模式稍加改造即可:
方法1:

代码:这是一种线程安全的懒汉模式

cpp 复制代码
单例模式  懒汉版  
#include<iostream>
#include<thread>
#include<mutex>
#include<condition_variable>

using namespace std;
mutex mtx;
class Singleton1
{
public:
		static Singleton1* GetOnly()
		{
			if (only == nullptr)
			{	
				unique_lock<mutex>lock(mutex);
				if (only == nullptr)
				{
					only = new Singleton1();
				}
			}
			return only;
		}
	void print()
	{
		cout << "hello world" << endl;
	}
private:
	Singleton1() {};
	~Singleton1() {};
	Singleton1(const Singleton1&) = delete;
	Singleton1& operator=(const Singleton1&) = delete;
	static Singleton1* only;

};

Singleton1* Singleton1::only=nullptr;
int main()
{
	Singleton1* t1 = Singleton1::GetOnly();
	t1->print();
}

有没有第二种设计懒汉模式的方案呢?有的;
方案二:

cpp 复制代码
#include<iostream>
#include<thread>
#include<mutex>
#include<condition_variable>

using namespace std;
mutex mtx;
class Singleton1
{
public:
		static Singleton1* GetOnly()
		{
			static Singleton1 install;
			return &install;

		}
	void print()
	{
		cout << "hello world" << endl;
	}
private:
	Singleton1() {};
	~Singleton1() {};
	Singleton1(const Singleton1&) = delete;
	Singleton1& operator=(const Singleton1&) = delete;
	static Singleton1* only;

};

Singleton1* Singleton1::only=nullptr;
int main()
{
	Singleton1* t1 = Singleton1::GetOnly();
	t1->print();
}

改动的地方如下:

但是这种方案是不是线程安全的呢?

是的,原因如下:

1.静态局部变量在程序启动阶段就已经被分配内存空间了,但是它的的初始化却是在第一次运行到它的时候,如果我们不调用GetOnly()方法,这个静态局部变量是不会被初始化的。

2.在多线程的情况下,可能会出现对个线程同时访问GetOnly()的情况,但是静态局部变量的初始化,在汇编指令上,已经自动添加了线程互斥指令了,所以总的来说是安全的。

饿汉模式和懒汉模式的优缺点

1.饿汉模式的优缺点

饿汉模式的优点:

线程安全:在类加载的时候就创建实例,不存在多线程环境下的线程安全问题(还没进入主函数就创建完实例了,所以不用担心线程安全问题)。
饿汉模式的缺点:

可能会造成资源浪费:在程序运行过程中始终存在实例,可能会占用一定的资源。

不支持延迟加载:无法实现延迟加载的特性。

2.懒汉模式的优缺点

懒汉模式的优点:

延迟加载:在第一次调用时才创建实例,节省资源。

节约内存:只有在需要时才创建实例,避免资源浪费。
懒汉模式的缺点:

线程安全性问题:在多线程环境下,需要额外的同步措施来保证线程安全。

可能存在性能问题:在第一次调用时需要进行实例化,可能会影响程序性能。

相关推荐
zh路西法2 分钟前
【C++决策和状态管理】从状态模式,有限状态机,行为树到决策树(一):从电梯出发的状态模式State Pattern
c++·决策树·状态模式
轩辰~16 分钟前
网络协议入门
linux·服务器·开发语言·网络·arm开发·c++·网络协议
lxyzcm36 分钟前
C++23新特性解析:[[assume]]属性
java·c++·spring boot·c++23
蜀黍@猿1 小时前
C/C++基础错题归纳
c++
雨中rain1 小时前
Linux -- 从抢票逻辑理解线程互斥
linux·运维·c++
ALISHENGYA2 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(实战项目二)
数据结构·c++·算法
arong_xu2 小时前
现代C++锁介绍
c++·多线程·mutex
汤姆和杰瑞在瑞士吃糯米粑粑3 小时前
【C++学习篇】AVL树
开发语言·c++·学习
DARLING Zero two♡3 小时前
【优选算法】Pointer-Slice:双指针的算法切片(下)
java·数据结构·c++·算法·leetcode
CodeClimb3 小时前
【华为OD-E卷-木板 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od