static_cast

将expression转换为type-id类型,主要用于非多态类型之间的转换,不提供运行时的检查来确保转换的安全性。如果type-id是指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。主要在以下几种场合中使用:

  1. 用于类层次结构中,基类和派生类之间指针和引用的转换;
    当进行上行转换,也就是把子类的指针或引用转换成父类表示,这种转换是安全的;
    当进行下行转换,也就是把父类的指针或引用转换成子类表示,这种转换是不安全的,也需要程序员来保证;
  2. 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum等等,这种转换的安全性需要程序员来保证;
  3. 把任何类型的表达式转换成void类型;
  4. 把void指针转换成目标类型的指针,是极其不安全的;
  5. 普通基本类型间的转换(double转int等)

注:static_cast不能去除expression的const、volatile属性

  1. const int a = 20;
  2. int *p1 = static_cast<int*>(&a); // 编译错误,static_cast不能转换掉a的const属性
  3. volatile int b = 20;
  4. int *p2 = static_cast<int*>(&b); // 编译错误,static_cast不能转换掉b的volatile属性
  5. int c = 20;
  6. char *pc = static_cast<char*>(&c); // 编译错误,转换无效,reinterpret_cast可实现这个操作
  7. char ch = static_cast<char>(c); // ok
  8. Derived *dp = static_cast<Derived *>(bp) // 下行转换,编译通过,但是是不安全的
  9. Base *bp = static_cast<Base *>(dp) // 上行转换,编译通过,是安全的

dynamic_cast

dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。在多态类型之间的转换主要使用dynamic_cast,因为类型提供了运行时信息。
相对于static_cast,用dynamic_cast进行转换时,如果将一个基类的指针或引用转换为派生类的指针或引用,会进行类型检查:expression是否真的指向一个type-id类型的?如果是,则能进行正确转换;否则返回NULL。如果是转换的类型是引用,则会在运行时抛出异常。
dynamic_cast操作类的指针或引用时,类中必须存在虚函数。由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表。

  1. class A { virtual void f() {}; };
  2. class B : public A { virtual void f() {}; };
  3. class C : public B { virtual void f() {}; };
  4. A *p1 = new A;
  5. A *p2 = new B;
  6. A *p3 = new C;
  7. B *q1 = dynamic_cast<B*>(p1); // q1 == NULL
  8. B *q2 = dynamic_cast<B*>(p2); // ok, p2 actually points to a B
  9. B *q3 = dynamic_cast<B*>(p3); // ok, p3 actually points to a Derived of B
  10. int c = 20;
  11. char ch = dynamic_cast<char>(c); // 编译出错,dynamic_cast只接受完整类型的指针引用或,
  12. // 换成static_cast可以
  13. void *pv;
  14. int *pi = dynamic_cast<int*>(pv); // 编译出错,换成static_cast可以

const_cast

const_cast一般用于强制消除对象的常量性(const和volatile都可)。它是唯一能做到这一点的C++风格的强制转型。这个转换能剥离一个对象的const属性,也就是说允许你对常量进行修改。
const_cast的type_id只能为指针或者引用,且不能像static_cast、dynamic_cast一样做普通类型转换。

  1. class A
  2. {
  3. public:
  4. A() : i(3) {}
  5. int i;
  6. void f(int v) const
  7. {
  8. this->i = v; // 编译错误: this 是指向 const 的指针
  9. const_cast<A*>(this)->i = v; // 将this指针从const type* 转为type*
  10. }
  11. };
  12. const int i = 1;
  13. volatile int ii = 2;
  14. int *pi = const_cast<int*>(&i); // ok
  15. int *pii = const_cast<int*>(&ii); // ok
  16. int j = const_cast<int>(i); // 编译出错,const_cast只能接受指针或引用
  17. long long *k = const_cast<long long*>(&i); // 编译出错,只能调节类型限定符不能更改基础类型

reinterpret_cast

reinterpret_cast 是特意用于底层的强制转型,导致实现依赖(就是说,不可移植)的结果。这样的强制类型在底层代码以外应该极为罕见。操作结果只是简单的从一个指针到别的指针的值得二进制拷贝。在类型之间指向的内容不做任何类型的检查和转换。reinterpret_cast 表达式不会编译成任何 CPU 指令,它纯粹是一个编译时指令,指示编译器将 expression 的位序列视为 new_type 类型的位序列。
reinterpret_cast可用于指针与足够大整数的互相转换、不同类型函数指针的互相转换、不同类型数据指针的互相转换、不同类型的类的函数成员的互相转换、不同类型的类的数据成员的互相转换。

  1. int i = 7;
  2. char *p0 = reinterpret_cast<char*>(i); // ok,认为i中存放了一个地址
  3. double f = 1.23;
  4. char *p1 = reinterpret_cast<char*>(f); // 编译出错,不能将浮点数认为是地址
  5. class A{...};
  6. A a;
  7. char *p2 = reinterpret_cast<char*>(a); // 编译出错,不能将类认为是地址
  8. void *p;
  9. int addr = reinterpret_cast<int>(p); // ok,addr中存放指针p的地址
  10. unsigned ui = reinterpret_cast<unsigned>(i); // 编译出错,类型转换无效

总结

static_cast可用于普通类型转换(double->int);void的互相转换;基类派生类指针引用的互相转换(其中上行转换是不安全的);不能用于消除const、volatile限定符。
dynamic_cast不可用于普通类型转换;只能将其它类型的指针转void
而不能将void转其它;基类派生类指针引用的互相转换(会做动态类型检查拒绝下行转换);不能用于消除const、volatile限定符。
const_cast只能用于去除指针、引用的const、volatile限定符,不接受实例;可用于const成员函数中去除this指针的const限定。
reinterpret_cast可用于更改指针、引用的类型(int
->char),包括void的互相转换;指针与整形的互相转换等。