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

news2024/10/10 0:54:47

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

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

文章目录

    • 什么是单例模式
    • 如何实现单例模式
    • 饿汉模式和懒汉模式
      • 饿汉模式
      • 懒汉模式
      • 饿汉模式和懒汉模式的优缺点
        • 1.饿汉模式的优缺点
        • 2.懒汉模式的优缺点

什么是单例模式

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

如何实现单例模式

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

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

#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:
在这里插入图片描述
代码:这是一种线程安全的懒汉模式

单例模式  懒汉版  
#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();
}

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

#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.懒汉模式的优缺点

懒汉模式的优点:
延迟加载:在第一次调用时才创建实例,节省资源。
节约内存:只有在需要时才创建实例,避免资源浪费。
懒汉模式的缺点:
线程安全性问题:在多线程环境下,需要额外的同步措施来保证线程安全。
可能存在性能问题:在第一次调用时需要进行实例化,可能会影响程序性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2200576.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

看历史远比看未来更加清楚,太像了

目录 ‌1994年8月,中国进行了分税制改革 ‌2024年9月,中国经济经历了一系列显著的经济变化和政策调整。‌ 经济政策密集出台 资本市场反应热烈 制造业趋稳运行 外汇储备保持稳定 信心与预期提升 ‌2024年10月,股市回调和消费市场的活跃 ‌1994年8月,中国进行了分税…

通过SE38编写一个报表

该编写操作重点在于理解语法基础&#xff0c;并不具有实际意义。 然后进入代码编辑界面。 首先定义X M Z三个字段的类型为C&#xff08;字符类型&#xff09;&#xff0c;最大长度为10然后给X M进行赋值第三步使用ABAP链接语句&#xff0c;把X M两个值进行链接在屏幕上输出Z值。…

SVM及其实践1 --- 概念、理论以及二分类实践

说明 SVM(support vector machine,支持向量机)的理论其实是很漂亮的&#xff0c;只是对于初学者而言有点晦涩难懂和繁琐(特别是诸多的公式推导)。因为其经典且应用范围广&#xff0c;其实网上(各编程语言)已经有很多很成熟的包/函数可以直接调用&#xff0c;而且有关SVM的比较细…

Spring Boot教学资源库:开发者的成长之路

2 相关技术简介 2.1Java技术 Java是一种非常常用的编程语言&#xff0c;在全球编程语言排行版上总是前三。在方兴未艾的计算机技术发展历程中&#xff0c;Java的身影无处不在&#xff0c;并且拥有旺盛的生命力。Java的跨平台能力十分强大&#xff0c;只需一次编译&#xff0c;任…

llama3 implemented from scratch 笔记

github地址&#xff1a;https://github.com/naklecha/llama3-from-scratch?tabreadme-ov-file 分词器的实现 from pathlib import Path import tiktoken from tiktoken.load import load_tiktoken_bpe import torch import json import matplotlib.pyplot as plttokenizer_p…

大数据新视界 --大数据大厂之 GraphQL 在大数据查询中的创新应用:优化数据获取效率

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

12.2 Linux_进程间通信_共享内存

概述 什么是共享内存&#xff1a; 共享内存又叫内存映射&#xff0c;可以通过mmap()映射普通文件。 实际上就是将磁盘中的一个文件映射到内存的一个缓冲区中去&#xff0c;这样进程就可以直接将这块空间当作普通内存来访问&#xff0c;不需要再使用I/O中的read/write去访问这…

霍普菲尔德(Hopfield)神经网络求解旅行商问题TSP,提供完整MATLAB代码,复制粘贴即可运行

Hopfield神经网络是以美国物理学家约翰霍普菲尔德&#xff08;John Hopfield&#xff09;的名字命名的。他在1982年提出了这种类型的神经网络模型&#xff0c;因此通常被称为Hopfield网络。旅行商问题&#xff08;Traveling Salesman Problem&#xff0c;TSP&#xff09;是一个…

IEDA创建文件模板

