当前位置: 首页 > news >正文

面向对象设计与分析(28)单例模式的奇异递归模板CRTP实现

前面我们介绍了单例模式的两种实现:懒汉模式和饿汉模式,今天我们以新的方式来实现可复用的单例模式。

奇异递归模板是指父类是个模板类,模板类型是子类类型,即父类通过模板参数可以知道子类的类型。

// brief: a singleton base class offering an easy way to create singleton
#include <iostream>template<typename T>
class Singleton{
public:static T& Instance(){static T instance;return instance;}Singleton(const Singleton&)=delete;Singleton& operator =(const Singleton&)=delete;protected:Singleton() {std::cout<<"constructor called!"<<std::endl;}
};
/********************************************/
// Example:
// 1.friend class declaration is requiered!
// 2.constructor should be private
class DerivedSingle : public Singleton<DerivedSingle> {// !!!! attention!!!// needs to be friend in order to// access the private constructor/destructorfriend class Singleton<DerivedSingle>;private:DerivedSingle() = default;
};int main(int argc, char* argv[]){DerivedSingle& instance1 = DerivedSingle::Instance();DerivedSingle& instance2 = DerivedSingle::Instance();return 0;
}

该模式的思想是,通过模板类的静态成员变量来确保一个类只有一个实例,并且可以通过静态函数来获取该实例。在这种模式下,我们将 Singleton 类作为基类,派生出一个具体的单例类(例如 MySingleton),并让 MySingleton 类继承自 Singleton<MySingleton>

这个单例模式有非常多的实现细节需要注意,足以考察你的C++功底。

首先Singleton的构造是protected的,因为Singleton本身只是个帮助类,并没有单独实例化的需要,但是子类需要实例化,所以需要protected子类才可以访问。

Singleton 类中,我们定义了一个 Instance() 静态函数,返回一个类型为 T& 的对象。在 Instance() 函数中,我们定义了一个静态局部变量 instance,用于存储 T 类型的唯一实例。由于静态局部变量的生命周期与程序的运行周期相同,因此 nstance 只会在程序第一次调用 Instance() 函数时被创建,并在程序结束时被销毁。通过返回 instance 的引用,我们可以保证每次调用 Instance() 函数时都返回同一个实例。

此外,我们在 Singleton 类删除拷贝构造和赋值运算符的语句,以确保单例对象不能被复制或赋值,并且能够正确释放资源。

在这里基类的析构函数可以不需要 virtual ,因为子类在应用中只会用 Derived 类型,保证了析构时和构造时的类型一致

MySingleton 类中,我们只需要简单地继承自 Singleton<MySingleton>,并在构造函数中添加一些特定的逻辑即可。由于 MySingleton 类已经继承自 Singleton<MySingleton>,因此可以通过调用 Singleton<MySingleton>::Instance() 函数来获取唯一的 MySingleton 实例。

这种使用 CRTP 实现的单例模式具有以下优点:

  • 代码简洁:只需要定义一个基类和若干个派生类即可,无需编写大量重复的单例模式代码。
  • 线程安全:由于静态局部变量的创建是线程安全的,因此该模式天然支持多线程环境下的单例实现。
  • 性能高效:由于只需要在程序第一次调用 Instance() 函数时创建实例,因此该模式对性能的影响较小

这里也有几个特殊的限制:

  • 首先,子类还必须将构造私有化
  • 其次,由于子类构造私有化,但父类需要创建子类实例,因此需要将父类声明为子类的友元类。

相关文章:

面向对象设计与分析(28)单例模式的奇异递归模板CRTP实现

前面我们介绍了单例模式的两种实现&#xff1a;懒汉模式和饿汉模式&#xff0c;今天我们以新的方式来实现可复用的单例模式。 奇异递归模板是指父类是个模板类&#xff0c;模板类型是子类类型&#xff0c;即父类通过模板参数可以知道子类的类型。 // brief: a singleton base…...

微信小程序 - 龙骨图集拆分

