【C++】特殊类设计+单例模式+类型转换

news2024/11/27 2:41:47


目录

一、设计一个类,不能被拷贝

1、C++98

2、C++11

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

1、将构造设为私有

2、将析构设为私有

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

四、设计一个类,不能被继承

1、C++98

2、C++11

五、设计一个类,只能创建一个对象(单例模式)

1、饿汉模式设计单例模式

2、懒汉模式设计单例模式

3、单例对象的释放

4、一种比较简洁但是可能存在线程安全的单例懒汉模式

六、类型转换

1、C语言类型转换

2、C++新增四种强制类型转换

2.1static_cast

2.2reinterpret_cast

2.3const_cast

2.4dynamic_cast

3、RTTI


一、设计一个类,不能被拷贝

1、C++98

class CopyBan
{
private:
	CopyBan(const CopyBan& cb);
	CopyBan& operator=(const CopyBan& cb);
};
int main()
{

	return 0;
}

        1、将拷贝构造和赋值运算符重载设置为私有;

        2、仅仅私有还不够,这并不能防止类内部就行拷贝。还要对拷贝构造和赋值运算符重载只声明却不实现。

2、C++11

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

        C++11直接使用delete禁用拷贝构造和赋值运算符重载。

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

1、将构造设为私有

        1、将构造设为私有,防止外部构造,并提供一个CreateObj的函数用于构造堆区对象;

        2、但是外部需要对象来调用CreateObj函数来构造对象,所以需要将CreateObj设置为静态函数,无需对象也能调用。

        3、外部通过CreateObj函数构造一个对象后,外部可以利用这个对象的指针拷贝构造一个栈区的对象,需要禁用拷贝构造。

2、将析构设为私有

        将析构函数设置为私有,栈区对象由于无法析构所以无法创建。堆区对象需要手动调用自己写的清理函数释放。

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

        1、将构造函数私有;

        2、提供一个静态的CreateObj方法用于构造栈区对象;

        3、但是无法防止外部构造静态对象。

        4、如果想彻底禁止生成静态的对象,需要再禁用拷贝构造。不过这样这个类只能生成临时对象或者引用的对象了,不能修改。

四、设计一个类,不能被继承

1、C++98

class FinalClass
{
	static FinalClass CreateObj()
	{
		return FinalClass();
	}
private:
	FinalClass()
	{}
};

        1、禁用构造函数;

        2、提供一个静态的CreateObj函数供外部创建父类对象,但是子类会因为构造私有的继承不可见的原因无法构造出父类对象,所以无法继承。

2、C++11

class FinalClass final
{
private:
	
};

        C++11直接使用final关键字

五、设计一个类,只能创建一个对象(单例模式)

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

1、饿汉模式设计单例模式

class InfoSingleton
{
public:
	static InfoSingleton& GetInstance()
	{
		return _sins;
	}
	void Insert(string name, int money)
	{
		_info[name] = money;
	}
	void Print()
	{
		for (auto kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}
private:
	InfoSingleton()
	{}
	InfoSingleton(const InfoSingleton&) = delete;
	InfoSingleton& operator=(const InfoSingleton&) = delete;
	map<string, int> _info;
	static InfoSingleton _sins;
};
InfoSingleton InfoSingleton::_sins;
int main()
{
	InfoSingleton& s = InfoSingleton::GetInstance();//使用单例对象
	s.Insert("小明", 100);
	s.Insert("小红", 50);
	s.Insert("小绿", 130);
	s.Print();
	cout << endl;
	s.Insert("小绿", 10086);
	s.Print();
	return 0;
}

        饿汉模式:在main函数被加载之前就创建好对象。(全局和静态将在main函数之前被加载)

        1、私有构造函数,禁用拷贝构造和赋值运算符重载;

        2、在类中声明、外部定义一个静态的对象,用于调用类中私有的构造函数,同时作为单例对象被使用;

        3、在类中提供一个获取静态对象的函数GetInstance,为了外部可调用,所以将该函数设置为静态。

饿汉模式的特点:

1、单例对象初始化时,数据太多会导致启动慢;

2、如果多个单例类有初始化的依赖关系,饿汉模式无法控制。例如A和B都是单例类,因为B的启动依赖A,所以需要先初始化A,再初始化B,但是饿汉模式无法控制对象的初始化顺序。

3、饿汉模式创建的对象绝对不会有线程安全问题,因为该模式的对象在main函数之前已经被创建好了,mian函数之前线程都没启动呢。

 

2、懒汉模式设计单例模式

