*_cast in C++
static_cast
static_cast
的主要应用场景为:
- 用于基本数据类型之间的转换。但是由于数值范围的不同,需要用户保证转换的安全性。
- 用于类指针/引用的上/下行转换。这种转换是在编译期完成的,如果无法完成转换,在编译的时候会报错。
其中对于类指针/引用的上/下行转换:
- 用于上行转换的时候是安全的。
- 用于下行转换的时候是不安全的,因为它不会做 run time 的类型检查。
这里对安全的定义,可以见下面和 dynamic_cast
对比的部分。
dynamic_cast
dynamic_cast
可以用于有直接的继承关系的类指针/引用的上/下行转换。
对于上行转换,dynamic_cast
和 static_cast
没有差别,因为肯定是安全的(parent 有的,child 肯定有)。对于下行转换,dynamic_cast
会做 run time type check(这是比 static_cast 多的)。
什么是 run time type check?或者说它检查的是什么?所谓的 run time type check 是指检查 待转换的指针/引用
是不是真的指向 目的类型
。如果是,转换成功;否则,转换失败。
但是对于类指针和引用的转换,在转换失败的时候的行为又有不同:
- 对于类指针的转换,即使转换失败也不会报错,但是转换失败的时候会返回
nullptr
。因此,可以根据转换后是否为nullptr
来判断转换是否成功。 - 对于引用的转换,如果转换失败会直接报错 (
the exception std::bad_cast is thrown
)。因为可以存在空指针 (nullptr
),但是不存在空引用。
Note:
dynamic_cast
只能对有 多态(polymorphic) 性质的 class 使用,也就是说必须要有 virtual function。原因是:compiler 只会对具有多态性质的 object 进行 run time type check。dynamic_cast
除了可以用于有直接的继承关系的类指针/引用的上/下行转换之外,还可以用于有间接关系的“兄弟”(有同一个 parent)类指针/引用的转换,高级用法见 dynamic_cast Operator | Microsoft Docs 。
static_cast vs dynamic_cast
举个例子来做对比。
1 | class B { |
对于上面的代码:
- 如果
pb
真的指向一个D
对象:那么pd1
,pd2
是一致的。 - 如果
pb
指向的是一个B
对象:由于dynamic_cast
会做 run time type check,发现pb
指向 parent, 为了保证安全(child 可能含有 parent 没有的 field 或 function),就不会进行转换,最终pd1 == nullptr
,但是pd2
不会有任何提示。此时,如果pd2
call 了 D 特有的 field 或 function,就会出错。这就是之所以说static_cast
是不安全的原因。
存在这种不同的原因是两者处理转换的逻辑不同:
static_cast
仅仅是依靠类型转换语句中提供的信息来进行转换。dynamic_cast
则会遍历整个类继承体系进行类型检查。比如上面的例子,dynamic_cast 会去确认 pb 指向的对象的真正类型,因此 dynamic_cast 在执行效率上比 static_cast 要差一些。
const_cast
const_cast
用于消除数据的 const
、volatile
、__unaligned
标示符。
但是最终还是要看 待转换的指针/引用
指向的那个 object
是不是 被 declared 成了 const
。如果不是,可以正常修改;否则,对转换后的 指针/引用
进行修改是 Undefined behavior
。
用个例子来说明:
1 |
|
注意:上面的例子里 *c = 30;
是 Undefined behavior
,因为 c
指向的是 a
,而 a
是 const
的。但是 *c1 = 50;
是合法的,因为 c
指向的是 a1
,而 a1
不是 const
的。The const_cast operator (C++ only)
那么问题来了,为什么会产生 Undefined behavior,而不是直接 run-time error 之类的?或者说为什么不允许修改被 declare 为 const 的 object? emm….还没找到原因。
Note:
- 在去掉
const
之后对数据做修改,仍然可能抛出 run-time error。比如,修改的constant
恰好被存储在read-only
的 memory 区域。
reinterpret_cast
reinterpret_cast
用于不同类型之间的指针或引用的转换 ,它的本质是对指向内存的比特位的重解释。谨慎使用。