C++中类型转换相关的关键字

Q:在C++中,有哪4个与类型转换相关的关键字?这些关键字有什么特点,应该在什么场合下使用?
C++中与类型转换相关的四个关键字有:const_cast、static_cast、dynamic_cast、reinterpret_cast。
类型转换(Type Cast)
C风格的类型转换的是强制类型转换,在以前的编程中我也是这么用的。形如:
TYPE b = (TYPE)a;
C++则是提供了4种类型转换符来应对不同场合的应用,这里对一些博客的内容进行总结。

1、const_cast 常量转换

用来修改类型的const或volatile属性,也就将是常型(const)转换为非常型,将易变型(volatile)转换为非易变型。如果用于其他类型的转换,一般会产生编译错误。
使用const_cast会消除被转换类型的const特性,而且只有const类型的变量才能使用。比如,有时候有的函数的形参类型为非const类型,那么如果你要将一个const类型的参数传入就会报错,在这种情况下需要先使用const_cast转化一下。
常量指针被转化成非常量指针,并且仍然指向原来的对象;
常量引用被转换成非常量引用,并且仍然指向原来的对象;
常量对象被转换成非常量对象。

1
2
3
4
5
6
7
8
9
10
class B {
public:
  int Num;
};
void foo() {
  const B b1;
  b1.Num = 100; //comile error
  B b2 = const_cast(b1);
  b2.Num = 200; //fine
}

上面的代码编译时会报错,因为b1是一个常量对象,不能对它进行改变;使用const_cast把它转换成一个常量对象,就可以对它的数据成员任意改变。

2、static_cast 静态转换

类似于C风格的强制转换,用于:
  1. 基类和子类之间转换:其中子类指针转换成父类指针是安全的;但父类指针转换成子类指针是不安全的。(基类和子类之间的动态类型转换建议用dynamic_cast)
  2. 基本数据类型转换。enum, struct, int, char, float等。static_cast不能进行无关类型(如非基类和子类)指针之间的转换。
  3. 把空指针转换成目标类型的空指针。
  4. 把任何类型的表达式转换成void类型。
  5. static_cast不能去掉类型的const、volitale属性(用const_cast)。

1
2
3
4
5
6
7
double d = 3.14159265;
int i = static_cast(d); //基本数据类型转换
int i = static_cast<int>(d); //基本数据类型转换

int *pn = &n;
double *d = static_cast<double *>(&n) //无关类型指针转换,编译错误
void *p = static_cast<void *>(pn); //任意类型转换成void类型

3、dynamic_cast 动态转换

它是一种作运行时(run-time)检测的类型转换。它可以将基类类型的指针或引用安全地转换为派生类型的指针或引用。当具有基类的引用或指针,但需要执行不是基类组成部分的派生类操作的时候,需要动态的强制类型转换。通常,从基类指针获得派生类行为最好的方法是通过虚函数。当使用虚函数的时候,编译器自动根据对象的实际类型选择正确的函数。但是,在某些情况下,不可能使用虚函数。这时候就需要使用dynamic_cast关键字了。但是,能用虚函数还是用虚函数最好。

有条件转换,动态类型转换,运行时类型安全检查(转换失败返回NULL):
  1. 安全的基类和子类之间转换。
  2. 必须要有虚函数。
  3. 相同基类不同子类之间的交叉转换,但结果是NULL。
  4. 在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的。
  5. 在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

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
33
class A{
public:
    int Num;
    virtual void f(){}
};

class B:public A{ 
};

class C:public A{
public:
char *Name[100];
};

class D:public A{
};

void func(A *pa){
C *pa1 = static_cast<C *>(pa); //静态类型转换,正确但不推荐
C *pa2 = dynamic_cast<C *>(pa); //动态类型转换,正确
/*如果pa指向一个C类型的对象,pa1和pa2是一样的,并且对这两个指针执行C类型的任何操作都是安全的;
但是,如果pa指向的是一个A类型的对象,那么pa1将是一个指向该对象的指针,对它进行C类型的操作将是不安全的(如访问Name),而pa2将是一个空指针。    
另外要注意:A要有虚函数,否则会编译出错;static_cast则没有这个限制。
*/

}

void foo(){    
    B *pb = new B;    
    pb->Num = 100;    
    D *pd1 = static_cast<D *>(pb); //compile error,使用static_cast进行转换是不被允许的
    D *pd2 = dynamic_cast<D *>(pb); //pd2 is NULL,使用dynamic_cast转换则是允许的,结果是空指针
    delete pb;    
}

4、reinterpreter_cast 重解释转换

如果被转换的两种类型之间不相关,比如int指针转换为char指针,就要使用reinterpret_cast这个关键字。
  1. 转换的类型必须是一个指针、引用、算术类型、函数指针或者成员指针。
  2. 在比特位级别上进行转换。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。但不能将非32bit的实例转成指针。
  3. 最普通的用途就是在函数指针类型之间进行转换。
  4. 平台移植性比较差。

1
2
3
4
class A {};
class B {};
A * a = new A;
B * b = reinterpret_cast(a);

总结

  去const属性用const_cast。
  基本类型转换用static_cast。
  多态类之间的类型转换用daynamic_cast。
  不同类型的指针类型转换用reinterpreter_cast。