c++修炼之路之特殊类设计与类型转换

news2025/1/11 4:23:23

目录

 一:特殊类设计

1.设计一个不能被拷贝的类

2.设计一个只能在堆上创建对象的类 

3.设计一个只能在栈上创建对象的类 

4.设计一个不能被继承的类 

5.设计一个只能创建一个对象的类(单例模式) 

二:c++的类型转换 

1.自定义类型,内置类型的各种转换

2.C++强制类型转换 

3.RTTI

接下来的日子会顺顺利利,万事胜意,生活明朗-----------林辞忧 

 一:特殊类设计

1.设计一个不能被拷贝的类

拷贝只会发生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,
只需让该类不能调用拷贝构造函数以及赋值运算符重载即可

在c++98中将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有

class CopyBan
{
	// ...

private:
	CopyBan(const CopyBan&);
	CopyBan& operator=(const CopyBan&);
	//...
};

原因:
1. 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不
能禁止拷贝了
2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写
反而还简单,而且如果定义了就不会防止成员函数内部拷贝了

在c++11后,在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数 

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

2.设计一个只能在堆上创建对象的类 

实现方式1:
1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建

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

	HeapOnly(const HeapOnly& hp) = delete;
	HeapOnly& operator=(const HeapOnly& hp) = delete;
private:
	HeapOnly()
	{}
};

int main()
{
	//三种创建对象的方式
	/*HeapOnly hp1;
	static HeapOnly hp2;
	HeapOnly* hp3 = new HeapOnly;*/

	HeapOnly* hp4 = HeapOnly::CreateObj();

	//但是此时防不住这样创建对象的,因此将拷贝构造和赋值给禁掉的
	//HeapOnly hp5(*hp4);
	delete hp4;

	return 0;
}

实现方式2:将类的析构函数私有

class HeapOnly
{
public:
	void Destroy()
	{
		delete this;
	}
private:
	~HeapOnly()
	{}
};

int main()
{
	//此时自定义类型是会自动调用构造和析构的,指针不会
	//HeapOnly hp1;
	//static HeapOnly hp2;
	HeapOnly* hp3 = new HeapOnly;

	//此时不能直接delete hp3
	hp3->Destroy();
	return 0;
}

3.设计一个只能在栈上创建对象的类 

方式1:不禁用拷贝构造

class StackOnly
{
public:
	static StackOnly CreateObj()
	{
		return StackOnly();
	}

	//直接禁用拷贝构造的话,上面的返回是要经过拷贝构造的
	//StackOnly(const StackOnly& s) = delete;
	void* operator new(size_t size) = delete;
	void operator delete(void* p) = delete;
private:
	StackOnly()
		:_a(0)
	{}
private:
	int _a;
};

int main()
{
	//static StackOnly s1;
	//StackOnly s2;
	//StackOnly* s3 = new StackOnly;

	StackOnly s4 = StackOnly::CreateObj();

	// 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉
	//StackOnly* s5 = new StackOnly(s4);

	//但还是防不住这种情况
	static StackOnly s6(s4);

	return 0;
}

方式2:禁用拷贝构造,提供移动构造

class StackOnly
{
public:
	static StackOnly CreateObj()
	{
		return StackOnly();
	}

	StackOnly(StackOnly&& s)
	{}

	StackOnly(const StackOnly& s) = delete;
private:
	StackOnly()
		:_a(0)
	{}
private:
	int _a;
};

int main()
{
	//static StackOnly s1;
	//StackOnly s2;
	//StackOnly* s3 = new StackOnly;

	StackOnly s4 = StackOnly::CreateObj();

	//此时拷贝构造的情况就都禁用了
	//StackOnly* s5 = new StackOnly(s4);
	//static StackOnly s6(s4);

	// 但是防不住下面的
	static StackOnly s6(move(s4));
	StackOnly* s5 = new StackOnly(move(s6));

	return 0;
}

4.设计一个不能被继承的类 

// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:
	static NonInherit GetInstance()
	{
		return NonInherit();
	}