1、点击设置-编辑器-文件与代码模板 2、输入对应的名称、扩展名、文件名 3、复制模板代码-点击应用、确定即可 4、新建配置项目&#xff0c;右键点击新建选择SpringMVC即可&#xff08;刚刚模板中的名称&#xff09;

D32【python 接口自动化学习】- python基础之输入输出与文件操作

day32 文件编码 学习日期&#xff1a;20241009 学习目标&#xff1a;输入输出与文件操作&#xfe63;-44 文件编码&#xff1a; 如何解决不同操作系统的文件乱码问题&#xff1f; 学习笔记&#xff1a; 为什么产生乱码 常见操作系统的文件编码 以不同的编码打开文件 # 以gb…

Linux学习网络编程学习(TCP和UDP)

文章目录 网络编程主要函数介绍1、socket函数2、bind函数转换端口和IP形式的函数 3、listen函数4、accept函数网络模式&#xff08;TCP&UDP&#xff09;1、面向连接的TCP流模式2、UDP用户数据包模式 编写一个简单服务端编程5、connect函数编写一个简单客户端编程 超级客户端…

如何实现不同VLAN间互通?

问题描述 客户要求不同VLAN的PC机互通&#xff0c;如下图拓扑所示。 此外&#xff0c;仅允许在设备 LSW3 上进行配置修改。 分析 由于所有的PC都在同一个网段&#xff0c;当任何一个设备想要和另一个设备通信时&#xff0c;它会首先根据数据交互的流程广播一个ARP请求报文来获…

1. Keepalived概念和作用

1.keepalived概念 (1)解决单点故障(组件免费) (2)可以实现高可用HA机制 (3)基于VRR协议(虚拟路由沉余协议) 2.keepalived双机主备原理

枚举+二分,CF 325B - Stadium and Games

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 325B - Stadium and Games 二、解题报告 1、思路分析 考虑 一个可能的初…

QD1-P8 HTML格式化标签

本节学习&#xff1a;HTML 格式化标签。 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p8 ‍ 一、font 标签 用途&#xff1a;定义文本的字体大小、颜色和 face&#xff08;字体类型&#xff09;。 示例 <!DOCTYPE html> <html><head><meta cha…

10.9QT对话框以及QT的事件机制处理

MouseMoveEvent(鼠标移动事件) widget.cpp #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);// 设置窗口为无边框&#xff0c;去掉标题栏等装饰this->setWi…

如何使用ArcGIS Pro设置一个图层不同标注

在有些时候&#xff0c;需要对某个要素进行突出显示&#xff08;比如省会城市&#xff09;&#xff0c;那就需要标注不同的样式&#xff0c;这里为大家介绍一下一个图层不同标注的方法。 分类标注 现在有一张广东省的行政区划图&#xff0c;想要突出标注广州市&#xff0c;虽…

超详解C++类与对象(中)

目录 1. 构造函数 1.1. 定义 1.2. 注意 2.析构函数 2.1定义 2.2注意 3.拷贝构造函数 3..1. 定义 3.2. 注意 4.运算符重载 4.1. 定义 5. 赋值运算符重载 5.1. 定义 5.2. 注意 ​​​​​​​ &#x1f493; 博客主页&#xff1a;C-SDN花园GGbond ⏩ 文章专…

大模型学习----什么是RAG

大模型快速定制的 RAG&#xff08;Retrieval-Augmented Generation&#xff09;方法 一、什么是 RAG RAG&#xff08;Retrieval-Augmented Generation&#xff09;即检索增强生成&#xff0c;它是一种结合了检索和语言生成的技术&#xff0c;旨在利用外部知识源来增强大型语言…

YOLO11改进|注意力机制篇|引入全局上下文注意力机制GCA

目录 一、【】注意力机制1.1【GCA】注意力介绍1.2【GCA】核心代码 二、添加【GCA】注意力机制2.1STEP12.2STEP22.3STEP32.4STEP4 三、yaml文件与运行3.1yaml文件3.2运行成功截图 一、【】注意力机制 1.1【GCA】注意力介绍 下图是【GCA】的结构图&#xff0c;让我们简单分析一下…