微信小程序 - 龙骨图集拆分 注意目录结构演示动画废话一下业务逻辑注意点龙骨JSON图集结构 源码分享dragonbones-split.jsdragonbones-split.jsondragonbones-split.wxmldragonbones-split.wxssimgUtil.js 参考资料 注意 只支持了JSON版本 目录结构 演示动画 Spine播放器1.5.…...

使用React 18和WebSocket构建实时通信功能

1. 引言 WebSocket是一种在Web应用中实现双向通信的协议。它允许服务器主动向客户端推送数据&#xff0c;而不需要客户端发起请求。在现代的实时应用中&#xff0c;WebSocket经常用于实时数据传输、聊天功能、实时通知和多人协作等场景。在本篇博客中&#xff0c;我们将探索如…...

vue3使用vue-router嵌套路由(多级路由)

文章目录 1、Vue3 嵌套路由2、项目结构3、编写相关页面代码3.1、编写route文件下 index.ts文件3.2、main.ts文件代码&#xff1a;3.3、App.vue文件代码&#xff1a;3.4、views文件夹下的Home文件夹下的index.vue文件代码&#xff1a;3.5、views文件夹下的Home文件夹下的Tigerhh…...

openGauss学习笔记-164 openGauss 数据库运维-备份与恢复-导入数据-使用COPY FROM STDIN导入数据-处理错误表

文章目录 openGauss学习笔记-164 openGauss 数据库运维-备份与恢复-导入数据-使用COPY FROM STDIN导入数据-处理错误表164.1 操作场景164.2 查询错误信息164.3 处理数据导入错误 openGauss学习笔记-164 openGauss 数据库运维-备份与恢复-导入数据-使用COPY FROM STDIN导入数据-…...

QT Widget - 随便画个圆

简介 实现在界面中画一个圆, 其实目的是想画一个LED效果的圆。代码 #include <QApplication> #include <QWidget> #include <QPainter> #include <QColor> #include <QPen>class LEDWidget : public QWidget { public:LEDWidget(QWidget *pare…...

js输入框部分内容不可编辑,其余正常输入,el-input和el-select输入框和多个下拉框联动后的内容不可修改

<tr>//格式// required自定义指令<e-td :required"!read" label><span>地区&#xff1a;</span></e-td><td>//v-if"!read && this.data.nationCode 148"显示逻辑<divclass"table-cell-flex"sty…...

分布式文件存储系统minio了解下

什么是minio minio 是一个基于 Apache License v2.0 开源协议的对象存储服务。非常适合于存储大容量非结构化的数据&#xff0c;例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等&#xff0c;而一个对象文件可以是任意大小。 是一种海量、安全、低成本、高可靠的云存储…...

迅为RK3568开发板使用OpenCV处理图像-ROI区域-位置提取ROI

在图像处理过程中&#xff0c;我们可能会对图像的某一个特定区域感兴趣&#xff0c;该区域被称为感兴趣区域&#xff08;Region of Interest, ROI&#xff09;。在设定感兴趣区域 ROI 后&#xff0c;就可以对该区域进行整体操作。 位置提取 ROI 本小节代码在配套资料“iTOP-3…...

重新认识Word——尾注

重新认识Word——尾注 参考文献格式文献自动生成器插入尾注将数字带上方括号将参考文献中的标号改为非上标 多处引用一篇文献多篇文献被一处引用插入尾注有横线怎么删除&#xff1f;删除尾注 前面我们学习了如何给图片&#xff0c;公式自动添加编号&#xff0c;今天我们来看看毕…...

所有学前教育专业,一定要刷到这篇啊

我是真的希望所有学前教育的宝子都能刷到这篇啊啊&#xff0c;只要输入需求&#xff0c;几秒它就给你写出来了&#xff0c;而且不满意还可以重新写多&#xff0c;每次都是不一样的内容。重复率真的不高&#xff0c;需求越多&#xff0c;生成的文字内容越精准&#xff01;&#…...

colmap三维重建核心逻辑梳理

