*_cast in C++

static_cast

static_cast 的主要应用场景为:

  • 用于基本数据类型之间的转换。但是由于数值范围的不同,需要用户保证转换的安全性。
  • 用于类指针/引用的上/下行转换。这种转换是在编译期完成的,如果无法完成转换,在编译的时候会报错。

其中对于类指针/引用的上/下行转换:

  • 用于上行转换的时候是安全的。
  • 用于下行转换的时候是不安全的,因为它不会做 run time 的类型检查。

这里对安全的定义,可以见下面和 dynamic_cast 对比的部分。

dynamic_cast

dynamic_cast 可以用于有直接的继承关系的类指针/引用的上/下行转换。

对于上行转换,dynamic_caststatic_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
2
3
4
5
6
7
8
9
10
11
class B {
public:
virtual void Test(){}
};

class D : public B {};

void f(B* pb) {
D* pd1 = dynamic_cast<D*>(pb);
D* pd2 = static_cast<D*>(pb);
}

对于上面的代码:

  • 如果 pb 真的指向一个 D 对象:那么 pd1pd2 是一致的。
  • 如果 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 用于消除数据的 constvolatile__unaligned 标示符。

但是最终还是要看 待转换的指针/引用 指向的那个 object 是不是 被 declared 成了 const。如果不是,可以正常修改;否则,对转换后的 指针/引用 进行修改是 Undefined behavior

用个例子来说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
using namespace std;

void f(int* p) {
cout << *p << endl;
}

int main(void) {
const int a = 10;
const int* b = &a;

// Function f() expects int*, not const int*
// f(b);
int* c = const_cast<int*>(b);
f(c);

// Lvalue is const
// *b = 20;

// Undefined behavior
// *c = 30;

int a1 = 40;
const int* b1 = &a1;
int* c1 = const_cast<int*>(b1);

// Integer a1, the object referred to by c1, has
// not been declared const
*c1 = 50;

return 0;
}

注意:上面的例子里 *c = 30;Undefined behavior,因为 c 指向的是 a,而 aconst 的。但是 *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 用于不同类型之间的指针或引用的转换 ,它的本质是对指向内存的比特位的重解释。谨慎使用。

Reference