常见手撕项目C++

常见手撕项目C++

  • 设计模式
    • 单例模式
      • 饿汉模式
      • 懒汉模式
    • 策略模式
      • 策略接口
      • 实现具体的策略(虚函数重写)
      • 定义上下文
      • 用户调用

设计模式

单例模式

在这里插入图片描述
单例模式是一种常用的软件设计模式,其目的是确保一个类只有一个实例,并提供一个全局访问点来获取该实例。

  • 优点:
    • 资源控制:单例模式能够确保一个类只有一个实例存在,这对于控制资源的使用非常有用,如配置文件的读取、数据库的连接等,可以避免由于多个实例造成的资源浪费或冲突。
    • 全局访问点:单例对象可以被全局访问,方便其他对象对其进行访问,而无需持有单例类的引用。
    • 数据共享:由于整个应用程序共享一个单例实例,它自然地提供了一个共享数据的环境,这在某些场合下是非常有用的。
  • 缺点:
    • 全局变量(同步)问题:单例模式本质上提供了一个全局可访问的实例,但全局变量(或对象)容易被误用,特别是涉及多个线程进行访问的时候,还会出现同步问题。
    • 违背单一职责原则:单例类除了管理自己的实例外,还承担了业务逻辑的职责,违反了单一职责原则。

介绍完单例模式,我们来看看单例模式的两种实现方式,分别是饿汉模式与懒汉模式。

饿汉模式

在这里插入图片描述

饿汉模式指的是单例实例在程序启动时就立即创建(迫不及待的感觉)。这种方式避免了线程安全问题,但可能会增加程序的启动时间,同时如果实例最终未被使用,则会造成资源的浪费。

class EagerSingleton{
private:
	// 将自己的实例化对象申明为静态资源
	static EagerSingleton instance;
protected:
	// 隐藏自己的构造函数以及析构函数,防止用户调用
	EagerSingleton() = default; // 这里构造函数设置为默认
	EagerSingleton(const EagerSingleton&) = default;
	EagerSingleton& operator= (const EagerSingleton&) = default;
	~EagerSingleton() = default;
public:
	EagerSingleton& getInstance(){
		return instance;
	}
}

// 静态的私有成员变量可以在类外进行初始化(一般在main()函数之前进行初始化),在这里,你可以理解instance是类内成员,可以访问私有以及保护成员。
EagerSingleton EagerSingleton::instance();

懒汉模式

在这里插入图片描述
懒汉模式指的是单例实例在第一次被使用时才进行创建(不叫我,那我就懒,不创建)。这种方式可以减少资源的消耗,但需要考虑线程安全问题(例如多个线程同时是第一次使用,所以一般需要锁)。

#include <mutex>

class lazySingleton{
private:
	static lazySingleton* instance; // 懒汉模式一般使用指针
	static mutex my_mu; // 考虑到线程安全,需要有锁。
protected:
	// 不给用户调用构造函数和析构函数的机会
	lazySingleton() = default;
	lazySingleton(const lazySingleton&) = default;
	lazySingleton& operator=(const laySingleton&) = default;
	~lazySingleton() = default;
public:
	lazySingleton* getInstance(){
		if(instance == nullptr){ // 第一次检查
			std::lock_guard<std::mutex> lock(my_mu); //作用域锁,离开作用域后,自动解锁
			if(instance == nullptr){ // 第二次检查
				instance = new lazySingleton();
			}
		}
		return *this;
	}
	// my_mu会在这里结束后,自动解锁
}

// 静态成员变量类外初始化
lazySingleton lazySingleton::instance = nullptr;
lazySingleton lazySingleton::my_mu; // 调用锁的自动初始化方法

这里可能会好奇,为什么需要两次判断instance == nullptr

  • 第一次检查 (instance == nullptr)
    第一次检查是在锁外进行的。这个检查的目的是避免在单例实例已经创建之后的每次调用都需要进行昂贵的锁操作。如果实例已经存在,就直接返回实例,这样大部分时间可以避免锁的开销。
  • 获取锁
    如果第一次检查发现实例为nullptr,即单例尚未被创建,那么就需要进入同步块(通过获取锁)来确保只有一个线程可以创建单例实例。这是必要的,因为可能有多个线程同时通过了第一次的nullptr检查。
  • 第二次检查 (instance == nullptr)
    即使线程成功获取了锁,仍然需要再次检查实例是否为nullptr。这是因为在当前线程等待锁的同时,可能有另一个线程已经获取了锁、创建了实例并释放了锁。第二次检查确保了即使在多个线程同时尝试创建单例实例的情况下,单例实例仍然是唯一的。

策略模式

在这里插入图片描述
策略模式是一种行为设计模式,它允许在运行时选择算法或行为的最佳策略。策略模式定义了一系列的算法,并将每一个算法封装起来,使它们可以互换。这种模式让算法的变化独立于使用算法的客户。

策略接口

首先,要定义一个策略接口,表示可以执行的操作

class Strategy{
public:
	virtual ~Strategy(){};
	virtual void excute() const = 0; // 操作定义为纯虚函数
}

实现具体的策略(虚函数重写)