private:
	NonInherit()
	{}
};


//C++11final关键字,final修饰类,表示该类不能被继承
class A  final
{
  // ....
};

5.设计一个只能创建一个对象的类(单例模式) 

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的
总结

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

单例模式实现模式1:饿汉模式

// 饿汉模式   在main函数之前创建对象
// 1、多个饿汉模式的单例,某个对象初始化内容较多(读文件),会导致程序启动慢
// 2、A和B两个饿汉,对象初始化存在依赖关系,要求A先初始化,B再初始化,饿汉无法保证
class InfoMgr
{
public:
	static InfoMgr& GetInstance()
	{
		return _ins;
	}

	void Print()
	{
		cout << _ip << endl;
		cout << _port << endl;
		cout << _buffSize << endl;
	}
private:
	InfoMgr(const InfoMgr&) = delete;
	InfoMgr& operator=(const InfoMgr&) = delete;

	InfoMgr()
	{
		cout << "InfoMgr()" << endl;
	}
private:
	string _ip = "127.0.0.1";
	int _port = 80;
	size_t _buffSize = 1024 * 1024;
	//...
	static InfoMgr _ins;
};

InfoMgr InfoMgr::_ins;

int main()
{
	InfoMgr::GetInstance().Print();
	//InfoMgr copy(InfoMgr::GetInstance());

	return 0;
}

单例模式实现模式2:懒汉模式(c++11之前)

// 懒汉模式
class InfoMgr
{
public:
	static InfoMgr& GetInstance()
	{
		// 第一次调用时创建单例对象
		// 线程安全的风险
		if (_pins == nullptr)
		{
			_pins = new InfoMgr;
		}

		return *_pins;
	}

	void Print()
	{
		cout << _ip << endl;
		cout << _port << endl;
		cout << _buffSize << endl;
	}

	static void DelInstance()
	{
		delete _pins;
		_pins = nullptr;
	}

private:
	InfoMgr(const InfoMgr&) = delete;
	InfoMgr& operator=(const InfoMgr&) = delete;

	InfoMgr()
	{
		cout << "InfoMgr()" << endl;
	}
private:
	string _ip = "127.0.0.1";
	int _port = 80;
	size_t _buffSize = 1024 * 1024;
	//...

	static InfoMgr* _pins;
};

InfoMgr* InfoMgr::_pins = nullptr;

int main()
{
	InfoMgr::GetInstance().Print();
	
	return 0;
}

单例模式实现模式2:懒汉模式(c++11之后)

class InfoMgr
{
public:
	static InfoMgr& GetInstance()
	{
		// 第一次调用时创建单例对象
		static InfoMgr ins;
		return ins;
	}

	void Print()
	{
		cout << _ip << endl;
		cout << _port << endl;
		cout << _buffSize << endl;
	}
private:
	InfoMgr(const InfoMgr&) = delete;
	InfoMgr& operator=(const InfoMgr&) = delete;

	InfoMgr()
	{
		cout << "InfoMgr()" << endl;
	}
private:
	string _ip = "127.0.0.1";
	int _port = 80;
	size_t _buffSize = 1024 * 1024;
	//...
};

int main()
{
	InfoMgr::GetInstance().Print();
	
	return 0;
}

二:c++的类型转换 

发生类型转换的,类型之间是有一定关联的

1.自定义类型,内置类型的各种转换

// a、内置类型之间
// 1、隐式类型转换    整形之间/整形和浮点数之间
// 2、显示类型的转换  指针和整形、指针之间

int main()
{
	int i = 1;
	// 隐式类型转换
	double d = i;
	printf("%d, %.2f\n", i, d);

	int* p = &i;
	// 显示的强制类型转换
	int address = (int)p;
	printf("%p, %d\n", p, address);

	return 0;
}
// b、内置类型和自定义类型之间
// 1、自定义类型 = 内置类型  ->构造函数支持
// 2、内置类型 = 自定义类型  ->使用operator+类型 支持
class A
{
public:
	//explicit A(int a)
	A(int a)
		:_a1(a)
		,_a2(a)
	{}

