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

C++ -------- 类型转换

目录

1.C语言中的类型转换 

2.为什么C++需要四种类型转换

3.C++强制类型转换

(1)static_cast

(2)reinterpret_cast

(3)const_cast

(4)dynamic_cast

4.explicit

 5.RTTI

6.常见测试题


         

1.C语言中的类型转换 

在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换和显式类型转换。

                 

(1) 两种形式的类型转换

  • 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败 
  • 显式类型转化:需要用户自己处理
  • 类型转换应为相近类型  : int, short, char之间就可以互相转换,因为他们都是用来表示数据的大小,只是范围不一样;char因为编码的原因用来表示字符,严格来说如果是有符号的话,它是从-128 - 127这个范围的整数
void Test()
{
	int i = 1;

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

	// 显示的强制类型转换
	int* p = &i;
	int address = (int)p;

	printf("%x, %d\n", p, address);
}

                         

(2)C语言类型转换的缺陷 : 转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换

        

        

        

2.为什么C++需要四种类型转换

(1)C风格的转换格式很简单,但是有不少缺点的:

  • ①隐式类型转化有些情况下可能会出问题:比如数据精度丢失
  • ② 显式类型转换将所有情况混合在一起,代码不够清晰
(2)因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格

                

                

3.C++强制类型转换

(1)static_cast

  • static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换
int main()
{
	double d = 12.34;
	int a = static_cast<int>(d); //相近类型转换
	cout << a << endl;

	int *p = static_cast<int*>(a);  //不相近类型转换  -- 报错

	return 0;
}

                 

(2)reinterpret_cast

  • reinterpret_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可以让编译器以FUNC的定义方式去看待DoSomething函数
  • 所以非常的BUG,下面转换函数指针的代码是不可移植的,所以不建议这样用
  •  C++不保证所有的函数指针都被一样的使用,所以这样用有时会产生不确定的结果
typedef void(*FUNC)();
int DoSomething(int i)
{
	cout << "DoSomething : " << i << endl;
	return 0;
}

void Test()
{
	FUNC f = reinterpret_cast<FUNC>(DoSomething);
	f();
}

int main()
{
	Test();
	return 0;
}

                

(3)const_cast

  • const_cast最常用的用途就是删除变量的const属性,方便赋值
  • C+ +把这个单独分出来,意思提醒用的人注意. const属性被去掉以后,会被修改。小心跟编译器优化冲突误判
     

①const_cast使用测试

void Test1()
{
	//const修饰的叫常变量
	const int a = 2;
	//a = 10;  //a不可修改

	//const int* p = &a; //还是不能改变
	// *p = 10;

	 int* p = const_cast<int*>(&a);  //const_cast用于去掉const属性
	 *p = 10; //可修改

	cout << a << endl;  //2 从寄存器中取
	cout << *p << endl; //10  从内存中取
}

                 

②对于结果不同的解释

编译器看到a是const在语法层面上认为 a是无法被改变的,(每次访问a的时候就不需要去内存里取了)于是将a的值存在寄存器,最终导致a的值在内存中被改了,但是你取值的时候是在寄存器,造成了预期与结果不一致volatile关键字能防止编译器优化。

                 

③关于常量区补充 : 

  • 常量区的那一段是在硬件上面保护的,只要有写指令它的编译不会报错,因为它看不出来你的指令是啥,运行的时候如果你要常量区进行写入就会报错。(程序运行起来时进程,通过页表将虚拟地址空间和物理地址进行映射,进程知道那一块是常量区)
  • 常量区为什么禁止写入 : 常量区存储的有编译好的指令,随便修改是会导致很多问题的

                 

(4)dynamic_cast

①dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)

  • 向上转型:子类对象指针/引用 -> 父类指针/引用(不需要转换,赋值兼容规则)
  • 向下转型:父类对象指针/引用 -> 子类指针/引用(用dynamic_cast转型是安全的)
②注意:
  • dynamic_cast只能用于含有虚函数的类 ,因为运行时类型检查需要运行时的类型信息,而这个信息是存储在虚函数表中的,只有定义了虚函数的类才有虚函数表。
  • dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0

③测试用例

  • pa有两种情况 : pa指向父类对象,pa指向子类对象
  • 如果pa是指向父类对象,那么不做任何处理
  • 如果pa是指向子类对象,那么请转回子类,并访问子类对象中_b成员
  • dynamic_cast : 如果pa指向的父类对象,那么则转换不成功,返回nullptr;  如果pa指向的子类对象,那么则转换成功,返回对象指针
  • dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回
class A
{
	virtual void f() {}
public:
};

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

