一、C 风格(C-style)类型强制转换
方法比较简单,如下所示:
TYPE1 A;
TYPE2 B;
A = (TYPE1)B; // 强制转换。
二、C++风格(C++ style)类型强制转换
C++标准定义了四个新的转换符:'reinterpret_cast', 'static_cast', 'dynamic_cast' 和 'const_cast',目的在于控制类(class)之间的类型转换。
代码:
reinterpret_cast<new_type>(expression)
dynamic_cast<new_type>(expression)
static_cast<new_type>(expression)
const_cast<new_type>(expression)
1、const_cast
const_cast转换符是用来移除变量的const或volatile限定符。
对于const变量,我们不能修改它的值,这是这个限定符最直接的表现。但是我们就是想违背它的限定希望修改其内容怎么办呢?
下边的代码显然是达不到目的的:const int constant = 10;
int modifier = constant;
因为对modifier的修改并不会影响到constant,这暗示了一点:const_cast转换符也不该用在对象数据上,因为这样的转换得到的两个变量/对象并没有相关性。
只有用指针或者引用,让变量指向同一个地址才是解决方案,可惜下边的代码在C++中也是编译不过的:const int constant = 21;
int* modifier = &constant
// Error: invalid conversion from 'const int*' to 'int*'
(上边的代码在C中是可以编译的,最多会得到一个warning,所在在C中上一步就可以开始对constant里面的数据胡作非为了)
把constant交给非const的引用也是不行的。const int constant = 21;
int& modifier = constant;
// Error: invalid initialization of reference of type 'int&' from expression of type 'const int'
于是const_cast就出来消灭const,以求引起程序世界的混乱。
下边的代码就顺利编译通过了:const int constant = 21;
const int* const_p = &constant;
int* modifier = const_cast<int*>(const_p);
*modifier = 7;
测试发现,指针modifier指向的内存的数据变为7,常量constant仍然是21,而不是7,为什么呢?modifier、&constant指向同一内存,内容却不一样,这是因为常量constant是“可折叠常量”,在编译阶段,对常量的引用全部替换为该常量的值(21),常量名constant并不会消失,编译器会把他放入到符号表中,同时,会为该变量分配空间。因此,常量constant不是靠内存中的内容来取数的,而是靠保存在符号表中的索引来取数的,因此常数constant无论什么情况下使用常数21,同时它又有内存空间。
C++准转换运算符是可以用传统转换方式实现的。const_cast实现原因就在于C++对于指针的转换是任意的,它不会检查类型,任何指针之间都可以进行互相转换,因此const_cast就可以直接使用显示转换(int*)来代替:
const int constant = 21;
int* modifier = (int*)(&constant);
2、static_cast
static_cast在面对const的时候无能为力:不能去除const限定。
static_cast只能用在“具有一定关系”的指针类型(类指针)之间的转换;此外,static_cast还可以用在基本类型和对象的强制类型转换上。对于static_cast所需要的关系,"继承"是其中之一,static_cast支持具有继承关系的类指针之间的类型转换。但要注意:这种转换是无条件的,只负责类型转换,不负责检测转换是否安全,即,转换后得到的结果(类指针)始终不为空,但是结果不一定正确。代码:
class A
{
......
};
class B : public A
{
......
};
int main()
{
B *pb =new B();
A *pa =static_cast<A*>(pb);// 子类强制转换为父类,结果pa不为空,结果正确。
A *pa2 =new A();
B *pb2 =static_cast<B*> (pa2);// 父类强制转换为子类,编译不会报错,但是不安全,子类的一些新的成员
// 无法调用。结果pb2不为空,但是结果错误。
......
}
实际上static_cast真正用处并不在指针和引用上,而在基本类型和类对象的类型转换上。而基本类型和对象的转换都是其他三个转换运算符所办不到的。代码:
float fV = 10.5;
int iV = static_cast<int>(fV);
A a;
B b =static_cast<B>(a);
3、dynamic_cast
dynamic_cast在面对const的时候无能为力:不能去除const限定。
dynamic_cast与static_cast一样,只能用在“具有一定关系”的指针类型(类指针)之间的转换,实际上只适用于具有继承关系的2个类指针之间的类型强制转换。dynamic_cast与static_cast的区别在于,它是有条件的转换,dynamic_cast会进行判别,确定源指针所指的内容,是否真的合适被目标指针接受(子类转换为父类可接受;父类转换为子类、没有关系的类之间的转换可能均不可接受)。如果是否定的,那么dynamic_cast则会返回NULL。这是通过检查"运行期类型信息"(Runtime type information,RTTI)来判定的,它还受到编译器的影响,有些编译器需要设置开启才能让程序正确运行),因此dynamic_cast也就不能用传统的转换方式来实现了。
4、reinterpret_cast
reinterpret_cast在面对const的时候无能为力:不能去除const限定。
该运算符用于不同类型的指针之间、指针与整数类型之间的类型强制转换,它会将一个类型的数据拷贝给另一个类型,使2个类型具有相同的数据,来实现类型强制转换。用于以下数据类型之间的强制转换:
- 从指针类型到一个足够大的整数类型;
- 从整数类型或者枚举类型到指针类型;
- 从一个指向函数的指针到另一个不同类型的指向函数的指针;
- 从一个指向对象的指针到另一个不同类型的指向对象的指针;
- 从一个指向类函数成员的指针到另一个指向不同类型的函数成员的指针;
- 从一个指向类数据成员的指针到另一个指向不同类型的数据成员的指针;
事实上reinterpret_cast的使用并不局限在上边所说的几项的,任何类型的指针之间都可以互相转换,都不会得到编译错误。总结来说:reinterpret_cast用在任意指针(或引用)类型之间的转换;以及指针与足够大的整数类型之间的转换;从整数类型(包括枚举类型)到指针类型。
但是,使用reinterpret_cast很容易导致程序的不安全,只有将转换后的类型值转换回到其原始类型,这样才是正确使用reinterpret_cast方式。这样说起来,reinterpret_cast转换成其它类型的目的只是临时的隐藏自己的类型,要真想使用那个值,还是需要让其露出真面目才行。这一点很重要,在一些特殊的场合中很有用。
总 结
去const属性用const_cast。
基本类型转换用static_cast。
多态类之间的类型转换用daynamic_cast。
不同类型的指针类型转换用reinterpreter_cast。