特殊类设计和类型转换

news2024/11/16 7:48:19

前言

这一篇博客我们讲特殊类设计和类型转换

1. 特殊类设计

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

这个比较简单
第一种方法就是将赋值和拷贝构造只定义不声明然后设置为私有就可以了
第二种方法就是直接令它为delete

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

class HeapOnly
{
public:
	static HeapOnly* CreateObj()
	{
		return new HeapOnly;
	}
private:
	HeapOnly()
	{}
	HeapOnly(const HeapOnly&h)
	{}
};

只需要把构造函数和拷贝构造私有化就可以了,然后提供一个接口
注意要有一个static,不然的话无法调用

class HeapOnly
{
public:
	static HeapOnly* CreateObj()
	{
		return new HeapOnly;
	}

	void destroy()
	{
		delete this;
	}
private:
	~HeapOnly()
	{}
};

第二个方法就是将析构函数私有化,这样就不能正常在栈上创建对象了

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

class StackOnly
{
public:
	StackOnly CreateObj()
	{
		return StackOnly();
	}
private:
	StackOnly()
	{}
	StackOnly(const StackOnly&h)
	{}
};

因为new一个对象会调用构造嘛,所以把构造函数私有,就无法new了
然后还是要提供接口去调用

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

class NonInherit
{
public:
	static NonInherit GetInstance()
	{
		return NonInherit();
	}
private:
	NonInherit()
	{}
};

C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承

第二个方法就是将类用final修饰,表示这个类不能继承

class A  final
{
   // ....
};

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

单例模式就是这个类只有一个实例化对象,而且这个东西是可以全局访问的
单例模式又分为两种模式,一个是饿汉模式,一个是懒汉模式
饿汉模式就是在程序启动前就已经创建好这个对象了
懒汉模式就是你要用的时候才创建好这个对象
我们先讲饿汉模式

{
public:
	/*A() = delete;*///不能删除,因为如果删除的话,就真的一个对象都没有了
	A(const A& a) = delete;
	static A _a;
	int _num;
private:
	A()
	{};
};
A A::_a;//这里会调用默认构造函数

这样就可以了,构造函数私有化,拷贝构造delete,那么就只有那一个静态成员的
注意这里A中含有一个static类型的A,在这里不是套娃
static A _a;是存在静态区的,是创建的一个变量,是全局的,只不过在A中就有了类域的限制,它不是A的一个成员,而是创建的一个对象,然后static类型的,声明与定义分离
但这个是在main函数之前就定义好了,这个对象是在main函数之前就创建好了
缺点就是,在main函数之前创建,就会导致,程序启动缓慢
下面来讲懒汉模式

class A
{
public:
	/*A() = delete;*///不能删除,因为如果删除的话,就真的一个对象都没有了
	A(const A& a) = delete;
	
	static A& CreatObj()
	{
		if (_ptr == nullptr)
		{
			_ptr = new A;
		}
		return *_ptr;
	}
	int _num;
	static A* _ptr;
private:
	A()
	{};
};
A* A::_ptr = nullptr;//main函数之前就创建了一个nullptr
A::CreatObj()._num++;
A::CreatObj()._num++;
A::CreatObj()._num++;

在这里插入图片描述
这里可以不用释放,因为只有一个对象,内存泄漏了,也没事

但是如果要释放的话,写个析构是不行的
因为static A* a是不会自动调用析构函数的
要释放的话,就要在里面写个专门释放的函数
然后再专门调用

或者写个获取_ptr的函数也行

void Destroy()
{
	delete _ptr;
}

比如这样
或者在类里面定义一个内部类

class CGarbo {
public:
~CGarbo(){
if (Singleton::m_pInstance)
delete Singleton::m_pInstance;
}
};

然后在用这个定义一个静态的成员变量

static CGarbo Garbo;

static A a在生命周期结束的时候是会自动调用析构函数的
然后内部类可以用外部类的成员变量,就可以析构了

