我们在C++常用四种类型转换: static_cast<type>
, const_cast<type>
,reinterpret_cast<type>
,以及dynamic_cast<type>
。其中static_cast<type>
转换如果出错, 则发生在编译时, 其他三种类型转换需要谨慎, 使用了他们代表着程序员事先清楚运行时不会出现错误。
static_cast<type>
主要用于下面三种情况:
1. void
指针转换为目标指针或者相反
2. 改变通常的标准隐式转换
3. 类层次中转换, 子类转向父类或者相反(这类转换后文还会讨论)
const_cast<type>
主要是用来去掉对象底层的const
属性,从而适配一些重载函数,比如:
const int i = 0;
const int *cpi = &i;
int *pi = const_cast<int *>(cpi);
但值得注意的是,虽然我们通过指针pi
获得了变量i的写权限, 但写操作是未定义的。
reinterpret_cast<type>
是最危险的操作, 因为这个转换是可以在毫不相干的类型中间转换但编译器是允许的, 比如
char c = 'a'
char *p = &a;
double *pd = reinterpret_cast<double*>(p);
这其实是一种底层的重新解释。
重点说一下dynamic_cast<type>
, 这个转换运算符主要用在具有多态行为的类之间, 关注的是对象的动态类型。注意与static_cast<type>
的区别, 举例如下:
class Base {
virtual void f();
};
class Derived : public Base {
void f();
void m();
};
void foo(Base *b) {
Derived *d1 = static_cast<Derived*>(b);
Derived *d2 = dynamic_cast<Derived*>(b);
}
在上面的例子中如果我们有以下的调用形式:
Base *p = new Derived();
foo(p);
那么上文中的两种转换是没有区别的, 但是如果我们按照下面的形式调用:
Base *p = new Base();
foo(p);
那么当d1
调用基类中没有的方法void m()
的时候就会有运行时错误, 但是d2
就不会有这样的问题出现, 因为dynamic_cast<type>
检测p
的运行时类型, 因为与等号左边的类型不符, 所以给d2
赋值为nullptr
,我们只需要检测d2
是不是一个空值,就可以避免运行时错误了。
如果Base
又派生了一个新类:
class NewDerived : public Base {}
Base *pb = new NewDerived();
Derived *pd1 = static_cast<Derived *>(pb);
Derived *pd2 = dynamic_cast<Derived *>(pb);
由于多了运行时检查,pd2
返回nullptr
,而pd1
知道运行遇到错误才会发现。
除此之外, 我们还可以在程序中用这种方式转换得到不同的派生类指针, 调用不同的成员函数, 实现手动的多态。