colmap三维重建核心逻辑梳理 1. 算法流程束流2. 初始化3. 重建主流程 1. 算法流程束流 重建核心逻辑见 incremental_mapper.cc 中 IncrementMapperController 中 Reconstruct 初始化变量和对象判断是否有初始重建模型&#xff0c;若有&#xff0c;则获取初始重建模型数量&am…...

查询某个类是在哪个JAR的什么版本开始出现的方法

背景 我们在依赖第三方JAR时&#xff0c;同时也会间接的依赖第三方JAR引用的依赖&#xff0c;而当我们项目中某个依赖的版本与第三方JAR依赖的版本不一致时&#xff0c;可能会导致第三方JAR的在运行时无法找到某些方法或类&#xff0c;从而无法正常使用。 如我正在开发的一个…...

Linux本地搭建StackEdit Markdown编辑器结合内网穿透实现远程访问

文章目录 1. docker部署Stackedit2. 本地访问3. Linux 安装cpolar4. 配置Stackedit公网访问地址5. 公网远程访问Stackedit6. 固定Stackedit公网地址 StackEdit是一个受欢迎的Markdown编辑器&#xff0c;在GitHub上拥有20.7k Star&#xff01;&#xff0c;它支持将Markdown笔记保…...

k8s中ConfigMap、Secret创建使用演示、配置文件存储介绍

目录 一.ConfigMap&#xff08;cm&#xff09; 1.适用场景 2.创建并验证configmap &#xff08;1&#xff09;以yaml配置文件创建configmap&#xff0c;验证变化是是否同步 &#xff08;2&#xff09;--from-file以目录或文件 3.如何使用configmap &#xff08;1&#x…...

Linux服务器性能优化小结

文章目录 生产环境监测常见专业名词扫盲服务器平均负载服务器平均负载的定义如何判断平均负载值以及好坏情况如果依据平均负载来判断服务器当前状况系统平均负载和CPU使用率的区别 CPU上下文切换基本概念3种上下文切换进程上下文切换线程上下文切换中断上下文切换 查看上下文切…...

ELF文件结构

ELF文件结构 前文结尾说到编译器编译源代码后生成的文件叫做目标文件&#xff0c;而目标文件经过编译器链接之后得到的就是可执行文件。那么目标文件到底是什么&#xff1f;它和可执行文件又有什么区别&#xff1f;链接到底又做了什么呢&#xff1f;接下来&#xff0c;我们将探…...

【C++】有关string迭代器的几道OJ题详解

目录 一、字符串最后一个单词的长度 题目描述 完整代码 二、验证回文串 题目描述 完整代码 三、反转字符串 题目描述 完整代码 四、反转字符串中的单词 题目描述 完整代码 一、字符串最后一个单词的长度 原题链接 题目描述 计算字符串最后一个单词的长度&#xff…...

谷歌宣布向云计算客户开放 Gemini Pro,开发者可用其构建应用

12 月 14 日消息&#xff0c;美国时间周三&#xff0c;谷歌宣布了一系列升级的人工智能&#xff08;AI&#xff09;功能&#xff0c;旨在为其云计算客户提供更好的服务。这家科技巨头正试图赶上竞争对手&#xff0c;比如微软和 OpenAI&#xff0c;它们都在积极利用人工智能的热…...

软件测试用例经典方法 | 单元测试法案例

单元测试又称模块测试&#xff0c;是对软件设计的最小单元的功能、性能、接口和设计约束等的正确性进行检验&#xff0c;检查程序在语法、格式和逻辑上的错误&#xff0c;并验证程序是否符合规范&#xff0c;以发现单元内部可能存在的各种缺陷。 单元测试的对象是软件设计的最…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题&#xff1a;3564. 季节性销售分析 题目&#xff1a; 表&#xff1a;sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

BLEU评分:机器翻译质量评估的黄金标准

BLEU评分&#xff1a;机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域&#xff0c;衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标&#xff0c;自2002年由IBM的Kishore Papineni等人提出以来&#xff0c;…...