class A
{
public:
	/*A() = delete;*///不能删除,因为如果删除的话,就真的一个对象都没有了
	A(const A& a) = delete;
	
	static A& CreatObj()
	{
		if (_ptr == nullptr)
		{
			_ptr = new A;
		}
		return *_ptr;
	}

	void Destroy()
	{
		delete _ptr;
	}

	class B
	{
	public:
		~B()
		{
			cout << "h" << endl;
			delete _ptr;
		}
	};
	int _num=0;
	static A* _ptr;
	static B b;
	~A()
	{
		;
	}
private:
	A()
	{};
};
A* A::_ptr = nullptr;//main函数之前就创建了一个nullptr
A::B A::b;

因为定义了b的static,这不是成员变量,也是单独的,所以static A a是会自己析构的

class A
{
public:
	/*A() = delete;*///不能删除,因为如果删除的话,就真的一个对象都没有了
	A(const A& a) = delete;
	static A& CreatObj()
	{
		static A a;
		return a;
	}
	int _num = 0;
private:
	A()
	{};
};

这个是懒汉模式的第二种
只有调用CreatObj函数的时候才会创建那个对象,然后这个会自动析构,不是开辟的,不用释放

反正static的都不是成员变量,而是单独的一个对象

2. C语言中的类型转换

2.1内置类型之间

隐式类型转换:整型家族之间,整型与浮点数之间
显示类型转换(也是强制类型转换):指针与整数之间,指针与指针之间

int a = 'c';//隐式类型转换

在这里插入图片描述
这个就不行,因为要强制类型转换的东西,隐式类型转换是不行的

int a = 'c';//隐式类型转换
int b = (int) & a;

这里我们就可以看出了,第一,类型转换转换的是类型比较相近的,其实隐式类型转换最多就是一些精度的丢失,数据的主体意思还是不会改变,而强制类型转换,是直接就改变了数据的意义

2.2 内置类型和自定义类型之间

class A
{
public:
	A(int a)
		:_a1(a)
		,_a2(a)
	{}
	A(int a1,int a2)
		:_a1(a1)
		, _a2(a2)
	{}
private:
	int _a1;
	int _a2;
};
A tmp1 = 1;
A tmp2 = {1,2};
const A& tmp3 = {1,2};

这里我们可以看出,内置类型是可以直接隐式类型转换成自定义类型的,前提就是支持相关的构造函数

A tmp1 = (A)1;

当然能隐式类型转换,肯定也能强制类型转换,两个参数的我不知道怎么强制类型转换

explicit A(int a)
	:_a1(a)
	,_a2(a)
{}

在这里插入图片描述

如果在构造函数的前面加上explicit就表示这个相关的隐式类型转换就不能进行

A tmp1 = (A)1;

但是强制类型转换还是可以的

接下来我们讲自定义类型隐式类型转换为内置类型

operator int()
{
	return _a1 + _a2;
}
int num = tmp1;
cout << num << endl;

在这里插入图片描述
在这里我们就可以看出,涉及自定义类型的转换,都是要相关的函数的
自定义隐式变内置类型,就要重载,但是这个重载比较特殊,返回值不能写,就是默认的int,能隐式肯定也能显示,就不用说了

在这里插入图片描述

shared_ptr中也有个隐式类型转换,是转换为bool值的,看shared_ptr指向的内容是否有效

2.3 自定义类型和自定义类型之间

只要有自定义类型,那么类型转换的话,就要有相应函数,直接转换肯定是不行的
这次的函数是构造函数

class B
{
public:
	B(int a)
		:_b1(a)
		, _b2(a)
	{}
	B(int a1, int a2)
		:_b1(a1)
		, _b2(a2)
	{}
	int _b1;
	int _b2;
};

class A
{
public:
	A(const B& b)
	{
		_a1 = b._b1;
		_a2 = b._b2;
	}
	explicit A(int a)
		:_a1(a)
		, _a2(a)
	{}
	A(int a1, int a2)
		:_a1(a1)
		, _a2(a2)
	{}

