【C++】 单例设计模式的讲解

news2024/11/18 8:34:00

前言
在我们的学习中不免会遇到一些要设计一些特殊的类,要求这些类只能在内存中特定的位置创建对象,这就需要我们对类进行一些特殊的处理,那我们该如何解决呢?

目录

    • 1. 特殊类的设计
      • 1.1 设计一个类,不能被拷贝:
      • 1.2设计一个类,只能在堆上创建对象
        • 方法二:
      • 1.3 设计一个类,只能在栈上创建对象
      • 1.4 请设计一个类,不能被继承:
    • 2 单例模式:
      • 2.1 饿汉模式:
      • 2.2 懒汉模式:
      • 2.3 特殊情况下,单例类的释放

1. 特殊类的设计

1.1 设计一个类,不能被拷贝:

  • 拷贝构造函数以及赋值运算符重载
  • 只需让该类不能调用拷贝构造函数以及赋值运算符重载即可

C++98的处理方式:

  • 让拷贝构造和赋值重载设成私有即可

  • 只声明不定义。

  • C++11扩展delete的用法,delete除了释放new申请的资源外

  • 如果在默认成员函数后跟上 = delete,表示让编译器删除掉该默认成员函数。

class CopyBan
{
	// ...
	CopyBan(const CopyBan&) = delete;
	CopyBan& operator=(const CopyBan&) = delete;
	// ...
};

1.2设计一个类,只能在堆上创建对象

C++98的设计的方法:

  • 就直接将构造函数私有化

  • 只声明不定义。

    C++11的做法:

class HeapOnly
{
public:
	static HeapOnly* CreateObj()
	{
		//这里new直接去调用构造函数去了,类里面直接调用没限制
		return new HeapOnly;
	}

	//防拷贝,不让拷贝(形参可写可不写)
	HeapOnly(const HeapOnly&) = delete;

	//赋值并不会影响创建的对象跑到其他的地方去了,赋值不是创建对象的
	//拷贝构造反而是用来创建对象的

private:
	//构造函数私有 -- 将构造函数封起来(三条路都封死)
	HeapOnly()
	{}
};

问题:

  1. 那我们在类外调用CreateObj()来创建对象的时候,会有个问题
  2. 那就是著名的先有鸡还是先有蛋的问题
  3. 调用成员函数,就要有对象,要有对象,要有对象,就需要先构造
  4. 我们要解决这个问题就必须打破这个循环死穴 我们将CreateObj()函数设成静态函数

补充:

  • 普通的非静态的构造函数都需要对象去调用
  • 而构造函数不需要

此时还有个问题就是,拷贝构造也是会在栈上创建对象,我们可以将拷贝构造设成私有,或者C++11中直接将拷贝构造给删除掉。

方法二:

相比于上一种方法(将构造函数和拷贝构造私有或者删除),方法二显得更加牵强一点,将析构函数设成私有的,这样当对象生命周期结束时自动调用析构函数时会调用不到。

  • 这种方式的缺陷是:new的时候确实能创建出来,但是在delete释放的时候会报错

解决办法:类外调用不了私有的析构函数,但是类内可以调用,所以我们可以提供一个接口

class HeapOnly
{
public:
	static void DelObj(HeapOnly* ptr)
	{
		delete ptr;
	}

    //比较花的玩法 -- 这样还不用传参了
	/*void DelObj()
	{
		delete this; 
	}*/

private:
	//析构函数私有
	~HeapOnly()
	{}
};

int main()
{
	//HeapOnly h1;
	//static HeapOnly h2;
	HeapOnly* ph3 = new HeapOnly;

	//delete ph3;
	
	//方法一:要传参
	ph3->DelObj(ph3);

	//方法二:不用传参
	//ph3->DelObj();

	return 0;
}

1.3 设计一个类,只能在栈上创建对象

类似于上一个问题中的方法,我们先将三条路封死,然后单独给在栈上创建对象开条出路。

思路如下:

  • 先将构造函数私有化
  • 然后在类内写一个函数专门用来创建对象并负责将其传出来
  • 注意:此时传返回值只能是传值返回,不能传指针,也不能传引用
  • 因为是在栈上创建对象,是个局部变量,出了作用域就销毁掉了