void func(A* pa)
{
	B* pb1 = static_cast<B*>(pa);
	B* pb2 = dynamic_cast<B*>(pa);
	if (pb2 == nullptr)
	{
		cout << "转换失败!" << endl;
	}
	else
	{
		pb2->_b++;
	}

	cout << "pb1:" << pb1 << endl;
	cout << "pb2:" << pb2 << endl;
}

int main()
{
	A aa;
	B bb;
	func(&aa);
	func(&bb);
}

                

                 

4.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这句代码时,会先构造一个临时对象,再用这个临时对象拷贝构造a2。但是现在的编译器已经做了优化,当遇到A a2 = 1这句代码时,会直接按照A a2(1)的方式进行处理,这也叫做隐式类型转换。

                 

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

                

                 

 5.RTTI

①RTTI(Run-Time Type Identification)就是运行时类型识别。

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

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

                         

6.常见测试题

①C++中的4种类型转换分别是:____ 、____ 、____ 、____。

  • 分别是static_cast、reinterpret_cast、const_cast和dynamic_cast。

 

②4种类型转换的应用场景。

  • static_cast用于相近类型的类型之间的转换,编译器隐式执行的任何类型转换都可用static_cast。
  • reinterpret_cast用于两个不相关类型之间的转换。
  • const_cast用于删除变量的const属性,方便赋值。
  • dynamic_cast用于安全的将父类的指针(或引用)转换成子类的指针(或引用)。
     

                

相关文章:

  • 【youcans 的图像处理学习课】11. 形态学图像处理(中)
  • 大二数据库实验-MySQL语句(Employee、Department、Salary)
  • 实验3 数据文件操作和异常处理
  • 【微信小程序】button和image组件的基本使用
  • 【吴恩达深度学习】——NLP和Word Embedding
  • 图解mysql(五)——日志篇
  • 图解mysql(六)——内存篇
  • 【数据库原理 | MySQL】 前世今生(入坑篇)
  • Python脚本在win10下开机自启动
  • docker安装rocketmq
  • 【C++】基础入门(三):引用超全整理
  • 【java_wxid项目】【第十四章】【Spring Cloud Stream集成】
  • python-pyecharts基础知识
  • 分类:概率生成模型 - 李宏毅机器学习笔记
  • Tkinter教程(每天半小时,3天彻底掌握Tkinter)day1
  • 【跃迁之路】【699天】程序员高效学习方法论探索系列(实验阶段456-2019.1.19)...
  • android高仿小视频、应用锁、3种存储库、QQ小红点动画、仿支付宝图表等源码...
  • Apache的80端口被占用以及访问时报错403
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • Git学习与使用心得(1)—— 初始化
  • go append函数以及写入
  • JAVA之继承和多态
  • magento2项目上线注意事项
  • 分享一份非常强势的Android面试题
  • 如何抓住下一波零售风口?看RPA玩转零售自动化
  • 提升用户体验的利器——使用Vue-Occupy实现占位效果
  • 正则与JS中的正则
  • ​​​​​​​​​​​​​​Γ函数
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • $.ajax,axios,fetch三种ajax请求的区别
  • (02)Cartographer源码无死角解析-(03) 新数据运行与地图保存、加载地图启动仅定位模式
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (三)elasticsearch 源码之启动流程分析
  • (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境
  • (原創) 如何將struct塞進vector? (C/C++) (STL)
  • (转)Scala的“=”符号简介
  • (转)大型网站架构演变和知识体系
  • .h头文件 .lib动态链接库文件 .dll 动态链接库
  • .net core webapi 部署iis_一键部署VS插件:让.NET开发者更幸福
  • .NET Framework 的 bug?try-catch-when 中如果 when 语句抛出异常,程序将彻底崩溃
  • .NET MVC之AOP
  • .Net Winform开发笔记(一)
  • .NET 事件模型教程(二)
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件
  • .net开源工作流引擎ccflow表单数据返回值Pop分组模式和表格模式对比
  • .NET开源项目介绍及资源推荐:数据持久层
  • .Net下使用 Geb.Video.FFMPEG 操作视频文件
  • .net专家(高海东的专栏)
  • [ vulhub漏洞复现篇 ] struts2远程代码执行漏洞 S2-005 (CVE-2010-1870)
  • [20171101]rman to destination.txt
  • [ASP.NET MVC]如何定制Numeric属性/字段验证消息
  • [bzoj1912]异象石(set)
  • [C#]扩展方法
  • [CSS]中子元素在父元素中居中
  • [Eclipse] 详细设置护眼背景色和字体颜色并导出