当前位置: 首页 > news >正文

C++ 命名类型转换

目录

    • 传统艺能😎
    • 类型转换🤔
    • 四大类型转换🤔
      • static_cast😋
      • reinterpret_cast😋
      • const_cast😋
      • dynamic_cast😋
    • explicit🤔
    • RTTI🤔

传统艺能😎

小编是双非本科大二菜鸟不赘述,欢迎米娜桑来指点江山哦
在这里插入图片描述
1319365055

🎉🎉非科班转码社区诚邀您入驻🎉🎉
小伙伴们,满怀希望,所向披靡,打码一路向北
一个人的单打独斗不如一群人的砥砺前行
这是和梦想合伙人组建的社区,诚邀各位有志之士的加入!!
社区用户好文均加精(“标兵”文章字数2000+加精,“达人”文章字数1500+加精)
直达: 社区链接点我


在这里插入图片描述

类型转换🤔

任何一门语言里面都会遇到赋值符号两边不同的情况,或者形参和实参不匹配,这种情况就存在类型转换,特别是 C++ 这种强类型语言,即使不发生显示的强制转换也会进行隐式类型转换

其实在C语言阶段就早有提出两种类型转换方式:

  1. 显示类型转换:需要手动转换成需要的类型对象:(指定类型)变量
  2. 隐式类型转换:在代码编译阶段自动执行,能转就转,不能转就编译失败

当然并不是显示转换就可以随心所欲的转换, 只有相近类型之间才能发生隐式类型转换 \color{red} {只有相近类型之间才能发生隐式类型转换} 只有相近类型之间才能发生隐式类型转换,比如 int 和 double 表示的都是数值,不过它们的范围和精度不同。而指针类型表示的是地址编号,因此整型和指针类型之间不会进行隐式类型转换,如果需要转换则只能进行显式类型转换:

int main()
{

	//显式类型转换
	int* p = &i;
	int address = (int)p;
	cout << p << endl;
	cout << address << endl;
	return 0;
	
	//隐式类型转换
	int i = 1;
	double d = i;
	cout << i << endl;
	cout << d << endl;
}

四大类型转换🤔

介于类型转换的场合非常多,且存在一些复杂场景的类型转换,单纯的 C 语言的类型转换虽然简单,但是存在精度丢失、可视性差等缺陷,于是 C++ 标准推出了四大命名类型转换操作符:

static_cast
reinterpret_cast
const_cast
dynamic_cast

static_cast😋

static_cast 用于相近类型之间的转换,编译器隐式执行的任何类型转换都可以用 static_cast,但它不能用于不相关类型之间的转换:

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

	int* p = &a;
	// int address = static_cast<int>(p); //error
	return 0;
}

reinterpret_cast😋

reinterpret_cast 用于两个不相关类型之间的转换:

int main()
{
	int a = 10;
	int* p = &a;
	int address = reinterpret_cast<int>(p);
	cout << address << endl;
	return 0;
}

他除了用于两个不相关类型的转换,还有一个强有力的功能:可以将带参带返回值的函数指针转换成了无参无返回值的函数指针,并且此时转换后函数指针还可以调用这个函数:

typedef void(*FUNC)();
int DoSomething(int i)
{
	cout << "DoSomething: " << i << endl;
	return 0;
}
int main()
{
	FUNC f = reinterpret_cast<FUNC>(DoSomething);
	f();
	return 0;
}

用转换后的函数指针调用该函数时没有传入参数,因此这里打印出参数 i 的值是一个随机值

const_cast😋

const_cast 用于删除变量的 const 属性,转换后就可以对 const 变量的值进行修改:

int main()
{
	const int a = 2;
	int* p = const_cast<int*>(&a);
	*p = 3;
	cout << a << endl;  //2
	cout << *p << endl; //3
	return 0;
}

代码中用 const_cast 删除了变量 a 地址的 const 属性,这时就可以通过这个指针来修改变量 a 的值

由于编译器认为 const 修饰的变量是不会被修改的,因此会将 const 修饰的变量存放到寄存器当中,当需要读取 const 变量时就会直接从寄存器中进行读取, 而我们修改的实际上是内存中的 a 的值,因此最终打印出 a 的值是未修改之前的值 \color{red} {而我们修改的实际上是内存中的 a 的值,因此最终打印出a的值是未修改之前的值} 而我们修改的实际上是内存中的a的值,因此最终打印出a的值是未修改之前的值

如果不想让 const 变量被优化到寄存器当中,可以用 volatile 关键字对 const 变量进行修饰,这时当要读取这个 const 变量时编译器就会从内存中进行读取,即保持了该变量在内存中的可见性

dynamic_cast😋

dynamic_cast 用于将父类的指针(或引用)转换成子类的指针(或引用),这就不得不提一下向上转型和向下转型了

其实早在继承和多态的博客中说过,向上转型是子类的指针(或引用)→ 父类的指针(或引用);向下转型是 父类的指针(或引用)→ 子类的指针(或引用)。向上转型就是所说的切割/切片,是语法天然支持的,不需要进行转换,而向下转型是语法不支持的,需要进行强制类型转换

  1. 其中向下转型是存在安全问题的:如果父类指向的是一个父类对象,那么将其转换为子类是不安全的,因为转换后可能会访问到子类的资源,而这个资源是父类对象所没有的
  2. 如果父类的指针(或引用)指向的是一个子类对象,那么将其转换为子类的指针(或引用)则是安全的