然后,利用继承,实现不同且具体的策略

class StrategyA : public Strategy{
public:
	void excute() const override{ //override 关键字表示一定是重写虚函数,避免出现覆盖的情况(如果有存在的话)
		cout << "StrategyA" << endl;
	}
}

class StrategyB : public Strategy{
public:
	void excute() const override{ //override 关键字表示一定是重写虚函数,避免出现覆盖的情况(如果有存在的话)
		cout << "StrategyB" << endl;
	}
}

class StrategyB : public Strategy{
public:
	void excute() const override{ //override 关键字表示一定是重写虚函数,避免出现覆盖的情况(如果有存在的话)
		cout << "StrategyB" << endl;
	}
}

定义上下文

接着,定义一个上下文类,用于从客户端接收策略,并使用它执行操作

#include <iostream>
#include <memory> // 智能指针共享库
using namespace std;

class Context{
public:
	unique_ptr<Strategy> strategy; // 使用独占的智能指针
	// 使用右值引用来接收一个unique_ptr,并提供默认参数(即调用这个函数的时候可以不用传入参数)
	Context(unique_ptr<Strategy> &&strategy = {}): strategy(std::move(strategy)){}
	
	// 设置接口到底执行哪一个函数
	void setStrategy(unique_ptr<Strategy> &&strategy){
		strategy = std::move(strategy);
	}
	
	// 执行具体函数
	void excuteStrategy() const{
		if(strategy){
			strtegy->excute();
		}
	}
}

用户调用