	operator int()
	{
		return _a1 + _a2;
	}
private:
	int _a1;
	int _a2;
};
B b1 = { 1,2 };
A a1 = b1;

这里我们就可以看出,如果要将B类型转换为A类型,那么A类型就要支持B类型的构造函数

3. C++强制类型转换

C语言中的类型转换只有内置类型与内置类型,后面两种是C++的
虽然支持隐式类型转换,但是C++为了规范,为了让别人知道是什么类型的转换
直接引入了四种强制类型转换操作符

3.1 static_cast

static_cast对应的就是以前的隐式类型转换
以前是这样

double d = 0.22;
int a = d;

现在可以是这样

	double d = 0.22;
	int a = static_cast<int>(d);

<>里面的内容就是要转换的内容

3.2 reinterpret_cast

reinterpret_cast就是作用于强制类型转换的
以前是这样

int a = 0;
int b = (int)&a;

现在是这样

int a = 0;
int b = reinterpret_cast<int>( & a);

还是那句话,要求强制的就不能隐式,能隐式的绝对能强制
所以按理说static_cast可以用的,reinterpret_cast也可以用
但是static_cast可以用的,reinterpret_cast也不可以用
在这里插入图片描述
因为主要是为了规范,隐式就必须用static_cast,显示就必须用reinterpret_cast
在这里插入图片描述

3.3 const_cast

const_cast主要是对const类型的进行转换,然后还可以去掉它的const属性,意思是,要去掉const属性的时候,就要用这个,
在这里插入图片描述
在这里插入图片描述
可以看出const_cast针对的类型是指针,转换为指针,而且还要具有const属性的
所以const_cast是针对指针和引用类型,转换为指针和引用类型,然后这个指针和引用类型还具有const,用了这个就可以去掉cosnt属性
前面两个都是无法去掉const属性的
在这里插入图片描述
比如这个,&a是const int类型的,使用了这个转换符还是无法转换为int,意思就是无法去掉const属性

const int a = 0;
int* d = const_cast<int*>(&a);
*d = 10;
cout << a << endl;
cout <<(*d) << endl;

这个就可以去掉const属性
在这里插入图片描述
d这个指针就不具有const属性,就可以对指向内容进行修改了
虽然进行了修改,按理说a是已经改变了的,但是const类型的,编译器有时会把它放在寄存器,有时会把它直接用2宏替换,所以用的a一直是0,并且a的const属性不会改变
虽然d指针指向a,但是也可以当做没有指向a,重新弄了一个数据为0来指向

3.4 dynamic_cast

danamic_cast主要是针对子类和父类的相互赋值的
第一子类可以直接赋值给父类,指针引用都可以赋给父类
第二但是父类的指针引用不能给子类,因为子类指向的空间更大,会越界访问
主要是越界访问的问题
所以danamic_cast针对第一种情况成功转换
第二种情况就会返回nullptr或者返回0
但是使用这个的前提就是父类要具有虚函数

class A
{
public:
	virtual void f() {}
};
class B : public A
{};
void fun(A* pa)
{
	// dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回
	B* pb2 = dynamic_cast<B*>(pa);
	if (pb2)
	{
		cout << "转换成功" << endl;
		cout << "pb2:" << pb2 << endl;
	}
	else
	{
		cout << "转换失败" << endl;
	}
}
int main()
{
	A a;
	B b;
	fun(&a);
	fun(&b);
	return 0;
}

第一个pa指向的空间是A类型的,所以转换为访问B类型的,就会越界,很危险
第二个pa指向的空间是B类型的,只不过指向的一部分,但是转换为B类型的指针没有问题,因为可以访问多余的空间

4. RTTI

RTTI:Run-time Type identification的简称,即:运行时类型识别。
C++通过以下方式来支持RTTI:

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

就是可以判断类型的就是RTTI

总结

下一篇博客我们讲讲linux

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

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

相关文章

自学成才:通过自学成为软件开发者——之入行成为软件开发者