	A(int a1, int a2)
		:_a1(a1)
		, _a2(a2)
	{}

	// ()被仿函数占用了,不能用
	// operator 类型实现,无返回类型
 	//explicit operator int()
	operator int()
	{
		return _a1 + _a2;
	}
private:
	int _a1 = 1;
	int _a2 = 1;
};
int main()
{
	//单参数/多参数构造函数会发生隐式类型转换,产生具有常性的临时变量
	string s1 = "1111111";
	A aa1 = 1;
	//A aa1 = (A)1;
	A aa2 = { 2,2 };
	const A& aa3 = { 2,2 };

	int z = aa1.operator int();
	//int x = (int)aa1;
	int x = aa1;
	int y = aa2;
	cout << x << endl;
	cout << y << endl;

	std::shared_ptr<int> foo;
	std::shared_ptr<int> bar(new int(34));

	//if (foo.operator bool())
	if (foo)
		std::cout << "foo points to " << *foo << '\n';
	else 
		std::cout << "foo is null\n";

	if (bar)
		std::cout << "bar points to " << *bar << '\n';
	else
		std::cout << "bar is null\n";

	return 0;
}
// c、自定义类型和自定义类型之间 -- 对应的构造函数支持
class A
{
public:
	A(int a)
		:_a1(a)
		, _a2(a)
	{}

	A(int a1, int a2)
		:_a1(a1)
		, _a2(a2)
	{}

	int get() const
	{
		return _a1 + _a2;
	}
private:
	int _a1 = 1;
	int _a2 = 1;
};

class B
{
public:
	B(int b)
		:_b1(b)
	{}

	B(const A& aa)
		:_b1(aa.get())
	{}

private:
	int _b1 = 1;
};

int main()
{
	A aa1(1);
	B bb1(2);

	bb1 = aa1;
	B& ref1= bb1;
	const B& ref2 = aa1;

	return 0;
}

这里还有个在list中使用const迭代器遍历的场景 

 

2.C++强制类型转换 

标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:
static_cast、reinterpret_cast、const_cast、dynamic_cast

int main()
{
	// 对应隐式类型转换 -- 数据的意义没有改变
	double d = 12.34;
	int a = static_cast<int>(d);
	cout << a << endl;
	
	// 对应强制类型转换 -- 数据的意义已经发生改变
	int* p1 = reinterpret_cast<int*>(a);

	// 对应强制类型转换中有风险的去掉const属性
	volatile const int b = 2;
	int* p2 = const_cast<int*>(&b);
	*p2 = 3;

	cout << b << endl;
	cout << *p2 << endl;

	return 0;
}

class A
{
public:
	virtual void f() {}

	int _a = 1;
};

class B : public A
{
public:
	int _b = 2;
};

//void fun(A* pa)
//{
//	// dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回
//	// 指向父类转换时有风险的,后续访问存在越界访问的风险
//	// 指向子类转换时安全
//	B* pb1 = (B*)pa;
//	cout << "pb1:" << pb1 <<endl;
//	cout << pb1->_a << endl;
//	cout << pb1->_b << endl;
//	pb1->_a++;
//	pb1->_b++;
//	cout << pb1->_a << endl;
//	cout << pb1->_b << endl;
//}

void fun(A* pa)
{
	// dynamic_cast会先检查是否能转换成功(指向子类对象),能成功则转换,
	// (指向父类对象)不能则返回NULL
	B* pb1 = dynamic_cast<B*>(pa);
	if (pb1)
	{
		cout << "pb1:" << pb1 << endl;
		cout << pb1->_a << endl;
		cout << pb1->_b << endl;
		pb1->_a++;
		pb1->_b++;
		cout << pb1->_a << endl;
		cout << pb1->_b << endl;
	}
	else
	{
		cout << "转换失败" << endl;
	}
}

int main()
{
	A a;
	B b;
	fun(&a);
	fun(&b);

	return 0;
}

3.RTTI

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

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