class StackOnly
{
public:
	static StackOnly CreateObj()
	{
		//局部对象,不能用指针返回,也不能用引用返回,只能传值返回
		//传值返回必然会有一个拷贝构造发生

		return StackOnly();
	}

	void Print()
	{
		cout << "Stack Only" << endl;
	}
private:
	//构造函数私有
	StackOnly()
	{}
};

int main()
{
	StackOnly h1 = StackOnly::CreateObj();

	//不用对象去接受
	StackOnly::CreateObj().Print();

	//static StackOnly h2;
	
	//禁掉下面的玩法
	//StackOnly* ph3 = new StackOnly;

	return 0;
}

注意:

这里不能将拷贝构造给禁掉,因为CreateObj()是传值返回,传值返回必然会有一个拷贝构造(不考虑编译器优化的问题)。

1.4 请设计一个类,不能被继承:

  • 构造函数私有
  • 只声明不定义
class A
{
private:
	A()
	{}
};

class B : public A
{

};

int main()
{
	B b;
	
	return 0;
}
  • 父类A的构造函数私有化以后,B就无法构造对象
  • 因为规定了子类的成员必须调用父类的构造函数初始化

这时又有一个问题 —— 先有鸡还是先有蛋的问题:

  • 调用成员函数需要对象,对象创建需要调用成员函数,调用成员函数需要对象…
    在这里插入图片描述

如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明补丁已,这样只要其他人想要调用就会报错。
在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。

2 单例模式:

概念:

一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,一个进程只能有一个对象,并提供一个访问它的全局访问点,该实例被所有程序模块共享.

2.1 饿汉模式:

  • 这种模式是不管你将来用不用这个类的对象,程序启动时就创建一个唯一的实例对象。
  • 创建多线程都是在main函数之后创建的。(main函数之前没有多线程)。
%饿汉模式--一开始(main函数之前)就创建出对象
%优点:简单、没有线程安全问题
%缺点:
 1,一个程序中,多个单例,并且有先后创建初始化顺序要求时,饿汉无法控制
  比如程序中两个单例类A和B,假设要求A先创建初始化,B再创建初始化,
 2.饿汉单例类,初始化时任务多,会影响程序启动速度。


class MemoryPool
{
public:
	static MemoryPool* GetInstance()
	{
		cout << _spInst << endl;
		return _spInst;
	}
	
	void Print();
private:
 //构造函数私有化
	MemoryPool()
	{}

	//防不住拷贝,直接将其删除
	MemoryPool(const MemoryPool&) = delete;
	MemoryPool & operator=(MemoryPool const&) = delete
	
	char*  _ptr=nullptr; //声明
	
	static MemoryPool* _spInst; //声明
};

void MemoryPool::Print()
{
	cout << *_ptr<< endl;
}

MemoryPool* MemoryPool::_spInst = new MemoryPool; //定义

int main()
{
	//GetInstance() 可以或者这个Singleton类的单例对象
	MemoryPool::GetInstance()->Print();

	//在外面就定义不出来对象了
	//MemoryPoolst1;
	//MemoryPool* st2 = new MemoryPool;
	
	//拷贝删除掉
	//MemoryPool copy(*MemoryPool::GetInstance());

	return 0;
}

2.2 懒汉模式:

  • 开始不创建对象,在第一调用GetInstance()的时候再创建对象。

优点:

  • 1.控制顺序
  • 2.不影响启动速度。

缺点:

  • 1.相对复杂(线程安全问题没讲)
  • 2.线程安全问题要处理好
class Singleton
{
public:
   static Singleton* GetInstance() {
    **/ 注意这里一定要使用Double-Check的方式加锁,才能保证效率和线程安全**
    %一定要用双把锁
     if (nullptr == m_pInstance) 
     {
          m_mtx.lock();
         if (nullptr == m_pInstance)
              {
               m_pInstance = new Singleton();
               }
        m_mtx.unlock();
     }
   return m_pInstance;
}

private:
   // 构造函数私有
   Singleton()
    {};
   // 防拷贝
   Singleton(Singleton const&);
   Singleton& operator=(Singleton const&);