一些优秀的程序员&#xff0c;可能以前从事的是其他职业&#xff0c;他们大都发现工作中的很多固定化的流程内容&#xff0c;如果可以实现自动化&#xff0c;不仅效率能够得到提高和保证&#xff0c;提高自己的生成力&#xff0c;同时自己也会从中释放出来&#xff0c;有更多的…

Go使用MongoDB应用指南

Go使用MongoDB应用指南 MongoDB 是一种高性能、开源、文档型的 NoSQL 数据库&#xff0c;广泛应用于 Web 应用、大数据以及云计算领域。Go 语言则以其快速、开发效率高、代码可维护性强著称。本指南将详细介绍如何在 Go 语言中使用 MongoDB 进行数据库操作&#xff0c;包括连接…

鸿蒙HarmonyOS开发知识:命令行工具Command Line Tools

该命令行工具集合了HarmonyOS应用开发所用到的系列工具&#xff0c;包括代码检查codelinter、三方库的包管理ohpm、命令行解析hstack、编译构建hvigorw。 命令行工具获取 请前往下载中心获取并下载命令行工具Command Line Tools。 配置环境变量 Windows 将解压后command-l…

英语四六级有多重要你不知道

卷出天际 IT业内卷严重大家都知道 因此也就打击了很多想入行的新人 到底什么是核心竞争力 放在十年前 稍微会Spring, CRUD 就能达到入门的台阶 那也是培训机构最繁荣的一段时期 而今顶峰已经过去 IT业从含金量上 已经大不如前 在野蛮发展期 如果不太挑的话 大专也是…

云轴科技ZStack AIOS平台智塔亮相FDS金融领袖峰会

人工智能&#xff08;AI&#xff09;正以前所未有的速度渗透到金融系统&#xff0c;推动着金融服务的创新和变革。这种深度融合不仅可以提高金融服务的效率和准确性&#xff0c;未来还可催生全新的金融产品和服务模式。尤其是生成式人工智能&#xff08;GenAI&#xff09;的出现…

算法的学习笔记—复杂链表的复制(牛客JZ35)

&#x1f600;前言 在许多实际应用中&#xff0c;我们会遇到复杂链表的复制问题。复杂链表不同于一般的单链表&#xff0c;不仅每个节点有指向下一个节点的指针&#xff0c;还有一个特殊的指针 random&#xff0c;可以指向链表中的任意节点或 null。如何高效地复制这样一个复杂…

CACTER直播预告:聚焦EDLP邮件数据防泄露实战重点

在信息高速流通的今天&#xff0c;邮件作为商务沟通的桥梁&#xff0c;不仅承载着日常沟通&#xff0c;更是企业机密和知识产权的重要载体。然而&#xff0c;邮件系统的开放性也使其成为网络攻击的主要目标。数据泄露不仅会导致商业损失&#xff0c;还可能对企业声誉造成不可逆…

【请安全下载】黑神话:悟空 单机游戏 它是如何保证安全的 怎样防破解的?安全措施:D加密,反外挂,代码加密,资源保护

单机 《黑神话&#xff1a;悟空》是一款单机游戏&#xff0c;由游戏科学开发&#xff0c;并于2024年8月20日全球同步上线。游戏以其独特的暗黑国风、深度的故事背景以及精致的游戏画面&#xff0c;重塑了西游题材&#xff0c;为玩家呈现了一个前所未有的悟空传奇。 黑神话&…

[Linux]在Ubuntu中安装samba并且正确配置(详细)

一、我们为什么需要samba服务 samba是一种实现windows和linux包括macos文件共享的套件。它能让我们像访问自己的磁盘一样去访问别的系统的文件。可以看得出来这种一种快速并且高效的文件传输协议。看到这里&#xff0c;大家可能会有些疑问。向linux传输文件&#xff0c;我们可以…

常用网络测试工具以及解决tcp协议带来得问题