相关文章

计算机的错误计算(八十五)

摘要 计算机的错误计算&#xff08;八十一&#xff09;至&#xff08;八十四&#xff09;介绍了双曲正弦、余弦、正割以及余割函数的计算精度问题。本节说明导致这些计算错误的主要原因。 首先&#xff0c;双曲正弦、余弦、正割以及余割函数主要包括 与 的计算&#xff0c;以…

逐行讲解Transformer的代码实现和原理讲解:nn.Linear线性层原理

视频详细讲解&#xff1a;LLM模型&#xff1a;代码讲解Transformer运行原理(1)_哔哩哔哩_bilibili 1 概述 经过Transformer的12个块处理完之后&#xff0c;4批文本数据得到了一个矩阵[4, 8, 16]&#xff0c;也就是每批数据都训练出了一个结果&#xff0c;在训练阶段&#xff…

Netty系列-3 ChannelFuture和ChannelPromise介绍

背景 Netty源码中大量使用了Future和Promise&#xff0c;学习ChannelFuture和ChannelFuture有助于理解Netty的设计思路。 本文的重点内容在于梳理清楚这些类的关系以及结合源码实现介绍这些类的作用&#xff0c;其中核心逻辑在于DefaultPromise和DefaultChannelPromise&#x…

GBase8sV8.8安装指南

目录 一、下载 Gbase 安装包二、安装预置条件1.确保安装包和平台适配2.安装依赖包&#xff1a;jdk(1.6版本以上)、unzip、libaio、libgcc、libstdc、ncurses、pam&#xff0c;如果缺失请提前安装 三、上传包并解压四、安装五、登录并创建数据库六、启动停止数据库七、常见问题八…

虚拟机ubuntu与主机共享文件夹

现在主机&#xff08;windows&#xff09;上新建一个共享文件夹 打开虚拟机 按下面操作打开共享文件夹 进入虚拟机的系统 cd /mnt/hgfs 如果报错 可以按下面的解决 挂载一下 sudo mount -t fuse.vmhgfs-fuse .host:/ /mnt/hgfs -o allow_other 如果显示不存在这个文…

session机制

场景&#xff1a;当众多用户访问网站&#xff0c;发出HTTP请求&#xff0c;那么网站是如何判断哪个HTTP请求对应的是哪个用户 &#xff1f; 作用&#xff1a;用于服务端区分用户。 当用户使用客户端登录时&#xff0c;服务端会进行验证&#xff0c;验证通过后会为这次登录创建…

剖析Cookie的工作原理及其安全风险

Cookie的工作原理主要涉及到HTTP协议中的状态管理。HTTP协议本身是无状态的&#xff0c;这意味着每次请求都是独立的&#xff0c;服务器不会保留之前的请求信息。为了在无状态的HTTP协议上实现有状态的会话&#xff0c;引入了Cookie机制。 1. Cookie定义 Cookie&#xff0c;也…

EMC测试

传导干扰测试&#xff1a; 现场实录CE传导骚扰电压测试&#xff0c;硬件环境&#xff1a; R&S EPL1000 EMI测量接收机&#xff08;支持时域测试&#xff09; R&S ENV216人工电源网络 R&S ELEKTRA 测试软件 黑色底板&#xff0c;不写丝印&#xff0c;0402封装平行排…

Tomcat服务详解

一、部署Tomcat服务器 JDK安装官方网址&#xff1a;https://www.oracle.com/cn/java Tomcat安装官方网址&#xff1a;Apache Tomcat - Welcome! 安装JDK 1.获取安装包 wget https://download.oracle.com/otn/java/jdk/8u411-b09/43d62d619be4e416215729597d70b8ac/jdk-8u41…

【工程测试技术】第13章 流体参量测量

目录 第13章 流体参量测量 13.1压力的测量 13.1.1 弹性式压力敏感元件 1. 波登管 2. 膜片和膜盒 3. 波纹管 13.1.2 常用压力传感器 1. 应变式压力传感器 2. 压阻式压力传感器 3. 压电式压力传感器 4. 电容式压力传感器 5. 谐振式压力传感器 6. 位移式压力传感器 (1)…