   static Singleton* m_pInstance; // 单例对象指针
   static mutex m_mtx; //互斥锁
};

Singleton* Singleton::m_pInstance = nullptr;
mutex Singleton::m_mtx


  • 与饿汉模式不同的是, 懒汉是模式则是一开始不创建对象,而是一开始先给一个指针,这样就避免了程序启动起来很慢的问题。

思路:

  • 将构造函数私有化
  • 一开始不构建对象,只是给一个空指针
  • 在需要的时候再调用其构造函数构造

补充:

  • 懒汉是一种延迟加载,饿汉是一开始就加载

2.3 特殊情况下,单例类的释放

在这里插入图片描述

解决方案:

  • 设计一个内部类
    在这里插入图片描述

尾声
看到这里,相信大家对这个C++有了解了。
如果你感觉这篇博客对你有帮助,不要忘了一键三连哦

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

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

相关文章

算法特训,AB5 .点击消除BC.149简写单词牛客.除2!牛客.Fibonacci数列

目录 AB5 .点击消除 BC.149简写单词 牛客.除2&#xff01; 牛客.Fibonacci数列 AB5 .点击消除 点击消除&#xff0c;类似于括号匹配a(b[b]a){c{d,这种&#xff0c;利用栈去消除,这样正好可以处理&#xff0c;假如相同就不进栈&#xff0c;同时还要出栈。注意我们这么搞完他是…

Leaflet【二】图层绘制——UI图层【点线面】 矢量图层【img、svg】

layer图层 在leaflet当中使用图层比OL当中简便一点&#xff0c;我们创建的layer图层可以直接通过 addTo 方法加到地图上&#xff0c;不需要通过layer、source再去做一些区分&#xff0c; 图标 Icon 创建Marker时提供的一个Icon 详细配置–>go // 导入一张图片作为图标imp…

在 Android 上存档短信:4 种方法的终极指南

概括 无论是个人对话还是专业信件&#xff0c;我们的短信收件箱很快就会因大量线程和对话而变得混乱。为了帮助管理这种过载&#xff0c;许多 Android 用户转向了归档短信这一便捷功能。在本指南中&#xff0c;我们将探讨如何在 Android 设备上存档短信的详细信息&#xff0c;…

文心一言 VS 讯飞星火 VS chatgpt (265)-- 算法导论20.1 4题

四、假设不使用一棵叠加的度为 u \sqrt{u} u ​ 的树&#xff0c;而是使用一棵叠加的度为 u 1 k u^{\frac{1}{k}} uk1​的树&#xff0c;这里 k 是大于 1 的常数&#xff0c;则这样的一棵树的高度是多少&#xff1f;又每个操作将需要多长时间&#xff1f;如果要写代码&#xf…

JPHS-JMIR Public Health and Surveillance

文章目录 一、期刊简介二、征稿信息三、期刊表现四、投稿须知五、投稿咨询 一、期刊简介 JMIR Public Health and Surveillance是一本多学科期刊&#xff0c;专注于公共卫生创新与技术的交叉领域&#xff0c;包括公共卫生信息学、监测&#xff08;监测系统和快速报告&#xff…

大模型难落地?聊聊大模型在智能财务应用的正确打开方式

大模型难落地&#xff1f;No&#xff0c;是你还不够了解它&#xff01; &#xff08;全文4989字&#xff0c;阅读约需10分钟&#xff09; 这两天&#xff0c;大模型几乎成了WAIC&#xff08;世界人工智能大会&#xff09;的唯一主题。大会上&#xff0c;各家企业的大模型悉数…

excel转pdf并且加水印,利用ByteArrayOutputStream内存流不产生中间文件

首先先引入包&#xff1a;加水印和excel转PDF的 <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.12</version></dependency><dependency><groupId>org.apache.poi&l…

vue核心模块源码解析

响应式原理 Object.definePropertysetterProxy var count 1 var state {} Object.defineProperty(state , count,{get(){return count},set(val){count val} }) //弊端&#xff1a;不能主动监听到对象属性的新增或者删除&#xff0c;add/deleteref和reactive 声明响应式数…

Golang net/http标准库常用方法(二)

