之前的一篇隐式类型转换,介绍了编译器在什么情况下会偷偷的进行类型转换,以及转换的一些规则,这篇文章主要总结显式类型转换的一些知识
简单回顾一下,显式的类型转换有哪些,C++中提供4种强制类型转换的方式
- static_cast
- dynamic_cast
- const_cast
- reinterpret_cast
接下来就对4种强制类型转换的方式分别进行分析
static_cast
任何具有明确定义的类型转换,都可以使用 static_cast
, 但是 static_cast
不能转换掉底层 const
,volatile
属性
注意注意,static_cast
由于不提供运行时的检查,因此需要开发者自己确保转换的安全性,否则可能产生未定义的后果。
主要在以下几个场景中使用比较多:
- 当需要把一个较大的算数类型赋值给较小的类型时,明确告诉了编译器我们不在乎潜在的精度损失,编译器也不会给出警告信息
- void指针和具体类型指针之间的转换,
static_cast
可以找回原本存在于void*
指针中的值, 当然也可以转换成void*
类型 - 原有一些隐式类型转换, 例如向上转型,
short
转int
,int
转double
等, 类类型转换(转换构造函数或者类型转换运算符)等等, 当然了可以不写static_cast
, 编译器也会默认转换的 - 派生类向基类的转换,注意
static_cast
是把子类对象的指针或引用转换成父类指针或引用,这种转换是安全的,但是进行下行转换,是不安全的
dynamic_cast
dynamic_cast
用于在类的继承层次之间进行类型转换,它既允许向上转型,也允许向下转型
dynamic_cast
运算符的使用形式如下所示:
// type 必须是一个类类型
dynamic_cast<type*>(e) // e 必须是一个有效的指针
dynamic_cast<type&>(e) // e 必须是一个左值
dynamic_cast<type&&>(e) // e 不能是左值
e
的类型必须符合以下三个条件中的任意一个
- 目标 type 的公有派生类
- 目标 type 的公有基类
- 目标 type 的类型
如果符合,则类型转换成功。否则,转换失败
- 如果一条
dynamic_cast
语句的转换目标是指针类型并且失败了, 则结果是0
// bp 指向一个 Base 对象
// Base 基类, Derived 为其派生类
if (Derived * dp = dynamic_cast<Derived> (bp)) {
// 使用 dp 指向的 Derived 对象
} else {
// 使用 bp 指向的 Base 对象
}
- 如果转换目标是引用类型并且失败了,则
dynamic_cast
运算符会抛出一个bad_cast
异常
try {
const Derived &d = dynamic_cast<const Derived&> d;
// 使用 b 引用的 Derived 对象
} catch(bad_cast) {
// 处理类型转换失败的情况
}
简单整理一下 dynamic_cast
- 相比
static_cast
,dynamic_cast
会在运行时检查类型转换是否合法,具有一定的安全性 dynamic_cast
转换仅适用于指针或引用。- 类的继承层次之间进行类型转换,有个前提是,基类必须要有虚函数,因为
dynamic_cas
t是运行时类型检查RTTI
,需要运行时类型信息,而这个信息是存储在类的虚函数表中,只有一个类定义了虚函数,才会有虚函数表- 向上转型,一定能转换成功,因为向上转型始终是安全的
- 向下转型,对于下行转换,
dynamic_cast
是安全的(当类型不一致时,转换过来的是空指针),而static_cast
是不安全的 - 但是在多层继承的情况下,向下转型可能不一定能转换成功(不是说不安全),这就涉及到 C++ RTTI 机制下对象内存方面的知识,这里先不展开,后面会单独写一篇这方面的知识
const_cast
const_cast
用于移除类型的 const
、volatile
属性,也是只能改变运算对象的 const
、volatile
属性
- 常量指针被转换成非常量指针,并且仍然指向原来的对象;
- 常量引用被转换成非常量引用,并且仍然引用原来的对象。
reinterpret_cast
reinterpret_cast
通过 重新解释 底层位模式 的类型间转换, 简单说就是是从底层对数据类型进行重新解释
int *ip;
char *pc = reinterpret_cast<char*>(ip);
pc
所指的真实对象是一个 int 而非字符,所以把 pc
当成普通字符指针使用就可能在运行时发生错误
string str(pc); // 非常危险
reinterpret_cast
相当于告诉编译器 pc
的值是 char*
类型,而实际上它存放的是指向 int
的指针
所以 reinterpret_cast
是非常激进的指针类型转换,在编译期完成,可以转换任何类型的指针,所以极不安全。不到万不得已,不用使用这个转换符
看到一篇博客里提到很有意思的一点:
reinterpret_cast
体现了 C++
语言的设计思想:用户可以做任何操作,但要为自己的行为负责。
总结
强制类型转换干扰了正常的类型检查,因此我们应该避免使用强制类型转换, 尤其是 reinterpret_cast
, 如果实在无法避免,也应该尽量限制类型转换的作用域,并且记录对相关类型的所有嘉定,这样可以减少错误的发生