整型数组按个位值排序

题目描述 给定一个非空数组(列表)&#xff0c;其元素数据类型为整型&#xff0c;请按照数组元素十进制最低位从小到大进行排序&#xff0c;十进制最低位相同的元司 相对位置保持不变。 当数组元素为负值时&#xff0c;十进制最低位等同于去除符号位后对应十进制值最低位。 输…

吐血整理 ChatGPT 3.5/4.0 新手使用手册~ 【2024.09.04 更新】

以前我也是通过官网使用&#xff0c;但是经常被封号&#xff0c;就非常不方便&#xff0c;后来有朋友推荐国内工具&#xff0c;用了一阵之后&#xff0c;发现&#xff1a;稳定方便&#xff0c;用着也挺好的。 最新的 GPT-4o、4o mini&#xff0c;可搭配使用~ 1、 最新模型科普&…

VisualStudio环境搭建C++

Visual Studio环境搭建 说明 C程序编写中&#xff0c;经常需要链接头文件(.h/.hpp)和源文件(.c/.cpp)。这样的好处是&#xff1a;控制主文件的篇幅&#xff0c;让代码架构更加清晰。一般来说头文件里放的是类的申明&#xff0c;函数的申明&#xff0c;全局变量的定义等等。源…

Java面试题·解释题·框架部分

系列文章目录 Java面试题解释题总体概括 Java面试题解释题JavaSE部分 Java面试题解释题框架部分 文章目录 系列文章目录前言一、MyBatis1. 请你介绍MyBatis框架2. MyBatis框架的核心思想是什么&#xff1f;3. MyBatis的核心配置文件中常用的子标签有哪些&#xff1f;4. mapper…

饲料加工机器设备有哪些组成部分

在快速发展的畜牧业中&#xff0c;饲料加工作为支撑养殖业的重要环节&#xff0c;其效率与品质直接影响着养殖业的成本效益与动物健康。随着科技的进步&#xff0c;饲料加工机器设备也在不断升级&#xff0c;为养殖行业带来了变革。一、智能化粉碎机&#xff1a;细度可调&#…

Unity Adressables 使用说明(五)在运行时使用 Addressables(Use Addressables at Runtime)

一旦你将 Addressable assets 组织到 groups 并构建到 AssetBundles 中&#xff0c;就需要在运行时加载、实例化和释放它们。 Addressables 使用引用计数系统来确保 assets 只在需要时保留在内存中。 Addressables 初始化 Addressables 系统在运行时第一次加载 Addressable …

SimD:基于相似度距离的小目标检测标签分配

摘要 https://arxiv.org/pdf/2407.02394 由于物体尺寸有限且信息不足&#xff0c;小物体检测正成为计算机视觉领域最具挑战性的任务之一。标签分配策略是影响物体检测精度的关键因素。尽管已经存在一些针对小物体的有效标签分配策略&#xff0c;但大多数策略都集中在降低对边界…

怎么利用XML发送物流快递通知短信

现如今短信平台越来越普遍了&#xff0c;而短信通知也分很多种&#xff0c;例如服务通知、订单通知、交易短信通知、会议通知等。而短信平台在物流行业通知这一块作用也很大。在家时:我们平时快递到了&#xff0c;如果电话联系不到本人&#xff0c;就会放到代收点&#xff0c;然…

正负极层数更新器

文件名&#xff1a;dcs_tkinter.py import tkinter as tk from tkinter import messagebox import redis# 连接Redis r redis.Redis(hostlocalhost, port6379, db0)def update_redis_and_display():try:# 从输入框获取值positive_layers int(entry_positive.get())negative_…

2024国赛数学建模C题论文:基于优化模型的农作物的种植策略

大家可以查看一下35页&#xff0c;包含结构完整&#xff0c;数据完整的C题论文&#xff0c;完整论文见文末名片 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 添加图片注释&#xf…