大家好&#xff0c;针对Go语言 net/http 标准库&#xff0c;将梳理的相关知识点分享给大家~~ 围绕 net/http 标准库相关知识点还有许多章节&#xff0c;请大家多多关注。 文章中代码案例只有关键片段&#xff0c;完整代码请查看github仓库&#xff1a;https://github.com/hltfa…

K8S认证|CKA题库+答案| 1. 权限控制RBAC

1、权限控制RBAC 您必须在以下Cluster/Node上完成此考题&#xff1a; Cluster Master node Worker node k8s master …

【云原生】Kubernetes 核心概念

什么是 Kubernetes Kubernetes&#xff0c;从官方网站上可以看到&#xff0c;它是一个工业级的容器编排平台。Kubernetes 这个单词是希腊语&#xff0c;它的中文翻译是“舵手”或者“飞行员”。在一些常见的资料中也会看到“ks”这个词&#xff0c;也就是“k8s”&#xff0c;它…

2024年信息素养大赛图形化编程、Python、算法创真题汇总

2024年信息素养大赛编程赛道初赛&#xff08;Scratch图形化编程、Python、C算法创意&#xff09;已经结束&#xff0c;根据Scratch实验室的了解全国青少年信息素养大赛初赛晋级及初赛成绩内容如下&#xff1a; 1.参赛选手将在 5 个工作日(节假日不计在内)内&#xff0c;通过信…

开源的数据标注工具--Label-Studio

最近在了解构建知识图谱的相关知识&#xff0c;收集了一些数据&#xff0c;对数据进行标注时尝试了下Label-Studio这个工具&#xff0c;它是开源的数据标注工具&#xff0c;个人觉得还是挺好用的。 Label-studio的安装 我是直接在服务器上用pip安装的&#xff0c;命令如下&am…

【刷题篇】位运算

文章目录 1、判定字符是否唯一2、丢失的数字3、两整数之和4、只出现一次的数字 II5、 消失的两个数字 1、判定字符是否唯一 class Solution { public:bool isUnique(string astr) {int nastr.size();if(n>26)//鸽巢原理return false;int bitMap0;for(auto& e : astr){in…

echart 折线图tooltip

运行结果 代码 import { truncate, merge } from lodash; import { getBasePieOptions, getTooltipFormatter } from "*/money/utils";const colorArray [#1F8BFF, #EDBE75, #26E3F0, #AF8FFF, #61DDAA, #FD996A, #8367E0, #1AAF87]export function getLineOptions…

大数据运维学习笔记之Ambari——筑梦之路

原则&#xff1a;分布式存储和分布式计算分开 今天就到这里啦。

以色列人Andi Gutmans开发的php zend

虽然目前php语言不行了【相关的文章前几年已经有人发过】&#xff0c;但这不是重点&#xff0c;重点是zend引擎的东西具有极大的技术价值&#xff0c;负责zend引擎实现的大佬都现在差不多都是40&#xff0c;50岁左右了&#xff0c;从1997&#xff0c;1998&#xff0c;2000到202…

记录centos中操作(查找、结束、批量)进程以及crontab定时写法的知识

环境&#xff1a;vps&#xff0c;centos7&#xff0c;python3。 近期写了个python程序&#xff0c;用青龙面板在centos上运行。程序中有while无限循环&#xff0c;但是我在青龙中设置了定时任务&#xff08;每隔半小时运行一次&#xff09;&#xff0c;于是造成了进程中有多个…

一个用Java编写的屏幕测距工具,包括游戏地图测量功能

该程序提供了一个简单便捷的方式&#xff0c;在屏幕上测量距离&#xff0c;包括游戏地图分析在内。它允许用户准确确定屏幕上两点之间的距离&#xff0c;帮助游戏过程中的战略规划、资源管理和决策制定。 特点&#xff1a; 简单易用的界面&#xff1a;直观的控制使测量距离变得…

C++的红黑树

目录 基本概念 插入结点的颜色 判断性质是否破坏 调整方式 u为g的右孩子 u存在且为红 u存在且为黑 u不存在 结论 红黑树结点定义 代码实现 基本概念 1、红黑树是一种特殊的二叉搜索树&#xff0c;每个结点会增加一个存储位表示结点的颜色&#xff08;红或黑&#x…