        饿汉模式:第一次获取单例对象的时候创建对象;

        1、私有构造函数,禁用拷贝构造和赋值运算符重载;

        2、在类中声明、外部定义一个静态的对象指针

        3、在类中提供一个获取静态对象的函数GetInstance,为了外部可调用,所以将该函数设置为静态。

        4、它与饿汉的写法区别如图红色标记处。

懒汉模式的特点:

1、对象在main函数之后才会创建;

2、可以主动控制对象的创建时机。

3、创建对象时存在线程安全问题,如果多个线程同时进入红框区域,会可能new多个对象,最后一个创建的对象指针会覆盖之前创建的对象指针,导致内存泄露。

 

        懒汉模式需要加锁解决线程安全问题:

        不过红框中new也可能会失败,再套一层try/catch看着太累,可以使用RAII锁解决:

        new这里还要try一下异常,main里函数捕获这个异常。(写漏了)

3、单例对象的释放

        1、一般单例对象不需要考虑释放,资源会在进程结束时自动释放;

        2、释放的写法如下:可以手动清理,将一些资源保存:可手动调用DelInstance进行资源的回收,main函数结束时,操作系统也会自动回收单例对象的资源。

4、一种比较简洁但是可能存在线程安全的单例懒汉模式

class InfoSingleton
{
public:
	static InfoSingleton& GetInstance()
	{
		//静态局部变量是在main函数之后才创建初始化
		static InfoSingleton sinst;
		return sinst;
	}
	void Insert(string name, int money)
	{
		_info[name] = money;
	}
	void Print()
	{
		for (auto kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}
private:
	InfoSingleton()
	{}
	InfoSingleton(const InfoSingleton&) = delete;
	InfoSingleton& operator=(const InfoSingleton&) = delete;
	map<string, int> _info;
};

        1、私有构造函数,禁用拷贝构造和赋值运算符重载;

        2、通过GetInstance返回静态对象(静态局部变量是在main函数之后才创建初始化)

这种方式构建的单例懒汉模式在C++11发布之前会有线程安全问题,多线程环境下可能会造成静态对象被初始化多次;而C++11规定静态局部变量是线程安全的,可以放心使用。 

六、类型转换

1、C语言类型转换

        1、隐式类型转换2、强制类型转换。

2、C++新增四种强制类型转换

        C++尤其认为隐式类型转化有些情况下可能会出问题:比如数据精度丢失。

2.1static_cast

int main()
{
	double d = 12.34;
	int a = static_cast<int>(d);
	cout << a << endl;
	return 0;
}

static_cast适用于相似类型的转换。(可以隐式类型转换的都能用static_cast) 

2.2reinterpret_cast

int main()
{
	double d = 12.34;
	int a = static_cast<int>(d);
	cout << a << endl;
	// 这里使用static_cast会报错,应该使用reinterpret_cast
	//int *p = static_cast<int*>(a);
	int* p = reinterpret_cast<int*>(a);
	return 0;
}

reinterpret_cast适用于不想关类型之间的转换。(不能隐式类型转换,只能强制类型转换的用reinterpret_cast)

2.3const_cast

const_cast用于删除变量的const属性。需要关注内存可见性问题。 

        编译器对const变量会有优化,认为const变量不会被改变,编译器在优化代码时可能会将变量放到寄存器或者其他高速缓存中。可以在a初始化时加上volatile关键字,加了volatile关键字后,对变量的读取和写入操作会从内存中进行,而不是从缓存中进行。

2.4dynamic_cast

        dynamic_cast用于父类的指针和引用转换为子类对象的指针和引用。(动态转换)

        向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)

        向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)

class A
{
public:
	virtual void f() {}
	int _a = 0;
};
class B : public A
{
public:
	int _b = 0;
};
void fun(A* ptr)
{
	//B* bptr = (B*)ptr;//直接转换是不安全的,父给子存在非法访问隐患
	B* bptr = dynamic_cast<B*>(ptr);
	if (bptr)//如果转换成功
	{
		bptr->_a++;
		bptr->_b++;
		cout << bptr->_a;
		cout << bptr->_b;
	}
}
int main()
{
	A aa;
	B bb;
	fun(&aa);//转换失败
	fun(&bb);//转换成功
	return 0;
}

注意:

1. dynamic_cast只能用于父类含有虚函数的类

2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0

3、RTTI

        RTTI:Run-time Type identifification的简称,即:运行时类型识别。

        C++通过以下方式来支持RTTI:

1. typeid运算符 
2. dynamic_cast运算符 
3. decltype

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

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

相关文章

UNIX环境高级编程——进程控制

8.1 引言 本章介绍UNIX系统的进程控制&#xff0c;包括&#xff1a; 创建新进程、执行程序、进程终止进程属性ID——实际、有效、保存的用户ID和组ID解释器文件system函数进程会计机制 8.2 进程标识 进程ID&#xff1a;一个非负整数&#xff0c;进程的唯一标识。 进程ID可…

【群智能算法】一种改进的蜣螂优化算法IDBO[2]【Matlab代码#18】

文章目录 1. 原始DBO算法2. 改进后的IDBO算法2.1 Bernoulli混沌映射种群初始化2.2 自适应因子2.3 Levy飞行策略2.4 动态权重系数 3. 部分代码展示4. 效果图展示5. 资源获取 1. 原始DBO算法 详细介绍此处略&#xff0c;可参考DBO算法介绍 2. 改进后的IDBO算法 2.1 Bernoulli混…

【Linux问题合集002】解决虚拟机里面的Linux系统部分无法上网情况,保姆级教程

&#x1f340;一、前言 正如标题所说&#xff0c;解决虚拟机里面的Linux系统部分无法上网情况&#xff0c;这个网络问题的原因有很多种可能&#xff0c;这篇博客不一定能够解决所有朋友的网络问题&#xff0c;但是如果遇到和我一样情况的&#xff0c;我保证解决步骤一定是非常详…

使用 Python 创建端到端聊天机器人

使用 Python 创建端到端聊天机器人 1. 效果图2. 原理2.1 什么是端到端聊天机器人&#xff1f;2.2 创建端到端聊天机器人步骤 3. 源码3.1 streamlit安装3.2 源码 参考 聊天机器人是一种计算机程序&#xff0c;它了解您的查询意图以使用解决方案进行回答。聊天机器人是业内最受欢…

《LKD3粗读笔记》(11)定时器和时间管理

文章目录 1、内核中的时间概念2、 节拍率&#xff1a;HZ3、jiffies4、硬时钟和定时器5、时钟中断处理程序6、实际时间7、定时器8、延迟执行 1、内核中的时间概念 硬件为内核提供了一个系统定时器用以计算流逝的时间&#xff0c;该时钟在内核中可看成是一个电子时间资源&#x…

Nginx安装删除JDK Tomcat Redis

1.卸载Nginx ps -ef|grep nginx 查询Nginx 进程pid 如上图 master是主进程, worker是工作进程, master负责维护worker进程 Nginx启动后默认启动master进程和worker进程 Nginx默认使用端口80 kill -9 7035 或者 kill -term 7035 kill -9 7036 查找根下所有名字包…

带你搞懂人工智能、机器学习和深度学习!

不少高校的小伙伴找我聊入门人工智能该怎么起步&#xff0c;如何快速入门&#xff0c;多长时间能成长为中高级工程师&#xff08;聊下来感觉大多数学生党就是焦虑&#xff0c;毕业即失业&#xff0c;尤其现在就业环境这么差&#xff09;&#xff0c;但聊到最后&#xff0c;很多…

07 Kubernetes 网络与服务管理

课件 Kubernetes Service是一个抽象层&#xff0c;用于定义一组Pod的访问方式和访问策略&#xff0c;其作用是将一组Pod封装成一个服务&#xff0c;提供一个稳定的虚拟IP地址和端口号&#xff0c;以便于其他应用程序或服务进行访问。 以下是Kubernetes Service YAML配置文件的…

FPGA时序约束(五)衍生时钟约束与I/O接口约束

系列文章目录 FPGA时序约束&#xff08;一&#xff09;基本概念入门及简单语法 FPGA时序约束&#xff08;二&#xff09;利用Quartus18对Altera进行时序约束 FPGA时序约束&#xff08;三&#xff09;时序约束基本路径的深入分析 FPGA时序约束&#xff08;四&#xff09;主时…

2023五一建模A题完整版本【原创首发】

已经完成五一数学建模全部内容&#xff0c;大家可以文末查看&#xff01;&#xff01;供参考使用&#xff01; 摘要 本文研究了喷气式无人机在执行空中物资投放和爆破任务过程中的数学建模问题。我们分析了无人机投放距离与飞行高度、飞行速度、空气阻力等因素之间的关系&…

