目录
一、C语言的类型转换
二、C++四种类型转换
①static_cast
②reinterpret_cast
③const_cast
④dynamic_cast
RTTI(了解内容)
一、C语言的类型转换
C语言中有隐式类型转换和显式的强制类型转换,如下所示:
隐式类型转换是意义相近类型之间的转换
显示的强制类型转换则是意义不相近类型之间的转换(值转换后有意义)
但是并不是所有的意义不相近类型都能转换的,例如结构体类型怎么转换也不能够转换为一个int类型的数
C语言中的显示类型转换还好说,隐式类型转换有些情况就会出现问题:
如上所示,我们有两个size_t类型的变量分别是a、b,当进入while循环时,a从5不断的--,直到减到0,按理说下一次a--到-1时就不满足条件了,但是运行结果却是一直死循环
原因就是size_t是无符号整数,a减到-1并不是负数,而是整数的最大值,所以会持续死循环
那这时,如果将a改为int类型,a--到-1是不是就能够解决这个问题呢,也并不是我们所想的那样:
程序依然死循环,这就是C语言中的隐式类型转换可能带来的问题
原因是:a变为了int类型,但是在while循环的判断条件中,int类型的a在与size_t类型的b比较时,会自动将int类型的a隐式提升为size_t类型,依然是第一次出现的问题
所以上述情况是C语言隐式类型转换的缺陷,转换的可视性较差,类型转换的情况不够清晰
也会有精度丢失的缺点
二、C++四种类型转换
由于C语言的类型转换由上面的一些缺陷,所以C++也提出了自己的类型转换,当然C++也还是会兼容C的类型转换(不会舍弃旧的语法)
C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:
static_cast、reinterpret_cast、const_cast、dynamic_cast
①static_cast
编译器隐式执行的任何类型转换都可用 static_cast(意义相近类型),但它不能用于两个不相关的类型进行转换(隐式类型转换)
用法如下:
static_cast后面的<>里放的就是转换后的类型
②reinterpret_cast
reinterpret_cast用于将一种类型转换为另一种不同的类型,即意义不相近类型(强制类型转换)
用法如下:
③const_cast
const_cast最常用的用途就是删除变量的const属性,方便赋值(强制类型转换)
如上图即删除了变量a的const属性,并将它的地址给了ptr指针,从而可以赋值
但是我们可以观察到,a的值和*ptr的值却是不同的,为什么const_cast将a的const属性删除了,且将a的地址赋值给了ptr,接着改变*ptr的值,a的值却没有变化呢?
原因是编译器对于const类型的一个优化,编译器认为const类型的a不会被改,所以就不用每次访问时都从内存中去读取a的数值,于是直接将a加载到一个寄存器中,所以虽然a在内存中被改了,但是编译器并不知道,编译器依然是从寄存器中读取的,而*ptr是从内存中取的,所以*ptr的值改变了,因此打印出来的a和*ptr的值是不同的
那么如何解决这个问题呢,有个关键字volatile就是专门解决这里的优化问题的
在const int a前面加上关键字volatile,就是在告诉编译器,不要优化a,每次访问时都去内存中取,优化后如下:
成功解决问题
④dynamic_cast
dynamic_cast是用于将一个父类对象的指针/引用转换为子类对象的指针/引用(动态转换)
向上转换:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)
向下转换:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)
例如父类A类,子类是B类,则子类对象是可以转换成父类对象的,赋值兼容,可以切片处理,而父类对象是不能转换成子类对象的:
在test函数中:参数A*类型的指针pa有可能指向父类A,也有可能指向子类B:
如果是test(&pb),传入的pb原本就是指向子类的指针,此时将test函数中的父类指针pa强制转换后赋值给pb,pb是可以指向子类的成员_b并使用的
但是如果是test(&pa),传入的pa原本就是指向父类的指针,即使在test函数中将父类指针强制转换后赋值给pb,由于本身就是父类指针,无法使用子类的_b成员,若使用子类成员_b是会发生越界的
所以这时C++引入了dynamic_cast,使用dynamic_cast是安全的
如果pa是指向子类,那么可以转换,转换表达式返回正确的地址
如果pa是指向父类,那么不能转换,转换表达式返回nullptr
这时使用方法如下:
main函数中先传入父类指针pa,进入test函数后使用dynamic_cast进行转换,因为指向父类,所以返回值为nullptr,因此进入else语句,打印转换失败
第二个传入子类指针pb,因为指向子类,所以返回正确的地址,因此进入if语句,打印转换成功
之所以说使用dynamic_cast是安全的,是因为如果不使用dynamic_cast,而是直接执行语句B* pb = (B*)pa的话,那么不论是传入父类还是子类指针都会进入if语句,然后就会发生越界错误,所以是不安全的
需要注意的是:
dynamic_cast只能用于父类含有虚函数的类(可以注意到上面所举例子的父类A里有虚函数f(),若没有虚函数则会语法报错)
RTTI(了解内容)
RTTI就是Run-time Type identification,即运行时类型识别。
C++通过以下方式来支持RTTI:
①、 typeid运算符:获取对象类型字符串
②、dynamic_cast运算符:可以识别父类的指针指向父类对象,还是子类对象
③、 decltype:可以推导一个对象的类型