static_cast
将expression转换为type-id类型,主要用于非多态类型之间的转换,不提供运行时的检查来确保转换的安全性。如果type-id是指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。主要在以下几种场合中使用:
- 用于类层次结构中,基类和派生类之间指针和引用的转换;
当进行上行转换,也就是把子类的指针或引用转换成父类表示,这种转换是安全的;
当进行下行转换,也就是把父类的指针或引用转换成子类表示,这种转换是不安全的,也需要程序员来保证; - 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum等等,这种转换的安全性需要程序员来保证;
- 把任何类型的表达式转换成void类型;
- 把void指针转换成目标类型的指针,是极其不安全的;
- 普通基本类型间的转换(double转int等)
注:static_cast不能去除expression的const、volatile属性
const int a = 20;
int *p1 = static_cast<int*>(&a); // 编译错误,static_cast不能转换掉a的const属性
volatile int b = 20;
int *p2 = static_cast<int*>(&b); // 编译错误,static_cast不能转换掉b的volatile属性
int c = 20;
char *pc = static_cast<char*>(&c); // 编译错误,转换无效,reinterpret_cast可实现这个操作
char ch = static_cast<char>(c); // ok
Derived *dp = static_cast<Derived *>(bp) // 下行转换,编译通过,但是是不安全的
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操作类的指针或引用时,类中必须存在虚函数。由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表。
class A { virtual void f() {}; };
class B : public A { virtual void f() {}; };
class C : public B { virtual void f() {}; };
A *p1 = new A;
A *p2 = new B;
A *p3 = new C;
B *q1 = dynamic_cast<B*>(p1); // q1 == NULL
B *q2 = dynamic_cast<B*>(p2); // ok, p2 actually points to a B
B *q3 = dynamic_cast<B*>(p3); // ok, p3 actually points to a Derived of B
int c = 20;
char ch = dynamic_cast<char>(c); // 编译出错,dynamic_cast只接受完整类型的指针引用或,
// 换成static_cast可以
void *pv;
int *pi = dynamic_cast<int*>(pv); // 编译出错,换成static_cast可以
const_cast
const_cast一般用于强制消除对象的常量性(const和volatile都可)。它是唯一能做到这一点的C++风格的强制转型。这个转换能剥离一个对象的const属性,也就是说允许你对常量进行修改。
const_cast的type_id只能为指针或者引用,且不能像static_cast、dynamic_cast一样做普通类型转换。
class A
{
public:
A() : i(3) {}
int i;
void f(int v) const
{
this->i = v; // 编译错误: this 是指向 const 的指针
const_cast<A*>(this)->i = v; // 将this指针从const type* 转为type*
}
};
const int i = 1;
volatile int ii = 2;
int *pi = const_cast<int*>(&i); // ok
int *pii = const_cast<int*>(&ii); // ok
int j = const_cast<int>(i); // 编译出错,const_cast只能接受指针或引用
long long *k = const_cast<long long*>(&i); // 编译出错,只能调节类型限定符不能更改基础类型
reinterpret_cast
reinterpret_cast 是特意用于底层的强制转型,导致实现依赖(就是说,不可移植)的结果。这样的强制类型在底层代码以外应该极为罕见。操作结果只是简单的从一个指针到别的指针的值得二进制拷贝。在类型之间指向的内容不做任何类型的检查和转换。reinterpret_cast 表达式不会编译成任何 CPU 指令,它纯粹是一个编译时指令,指示编译器将 expression 的位序列视为 new_type 类型的位序列。
reinterpret_cast可用于指针与足够大整数的互相转换、不同类型函数指针的互相转换、不同类型数据指针的互相转换、不同类型的类的函数成员的互相转换、不同类型的类的数据成员的互相转换。
int i = 7;
char *p0 = reinterpret_cast<char*>(i); // ok,认为i中存放了一个地址
double f = 1.23;
char *p1 = reinterpret_cast<char*>(f); // 编译出错,不能将浮点数认为是地址
class A{...};
A a;
char *p2 = reinterpret_cast<char*>(a); // 编译出错,不能将类认为是地址
void *p;
int addr = reinterpret_cast<int>(p); // ok,addr中存放指针p的地址
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的互相转换;指针与整形的互相转换等。