【Mybatis源码分析】动态标签的底层原理,DynamicSqlSource源码分析

DynamicSqlSource 源码分析 一、DynamicSqlSource 源码分析&#x1f62f;DynamicContext源码分析&#x1f644;SqlNode源码分析&#xff08;动态SQL标签&#xff09;Mybatis 动态SQL标签举例、调试SqlNode源码分析MixedSqlNodeIfSqlNodeWhereSqlNode、SetSqlNode、TrimSqlNodeS…

区域医疗云his系统源码,具有可扩展、易共享、易协同的优势

云HIS系统采用SaaS软件应用服务模式&#xff0c;提供软件应用服务多租户机制&#xff0c;实现一中心部署多机构使用。相对传统HIS单机构应用模式&#xff0c;它可灵活应对区域医疗、医疗集团、医联体、连锁诊所、单体医院等应用场景&#xff0c;并提升区域内应用的标准化与规范…

安装配置goaccess实现可视化并实时监控nginx的访问日志

一、业务需求 我们安装了nginx后,需要对nginx的访问情况进行监控(希望能够实时查看到访问nginx的情况),如下图所示: 二、goaccess的安装配置步骤 2.1、准备内容 需要先安装配置nginx或OpenResty - 安装 Linux环境对Nginx开源版源码下载、编译、安装、开机自启https://b…

从0开始利用Jenkins构建Maven项目(微服务)并自动发布

0. 前言 本文旨在帮助读者梳理如何从0开始利用Jenkins构建Maven项目&#xff08;微服务&#xff09;的自动发布任务 本文目录如下&#xff1a; 如何完成自动部署 0. 前言1. 配置工具类地址1.1 JDK1.2 Git1.3 Maven 2. 安装Jenkins3. 安装额外的工具插件4. 配置必要参数4.1 配…

TiDB实战篇-索引设计

简介 实战索引设计 数据映射原理 索引 唯一索引 二级索引 索引实例 索引设计 索引创建&#xff08;建表的时候创建&#xff09; 建表完以后添加 联合索引&#xff08;最左原则&#xff0c;索引覆盖&#xff09; 使用例子 索引覆盖 表达式索引 表达式索引的使用 不可见…

【youcans 的 OpenCV 学习课】23. 人脸检测:Haar 级联检测器

专栏地址&#xff1a;『youcans 的图像处理学习课』 文章目录&#xff1a;『youcans 的图像处理学习课 - 总目录』 【youcans 的 OpenCV 学习课】23. 人脸检测&#xff1a;Haar 级联检测器 4. Haar 级联分类器5. Haar 人脸/人眼检测器5.1 OpenCV 中的级联分类器5.2 Haar 级联检…

OPNET Modeler 调试简介

在使用 OPNET Modeler 软件运行仿真时&#xff0c;经常会遇到错误&#xff0c;发现和定位错误所在的地方是解决错误的第一步&#xff0c;那么怎么定位错误呢&#xff0c;这个时候就需要采用仿真调试器 (OPNET Simulation Debugger&#xff0c;ODB)进行调试了。 在 OPNET 中&…

【模电实验】电路元件伏安特性的测绘及电源外特性的测量

实验2电路元件伏安特性的测绘及电源外特性的测量 实验目的 学习测量线性和非线性电阻元件伏安特性的方法&#xff0c;并绘制其特性曲线学习测量电源外特性的方法掌握运用伏安法判定电阻元件类型的方法学习使用直流电压表、电流表&#xff0c;掌握电压、电流的测量方法 实验原…

Java BIO(Blocking IO:同步并阻塞式IO)

1.基本介绍 1>.Java BIO就是传统的java io编程,其相关的类和接口在"java.io"包下; 2>.BIO(Blocking I/O): 同步阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理; 如果这个连接不做任何事情会造成(服务器)不必…

【C++】STL——list深度剖析 及 模拟实现

文章目录 前言1. list的介绍及使用1.1 list的介绍1.2 list的使用遍历插入删除数据Operations迭代器的功能分类list 的sort性能测试 2. list的模拟实现2.1 STL_list源码浏览2.2 基本结构实现2.3 思考&#xff1a;list迭代器是否可以用原生指针2.4 list迭代器的实现&#xff08;重…