一、解决粘包问题 1.1、tcp的特点 面向字节流特点&#xff0c;会造成可能数据与数据发送到一块&#xff0c;成为粘包&#xff0c;数据之间不区分 1.2、拆包 因为缓冲区的大小&#xff0c;一次性发送的数据会进行拆分&#xff08;大小不符合的时候&#xff09; 就和水一样一…

vue3使用i18n实现国际化

安装vue-i18n npm install vue-i18n创建一个ts文件用于存储各种翻译 globalLang.ts的内容如下&#xff1a; export default {"cn": {},"en": {},"de": {},"es": {},"fr": {},"id": {},"it": {},&quo…

HDMI画面发白

这个问题困扰我很久了&#xff0c;今天在抖音上看到了解决方案! https://v.douyin.com/Ceie2g2s/ 量化范围&#xff1a;有限范围改成全范围。

Tomcat安装部署

简介 Tomcat 是由 Apache 开发的一个 Servlet 容器&#xff0c;实现了对 Servlet 和 JSP 的支持&#xff0c;并提供了作为Web服务器的一些特有功能&#xff0c;如Tomcat管理和控制平台、安全域管理和Tomcat阀等。 简单来说&#xff0c;Tomcat是一个WEB应用程序的托管平台&…

关于elementui table组件 —— 竖向表格

前端模拟数据方式&#xff1a; html代码&#x1f447;&#xff1a; <template><el-table :data"tableData" style"width: 60%;margin-top:20px" stripe :show-header"false" border :row-style"rowStyle"><el-table…

培训第三十五天(容器的基础命令使用)

1、创建一个容器并同时执行echo命令 # 快速启动一个容器执行特定的一次性命令并查看输出结果&#xff0c;输出结果后容器直接退出[rootdocker ~]# docker run -it --namea0 centos:latest echo "abc"abc[rootdocker ~]# docker psCONTAINER ID IMAGE COMMAND …

FreeRTOS 快速入门(六)之互斥量

目录 一、互斥量1、基本概念2、运作机制3、死锁现象4、递归互斥量 二、优先级反转和优先级继承问题1、优先级反转问题2、优先级继承问题 三、互斥量函数1、互斥量1、创建 2、获取互斥量3、释放互斥量4、删除互斥量 一、互斥量 1、基本概念 互斥量又称互斥信号量&#xff08;本…

Vue.js学习笔记(七)使用sortablejs或el-table-draggable拖拽ElementUI的el-table表格组件

文章目录 前言一、el-table-draggable是什么&#xff1f;二、使用步骤1.安装使用2.sortablejs 总结 前言 记录 el-table-draggable 插件使用方法。 一、el-table-draggable是什么&#xff1f; el-table-draggable的存在就是为了让vue-draggable支持element-ui中的el-table组件…

Lesson 81+82 Roast beef and potatoes

Lesson 8182 Roast beef and potatoes 词汇 bath n. 洗澡&#xff0c;浴缸 搭配&#xff1a;have a bath 泡澡 相关&#xff1a;take a shower 淋浴&#xff0c;冲个澡    shower&#xff1a;花洒&#xff0c;喷头 例句&#xff1a;Bobby总是在傍晚洗澡。    Bobby alw…

基于预训练模型,进行氨基酸序列编码,用于深度学习模型构建

本团队提供生物医学领域专业的AI&#xff08;机器学习、深度学习&#xff09;技术支持服务。如果您有需求&#xff0c;请扫描文末二维码关注我们。 在对氨基酸序列数据进行深度学习模型构建时&#xff0c;首先需要将字符形式的序列数据进行编码操作。最简单的当然是One-hot编码…

【Java】/* 双向链式队列 和 循环队列 - 底层实现 */

一、链式队列 1. 使用双向链表实现队列&#xff0c;可以采用尾入&#xff0c;头出 也可以采用 头入、尾出 (LinkedList采用尾入、头出) 2. 下面代码实现的是尾入、头出&#xff1a; package bageight;/*** Created with IntelliJ IDEA.* Description:* User: tangyuxiu* Date: …