C++11改进单例模式

        单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。

        C++11之前,实现一个通用的泛型单例模式时,会遇到一个问题:这个泛型单例要能够创建所有的类型对象,但是这些类型的构造函数形参可能尽不相同,参数个数和参数类型可能都不相同,这导致我们不容易做一个所有类型都通用的单例。一种方法是通过定义一些创建单例的模板函数来实现。在一般情况下,类型的构造函数形参不超过6个,所以可以通过定义0~6个形参的创建单例的模板函数来实现一个通用的单例模式,代码如下所示:

#include <iostream>
using namespace std;template<typename T>
class Singleton
{
public:///支持0个参数的构造函数static T* Instance(){if (m_pInstance == nullptr){m_pInstance = new T();}return m_pInstance;}///支持1个参数的构造函数template<typename T0>static T* Instance(T0 arg0){if (m_pInstance == nullptr){m_pInstance = new T(arg0);}return m_pInstance;}///支持2个参数的构造函数template<typename T0, typename T1>static T* Instance(T0 arg0, T1 arg1){if (m_pInstance == nullptr){m_pInstance = new T(arg0, arg1);}return m_pInstance;}///支持3个参数的构造函数template<typename T0, typename T1, typename T2>static T* Instance(T0 arg0, T1 arg1, T2 arg2){if (m_pInstance == nullptr){m_pInstance = new T(arg0, arg1, arg2);}return m_pInstance;}///支持4个参数的构造函数template<typename T0, typename T1, typename T2, typename T3>static T* Instance(T0 arg0, T1 arg1, T2 arg2, T3 arg3){if (m_pInstance == nullptr){m_pInstance = new T(arg0, arg1, arg2, arg3);}return m_pInstance;}///支持5个参数的构造函数template<typename T0, typename T1, typename T2, typename T3, typename T4>static T* Instance(T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4){if (m_pInstance == nullptr){m_pInstance = new T(arg0, arg1, arg2, arg3, arg4);}return m_pInstance;}///支持6个参数的构造函数template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>static T* Instance(T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5){if (m_pInstance == nullptr){m_pInstance = new T(arg0, arg1, arg2, arg3, arg4, arg5);}return m_pInstance;}///获取单例static T* GetInstance(){if (m_pInstance == nullptr){std::logic_error("the instance is not init, please initialize the instance first");}return m_pInstance;}///释放单例static void DestoryInstance(){delete m_pInstance;m_pInstance = nullptr;}private:///不允许复制和赋值Singleton(void);virtual ~Singleton(void);Singleton(const Singleton&);Singleton& operator=(const Singleton&);private:static T* m_pInstance;
};template<class T> 
T* Singleton<T>::m_pInstance = nullptr;class A
{
public:A(){cout << "construct A...." << endl;}
};class B 
{
public:B(int x){m_x = x;cout << "construct B...." << endl;}
private:int m_x;
};class C 
{
public:C(int x, double db){m_x = x;m_db = db;cout << "construct C...." << endl;}
private:int m_x;int m_db;
};int main()
{///创建A类型的单例Singleton<A>::Instance();///创建B类型的单例Singleton<B>::Instance(1);///创建C类型的单例Singleton<C>::Instance(1, 2.0);Singleton<A>::DestoryInstance();Singleton<B>::DestoryInstance();Singleton<C>::DestoryInstance();return 0;
}

        从测试代码中可以看到,这个Singleton<T>可以创建大部分类型,支持不超过6个参数的类型。不过,从实现代码中可以看到,有很多重复的模板定义,这种定义繁琐而又重复,当参数超过6个时,我们不得不再增加模板定义。这种预先定义足够多的模板函数的方法显得重复又不够灵活。

        C++11d可变参数模板正好可以消除这种重复,同时支持完美转发,即避免不必要的内存复制提高性能,又增加了灵活性。C++11实现的一个简洁通用的单例模式如下所示:

#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <string.h>
using namespace std;template<typename T>
class Singleton
{
public:template<typename ...Args>static T* Instance(Args&& ...args){if (m_pInstance == nullptr) {///完美转发m_pInstance = new T(std::forward<Args>(args)...);}return m_pInstance;}///获取单例static T* GetInstance(){if (m_pInstance == nullptr){throw std::logic_error("the instance is not init, please initialize the instance first");}return m_pInstance;}static void DestoryInstance(){delete m_pInstance ;m_pInstance = nullptr;}private:Singleton(void);virtual ~Singleton();Singleton(const Singleton&);Singleton& operator=(const Singleton&);private:static T* m_pInstance;
};template<class T>
T* Singleton<T>::m_pInstance = nullptr;class A
{
public:A(const string& ){cout << "A lvalue" << endl;}A(string&& ){cout << "A rvalue" << endl;}
};class B 
{
public:B(const string& ){cout << "B lvalue" << endl;}B(string&& ){cout << "B rvalue" << endl;}
};class C
{
public:C(int x, int y){cout << "C construct" << endl;}void func(){cout << "class C call func..." << endl;}
};std::once_flag init_flagA;
std::once_flag init_flagB;
std::once_flag init_flagC;void func1()
{///为了保证在多线程环境中某个函数仅被调用一次,比如,需要初始化某个对象,而这个对象只能初始化一次,就可以用std::call_once来保证在多线程环境中只被调用一次std::call_once(init_flagA, []() {Singleton<A>::Instance("123");});std::call_once(init_flagB, []() {Singleton<B>::Instance(std::move("abc"));});std::call_once(init_flagC, []() {Singleton<C>::Instance(1, 2);});
}void func2()
{///为了保证在多线程环境中某个函数仅被调用一次,比如,需要初始化某个对象,而这个对象只能初始化一次,就可以用std::call_once来保证在多线程环境中只被调用一次std::call_once(init_flagA, []() {Singleton<A>::Instance("456");});std::call_once(init_flagB, []() {Singleton<B>::Instance(std::move("efg"));});std::call_once(init_flagC, []() {Singleton<C>::Instance(3, 4);});
}int main()
{thread t1(func1);thread t2(func1);t1.join();t2.join();Singleton<C>::GetInstance()->func();getchar();Singleton<A>::DestoryInstance();Singleton<B>::DestoryInstance();Singleton<C>::DestoryInstance();return 0;
}

        可以看到,C++11版本的通用单例模式的实现,没有了重复的模板定义,支持任意个数参数的类型创建,不用再担心模板函数定义得不够,还支持完美转发,无论是左值还是右值都能转发到正确的构造函数中,通过右值引用的移动语义还能进一步提高性能,简洁而优雅。

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

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

相关文章

Appium:进行iOS自动化测试遇到的问题与解决方案

问题与解决方案 在使用Appium进行iOS自动化测试时&#xff0c;可能会遇到各种问题。 以下是一些常见的问题及其解决方案&#xff1a; 无法启动inspector问题 最新版本的Appium已经将内置的inspector独立了出去&#xff0c;需要单独安装inspector进行调试。确保inspector对应…

任意密码重置+CRRF

一、XSS漏洞 在商城的搜索处&#xff0c;输入标准语句的传参直接就可以弹窗 二、逻辑漏洞-用户枚举 在用户注册界面&#xff0c;点击发送验证码&#xff0c;然后用BURP发包 更改手机号传参&#xff0c;这里手机号传参没有进行加密&#xff0c;直接用手机号的位置进行爆破 正确的…

LeetCode力扣每日一题(Java):9、回文数

一、题目 二、解题思路 1、我的思路 当x<0时&#xff0c;x一定不是回文数&#xff0c;直接返回false 当x>0且x<10时&#xff0c;x一定是回文数&#xff0c;直接返回true x>10时&#xff0c;先将x转为字符串。将数字转成字符串方法挺多的&#xff0c;以下是&…

06、基于内容的过滤算法Tensorflow实现

06、基于内容的过滤算法Tensorflow实现 开始学习机器学习啦&#xff0c;已经把吴恩达的课全部刷完了&#xff0c;现在开始熟悉一下复现代码。全部工程可从最上方链接下载。 05、基于梯度下降的协同过滤算法中已经介绍了协同过滤算法的基本实现方法&#xff0c;但是这种方法仅…

SpringBoot集成i18n(多语言)

配置文件 spring: messages: basename: il8n/messages # 配置国际化资源文件路径 fallback-to-system-locale: true # 是否使用系统默认的语言环境作为备选项 国际化配置 import org.springframework.context.annotation.Bean; import org.spr…

Spring中通知是什么

面向切面编程 这个切面是哪个面 面向切面编程&#xff08;Aspect-Oriented Programming&#xff0c;AOP&#xff09;是一种编程范式&#xff0c;它通过切面&#xff08;Aspect&#xff09;来分离横切关注点&#xff08;Cross-cutting Concerns&#xff09;和核心业务逻辑。 在…

MySQL-宋红康-(课P14-P15)-基本查询语句(Select)

b站视频&#xff1a; 14-最基本的SELECT...FROM结构_哔哩哔哩_bilibili 8.4 基本查询(Select)语句 数据table: emp员工表创建如下 # 员工表 CREATE TABLE EMP (EMPNO INT PRIMARY KEY, -- 员工编号ENAME VARCHAR(10), -- 员工名称JOB VARCHAR(9), -- 工…

10、SQL注入——数据库基础

文章目录 一、数据库概述二、数据库分类Mysql数据库连接方法 三、识别数据库四、SQL语法4.1 SQL基本语法4.2 高级操作 一、数据库概述 数据库&#xff08;database&#xff09;&#xff1a;存储在磁盘、磁带、光盘或其他外存介质上、按一定结构组织在一起的相关数据的集合。数…

webpack对项目进行优化

对项目进行优化是提高性能和效率的关键&#xff0c;以下是一些实用的Webpack优化技巧&#xff1a; 代码拆分&#xff08;Code Splitting&#xff09;&#xff1a;将代码拆分为多个小块&#xff0c;按需加载。通过配置splitChunks插件&#xff0c;可以将公共代码提取到单独的文件…

常见代码优化案例记录

1. 使用StringBuilder优化字符串拼接&#xff1a; // 不优化的写法 String result ""; for (int i 0; i < 1000; i) {result i; }// 优化的写法 StringBuilder resultBuilder new StringBuilder(); for (int i 0; i < 1000; i) {resultBuilder.append(i)…

龙迅分配器LT86102UXE/LT86104UX,HDMI一分二/HDMI一分四

龙迅LT86102UXE描述; Lontium LT86102UXE HDMI2.0分配器具有1&#xff1a;2的分配器&#xff0c;符合HDMI2.0/1.4规范&#xff0c;最大6Gbps高速数据速率&#xff0c;自适应均衡RX输入和预先强调的TX输出&#xff0c;以支持长电缆应用程序&#xff0c;内部TX通道交换灵活的PCB…

部分c++11特性介绍

在2003年C标准委员会曾经提交了一份技术勘误表(简称TC1)&#xff0c;使得C03这个名字已经取代了C98称为C11之前的最新C标准名称。不过由于C03(TC1)主要是对C98标准中的漏洞进行修复&#xff0c;语言的核心部分则没有改动&#xff0c;因此人们习惯性的把两个标准合并称为C98/03标…

Day50力扣打卡

打卡记录 三个无重叠子数组的最大和 链接 滑动窗口 class Solution:def maxSumOfThreeSubarrays(self, nums: List[int], k: int) -> List[int]:n, ans len(nums), []sum1 sum2 sum3 0maxsum1idx, maxsum12idx 0, ()maxsum1 maxsum12 total 0for i in range(2 * …

01 高等数学.武忠祥.0基础

第一章 函数与极限 01映射与函数 02 函数概念 对应法则 定义域 常见函数 函数的几种特性 周期函数不一定有最小周期。 涉及额外与复习 存在与任意的关系

在python中自己定义一个方法,但是没有写return XXX会有什么影响

【多么痛的领悟&#xff0c;找了两个小时的错误&#xff01;&#xff01;&#xff01;发现是少写个return】 print(data.task_ID) AttributeError: NoneType object has no attribute task_ID** 然后一句一句找&#xff0c;我找啊找&#xff01;&#xff01;&#xff01;&…

MYSQL练题笔记-高级查询和连接-简单题3题

写了下面的前3道题。 一、每位经理的下属员工数量 看到题目就知道和之前的至少有5名下属的经理很相似&#xff0c;嘿嘿写对了就不做过多的讲解了。 二、员工的直属部门相关表和题目如下 刚开始觉得很简单&#xff0c;但是仔细想想这道题有两个输出&#xff0c;觉得想不出来&a…

C语言数组(上)

# 数组的概念 数组是一组相同类型元素的集合。数组中存放的是一个或多个数据&#xff0c;但是数组中的元素个数不能为零&#xff1b;数组中存放的所有元素&#xff08;数据&#xff09;的类型必须是相同的。 数组分为一维数组和多维数组&#xff0c;多维数组一般比较多见的是二…

跨域问题与解决-gatway

3.6.1.什么是跨域问题 跨域&#xff1a;域名不一致就是跨域&#xff0c;主要包括&#xff1a; 域名不同&#xff1a; www.taobao.com 和 www.taobao.org 和 www.jd.com 和 miaosha.jd.com域名相同&#xff0c;端口不同&#xff1a;localhost:8080和localhost8081 跨域问题&a…

ADB命令集锦,一起来学吧

前言 在测试APP时&#xff0c;我们常常会用到adb命令来协助测试&#xff0c;那么adb命令到底是什么&#xff1f;有什么用&#xff1f;怎么用&#xff1f; 今天我就整理了一些工作中常用的adb知识点&#xff0c;希望对大家有所帮助。 ADB学习全攻略 ADB是什么&#xff1f; a…

三种定时器的实现方式

一、Scheduled Schedule是Spring框架提供的一种简单的定时任务调度方法&#xff0c;通过注解的方式即可实现定时任务的调度。它适用于简单的定时任务需求&#xff0c;例如每隔一段时间执行一次任务或者在特定时间执行任务。Scheduled可以轻松地集成到Spring应用中&#xff0c;…