若果要用 C 语言的强制类型转换进行向下转型也是不安全的,因为无论父指向的是父类对象还是子类对象都会进行转换。而使用 dynamic_cast 进行向下转型则是安全的,但如果父类指向的是父类对象那么 dynamic_cast 会转换失败并返回空指针!

class A
{
public:
	virtual void f()
	{}
};
class B : public A
{};
void func(A* pa)
{
	B* pb1 = (B*)pa;               //不安全
	B* pb2 = dynamic_cast<B*>(pa); //安全

	cout << "pb1: " << pb1 << endl;
	cout << "pb2: " << pb2 << endl;
}
int main()
{
	A a;
	B b;
	func(&a);
	func(&b);
	return 0;
}

dynamic_cast 只能用于含有虚函数的类,因为运行时类型检查需要运行时的类型信息,而这个信息存储在虚函数表中,只有定义了虚函数的类才有虚函数表!

explicit🤔

explicit 用来修饰构造函数,从而禁止单参数构造函数的隐式转换:

class A
{
public:
	explicit A(int a)
	{
		cout << "A(int a)" << endl;
	}
	A(const A& a)
	{
		cout << "A(const A& a)" << endl;
	}
private:
	int _a;
};
int main()
{
	A a1(1);
	//A a2 = 1; //error
	return 0;
}

在语法上,代码中的A a2 = 1等价于以下两句代码:

A tmp(1);  //先构造
A a2(tmp); //再拷贝构造

可见和上述这种用临时变量拷贝构造的方法不同,现在的编译器做了优化,当遇到 A a2 = 1这句代码时,会直接按照 A a2(1) 的方式进行处理,这也叫做隐式类型转换

对于单参数的自定义类型来说,A a2 = 1 这种代码可读性不是很好,因此可以用 explicit 修饰单参数构造函数,从而禁止单参数构造函数的隐式转换

RTTI🤔

RTTI 就是运行时类型识别,C++ 通过以下几种方式来支持 RTTI:

  1. typeid:在运行时识别出一个对象的类型。
  2. dynamic_cast:在运行时识别出一个父类的指针(或引用)指向的是父类对象还是子类对象。
  3. decltype:在运行时推演出一个表达式或函数返回值的类型

相关文章:

  • 【定制项目】【M15 消防安全宣传】【横屏版】主要模块:视频 + 音频 + 图标 + 问答游戏
  • 在 Linux 中使用 tcp 转储命令来分析网络
  • 结合viewBinding实现RecyclerView组件的滚动列表显示
  • 【C++】STL——stack和queue(万字详解)
  • Kunyu安装使用教程(linux)
  • 34岁本科男,做了5年功能测试想转行,除了进厂还能干什么?
  • 数据库--mysql(SQL语句)
  • 论文分享 | SpeechFormer: 利用语音信号的层次化特性提升Transformer在认知性语音信号处理领域中的性能
  • JavaEE之CSSⅠ(前端)
  • [ 英语 ] 马斯克抱水槽“入主”推特总部中那句 Let that sink in 到底是什么梗?
  • 中国海底电缆行业发展前景及投资风险预测分析报告
  • 计算机网络【UDP与TCP协议(三次握手、四次挥手)】
  • 爱奇艺开源的高性能网络安全监控引擎
  • JAVA房产交易系统计算机毕业设计Mybatis+系统+数据库+调试部署
  • 【MySQL数据库和JDBC编程】第三章-第二节:MySQL的增删查改进阶篇
  • 深入了解以太坊
  • [LeetCode] Wiggle Sort
  • android高仿小视频、应用锁、3种存储库、QQ小红点动画、仿支付宝图表等源码...
  • C++入门教程(10):for 语句
  • co.js - 让异步代码同步化
  • iOS 颜色设置看我就够了
  • javascript从右向左截取指定位数字符的3种方法
  • node 版本过低
  • session共享问题解决方案
  • SQLServer之创建数据库快照
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • unity如何实现一个固定宽度的orthagraphic相机
  • win10下安装mysql5.7
  • 复杂数据处理
  • 蓝海存储开关机注意事项总结
  • 每天10道Java面试题,跟我走,offer有!
  • 每天一个设计模式之命令模式
  • 新版博客前端前瞻
  • 在Mac OS X上安装 Ruby运行环境
  • 2017年360最后一道编程题
  • ionic异常记录
  • MyCAT水平分库
  • Prometheus VS InfluxDB
  • ​html.parser --- 简单的 HTML 和 XHTML 解析器​
  • ​人工智能之父图灵诞辰纪念日,一起来看最受读者欢迎的AI技术好书
  • #ubuntu# #git# repository git config --global --add safe.directory
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • $$$$GB2312-80区位编码表$$$$
  • (动态规划)5. 最长回文子串 java解决
  • (独孤九剑)--文件系统
  • (附源码)springboot宠物医疗服务网站 毕业设计688413
  • (规划)24届春招和25届暑假实习路线准备规划
  • (剑指Offer)面试题34:丑数
  • (免费领源码)Java#ssm#MySQL 创意商城03663-计算机毕业设计项目选题推荐
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • .【机器学习】隐马尔可夫模型(Hidden Markov Model,HMM)
  • .NET简谈互操作(五:基础知识之Dynamic平台调用)
  • .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验
  • @31省区市高考时间表来了,祝考试成功
  • [ 2222 ]http://e.eqxiu.com/s/wJMf15Ku