四种 cast 强制类型转换
前言
在 C 语言中,直接使用类型即可完成强制类型转换,形如 (int)x。而在 C++ 中强制类型转换被分为四类:static_cast / const_cast / reinterepret_cast / dynamic_cast 四类。当然 C++ 也保留了传统类型转换。
传统类型转换
传统 C 语言类型转换具有最高的级别,它能完成我们后面将会介绍的四种 cast 支持的所有强制类型转换。但在 C++ 中之所以提出四类 cast ,是因为将强制类型转换分门别类被认为是更安全的。首先,强制类型转换都是危险的,你需要清楚的知道进行转换可能带来的后果,四类 cast 一定程度上确保你正确进行转换,否则将会发生编译错误(dynamic_cast 可能会发生运行时错误)。
值得注意的是,在 C++ 中提出的 static_cast / const_cast / reinterepret_cast / dynamic_cast 都是关键字,足见其地位之重。
最后,传统 C 语言类型转换在 C++ 中又分为两种:C 风格类型转换、 C++ 风格传统类型转换。其实两者并没有区别,只是在语法习惯上作区分。
1 | int x = 10; |
static_cast 静态类型转换
这是最常用的类型转换,可以完成所有单一类型转换操作(复合类型的情况比较复杂)。但是不能做的操作有:
- 不能去除指针、数组、引用的底层
cosnt,但可以添加底层const。注意数组、引用的const都是底层const即它们没有顶层const。 - 不能修改指针底层类型。但在不违反上一原则的前提下,能将任意指针类型转为
void */const void *,或将void */const void *转为任意指针类型。
这两种不能进行的转换可以使用后面介绍的方法转换,将在后面章节介绍。
1 | int x = 10; |
另外,在面向对象方面,static_cast 还有其特殊性。主要包含两点:
- 对于用户定义类型,如果定义了类型转换函数,则其类型转换是合法的,否则是非法的。
- 如果类之间满足继承关系,则其类型转换是合法的。当然,基类到派生类的转换可能引入新的变量,这是危险的。(后面将介绍
dynamic_cast应对这个问题)
const_cast 常量类型转换
可以任意修改 const,包括底层和顶层 const,但不能进行其他类型转换。const_cast 对应处理 static_cast 的第一条不可用原则。
1 | int x = 10; |
const_cast 通常用于临时修改变量的可修改性,从而保证该变量通常是不可修改的,在需要时临时修改。const_cast 的存在因此被认为是有利于程序安全的,而其他强制类型转换的出现都被认为代码存在或多或少的缺陷。当然,将 const_cast 返回值存储并长期使用依然是危险的。
1 | // 一个安全使用 const_cast 的示例 |
reinterpret_cast 重新解释类型
也用作指针、数组、引用类型的转换,在不修改底层 const 的前提下可以进行任意底层类型的修改。reinterpret_cast 是十分危险的,正如其名字一样,reinterpret_cast 只对底层进行重新解释,而不作转换,对于自定义类型也不调用类型转换函数。
1 | // 这个例子展示了 reinterpret_cast 的危险性 |
另外,reinterpret_cast 其实可以通过两次 static_cast 实现。
1 | int *p = new int(10); |
1 | // 想说明 reinterpret_cast 并不是等价于两次 static_cast 的 |
dynamic_cast 动态类型转换
dynamic_cast 是 RTTI(runtime type identification)的两个运算符之一,它作用于运行时,用于确保抽象类到派生类的安全转换。在使用抽象类时,我们通常将派生类转换为抽象类指针或引用,而虚函数将继续与派生类绑定。而 dynamic_cast 提供了安全的运行时类型转换,通常用于抽象类到具体类的转换。
当进行错误的动态类型转换时会发生错误。具体表现为一下三个方面:
- 如果原始类型和目标类型没有运行时多态关系,则
dynamic_cast发生编译时错误。(两个类如果存在公共抽象类,且存在虚函数继承关系,则它们存在运行时多态关系) - 如果转换后类型不是原类型或其一个基类,则
dynamic_cast发生运行时错误,其中指针和引用的表现形式是不同的:- 如果转换类型为指针,则错误时返回空指针。
- 如果转换类型为引用,则错误时抛出
bad_cast异常。
需要注意的是,使用 static_cast(以及传统类型转换)也能完成这些类型转换,但其没有像 dynamic_cast 一样的确保安全性的机制。
1 | // 一些类的定义 |
1 | // dynamic_cast 判别为是相同类型 |
1 | // dynamic_cast 判别为非相同类型并返回 NULL |
1 | // static_cast 无法判断转换正误是相当危险的 |
1 | // dynamic_cast 检测到编译错误 |