int main(){
	// 需要注意make_unique<StrategyA>()是一个不具名的右值,所以可以正确调用右值构造函数
	Context context(make_unique<StrategyA>());
	context.excuteStrategy(); // 输出:StrategyA
	
	// 动态切换策略
	context.setStrategy(make_unique<StrategyB>());
	context.excuteStrategy(); // 输出:StrategyB

	context.setStrategy(make_unique<StrategyC>());
	context.excuteStrategy(); // 输出:StrategyC
	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Modbus转Profinet网关快速解决PLC插槽数量不够用的烦恼

通过Modbus转Profinet&#xff08;XD-MDPN100&#xff09;网关的应用&#xff0c;不仅可以实现Modbus设备与Profinet网络的平滑对接&#xff0c;还能有效解决PLC插槽限制和Modbus指令轮询等问题&#xff0c;Modbus转Profinet网关&#xff08;XD-MDPN100&#xff09;在解决PLC插…

Linux多进程开发2 - 孤儿、僵尸进程

参考学习&#xff1a;彻底搞懂孤儿/僵尸/守护进程 一、孤儿进程(Orphan Process) 父进程运行结束&#xff0c;但子进程还在运行&#xff0c;这样的子进程就称为孤儿进程每当出现一个孤儿进程的时候&#xff0c;内核就把孤儿进程的父进程设置为 init (即养父)Init 会等待被收养…

阿里云实时计算Flink的产品化思考与实践【下】

摘要&#xff1a;本文整理自阿里云高级产品专家黄鹏程和阿里云技术专家陈婧敏在 FFA 2023 平台建设专场中的分享。内容主要为以下五部分&#xff1a; 阿里云实时计算 Flink 产品化思考 产品化实践 SQL 产品化思考及实践 展望 接上篇&#xff1a;阿里云实时计算Flink的产品…

Centos服务器Open Gauss 部署

近期很多的项目由于信创要求使用一些国产的数据库&#xff0c;比如OpenGauss。OpenGuass是华为高斯DB的开源版&#xff0c;内核还是PostgreSQL&#xff0c;商业版是收费的。这里记录一下是如何安装部署 的。 官方中文文档 官方下载地址 部署要求 操作系统要求 ARM&#xff…

《数据结构学习笔记---第六篇》---栈和队列的实现

目录 1.栈 1.1栈的概念及结构 1.2栈的实现 2.队列 2.1队列的概念及结构 ​2.2队列的实现 3.顺序栈的具体实现 3.1建头文Stack.h” 3.2创建具体接口实现文件Stack.c 3.2.1初始化 3.2.2入栈出栈 3.2.4判空 3.2.5栈的大小 3.2.6销毁栈 3.3主函数的实现 4.链队的具体实现…

iOS UIFont-真香警告之字体管理类

UIFont 系列传送门 第一弹加载本地字体:iOS UIFont-新增第三方字体 第二弹加载线上字体:iOS UIFont-实现三方字体的下载和使用 第三弹搭建字体管理类:iOS UIFont-真香警告之字体管理类 前言 不知道友们是否有过这种经历,项目已经迭代了很多版本,项目中的文件已经上千个了…

钉钉服务端API报错 错误描述: robot 不存在;解决方案:请确认 robotCode 是否正确

problem 调用钉钉服务端API&#xff0c;机器人发送群聊消息&#xff0c;后台返回报错信息: 钉钉服务端API报错 错误描述: robot 不存在&#xff1b;解决方案:请确认 robotCode 是否正确&#xff1b; reason 定位: 登录后台&#xff0c;查看机器人是存在查看机器人调用权限接…

DARTS-PT: RETHINKING ARCHITECTURE SELECTION IN DIFFERENTIABLE NAS

Rethinking Architecture Selection in Differentiable NAS 论文链接&#xff1a;https://arxiv.org/abs/2108.04392v1 项目链接&#xff1a;https://github.com/ruocwang/darts-pt ABSTRACT 可微架构搜索(Differentiable Neural Architecture Search, NAS)是目前最流行的网…

上位机图像处理和嵌入式模块部署(qmacvisual透视变换)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 说到透视变换&#xff0c;以前我也不明白为什么有这样一个需求。后来在tier1做车道线检测的时候&#xff0c;才知道如果把camera拍摄到的图像做一次…

FebHost:意大利.IT域名一张意大利网络名片

.IT域名是意大利的国家顶级域名&#xff0c;对于意大利企业和个人而言,拥有一个属于自己的”.IT”域名无疑是件令人自豪的事。这个被誉为意大利互联网标志性代表的域名,不仅隐含着浓厚的意大利文化特色,还为使用者在当地市场的推广铺平了道路。 对于那些希望在意大利市场建立强…

HEVC的Profile和Level介绍

文章目录 HEVCProfile&#xff08;配置&#xff09;&#xff1a;Level&#xff08;级别&#xff09;&#xff1a;划分标准 HEVC HEVC&#xff08;High Efficiency Video Coding&#xff09;&#xff0c;也称为H.265&#xff0c;是一种视频压缩标准&#xff0c;旨在提供比先前的…

Linux(CentOS7.5) 安装部署 Python3.6(超详细!包含 Yum 源配置!)

文章目录 1.配置 Yum 源2.下载 Python3 包3. 解压4.安装依赖环境5.安装出错场景 6.创建软链接7.配置 Python3 的环境变量8.验证补充&#xff1a;安装 openssl-devel补充&#xff1a;pip3 源配置 1.配置 Yum 源 # 注意&#xff01;&#xff01;&#xff01;请先切换到 root 账号…

stable diffusion如何下载预处理器?

如何下载预处理器&#xff1f; 具体位置:SD文件>extensions>sd-webui-controlnet>annotator” 把整个文件夹复制到SD的文件夹里面 里面有一个“downloads”文件夹 把这些模型复制到“downloads”文件夹里

【QT学习】1.qt初识,创建qt工程,使用按钮,第一个交互按钮

1.初识qt--》qt是个框架&#xff0c;不是语言 1.学习路径 一 QT简介 &#xff0c;QTCreator &#xff0c;QT工程 &#xff0c;QT的第一个程序&#xff0c;类&#xff0c;组件 二 信号与槽 三 对话框 四 QT Desiner 控件 布局 样式 五 事件 六 GUI绘图 七 文件 八 …

constexpr与std::is_same_v碰撞会产生什么火花?

1. 只编译会用到的if分支 示例代码一中&#xff0c;checkType_v1和checkType_v2两个函数的区别就是if的条件里一个加了constexpr一个没加&#xff0c;加与不加从结果来看都一样&#xff0c;那在编译时和运行时各有什么区别呢&#xff1f; 示例代码一&#xff0c;test_01.cpp&…

数字孪生项目的开发工具

数字孪生项目的开发工具是实现数字孪生技术应用的关键。它们使得开发者能够创建、管理和优化数字孪生模型&#xff0c;以及与真实世界的实体进行交互。以下是一些数字孪生项目开发中常用的工具&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件…

软件工程学习笔记14——案例解析篇

一、大型开源项目对软件工程的应用 以VS Code为例&#xff0c;看大型开源项目是如何应用软件工程的。 软件工程的核心&#xff0c;就是围绕软件项目开发&#xff0c;对开发过程的组织&#xff0c;对方法的运用&#xff0c;对工具的使用。 所以当我们去观察一个软件项目&#…

主流好用的 Markdown 编辑器介绍

在当今程序员的日常工作中&#xff0c;Markdown 已经成为了一种常用的文本标记语言&#xff0c;它简洁、易读、易写&#xff0c;被广泛应用于写作、文档编写、博客撰写等场景。为了更高效地编辑和管理 Markdown 格式的文档&#xff0c;选择一款功能强大、易用的 Markdown 编辑器…

基于jdbc+mysql+java的简单货物管理系统

智能软件&#xff08;后端测试&#xff09; 路线篇 第一阶段&#xff1a; javamysqljdbc综合实战应用ai企业级框架工具&#xff1a;eclipse 第二阶段 seevletssm框架vueai综合实战serclet工具&#xff1a; ideaiflycode大模型gitmaven 组内任务 项目开发答辩 第三阶段 基…

GEE实践应用|热岛效应(一)地表温度计算

目录 1.学习目标 2.理论介绍 3.从MODIS获得地表温度 4.从Landsat卫星获得地表温度 1.学习目标 ①了解如何使用GEE计算地表温度 2.理论介绍 城市化涉及用建筑物、道路和停车场等建筑结构取代自然景观。这种土地覆盖的改变也改变了土地表面的特性。这些变化的范围从表面反射和…