C++的强制类型转化用于不同的情况和原因,比C进步的是C++将这些情况细分并用不同的强制类型转换符号来表示。这样程序就比C更容易解析。
命名的强制类型转换符号的一般形式如下:
cast-name<type>(expression);
其中cast-name为static_cast、dynamic_cast、const_cast、reinterpret_cast之一,type为
换的目标类型,而expression则是被强制转换的值。
编译器隐式执行的任何类型转换都可以由static_cast显示完成,也就是说一般的类型转换都是使用static_cast比较好。static_cast基本上拥有与C旧式转换相同的威力与意义,以及相同的限制。例如你不能够利用static_cast将一个struct转型为int,或者将一个double转型为pointer。
const_cast就是用来转换表达式的const性质。例如: constchar *pc_str; const_cast<char*>(pc_str); 类似地,除了添加或者删除const特性,用const_cast符来执行其他任何类型转换,都会引起编译错误。
reinterpret_cast通常为操作数的位模式提供较低层次的重新解释。reinterpret_cast本质上依赖于机器。为了安全使用reinterpret_cast,要求程序员完全理解所涉及的数据类型,以及编译器实现强制类型转换的细节。由于reinterpret_cast与编译平台相关,所以不具备可移植性。至今我都没有讲过reinterpret_cast的例子程序。用的很少也不建议使用。
对于这个类型转化是与C++的RIIT特性相关的。
首先解释一下tpye_info结构,C++中各个类型(无论是内置类型还是自定义类型)的信息都由各自的type_info来描述。这里从VC++的typeinfo.h中找到VC++版本下的type_info定义:
class type_info { public: virtual~type_info(); intoperator==(const type_info& rhs) const; intoperator!=(const type_info& rhs) const; intbefore(const type_info& rhs) const; constchar* name() const; //传回原始名称 constchar* raw_name() const; //传回编码后名称 private: void *_m_data; char _m_d_name[1]; type_info(const type_info& rhs); type_info& operator=(const type_info& rhs); };
以下的一段程序测试了tpyeid(取得类型的type_info结构)的功能:
#include<iostream> #include<typeinfo> using namespace std; class B{}; class D:public B{}; int main() { //测试组1 intvInt=10; intarr[2]={10,20}; int*p=&vInt; int**p2p=&p; int*parr[2]={&vInt,&vInt}; int(*p2arr)[2]=&arr; //测试组2 B*pb=new B; D*pd=new D; B*pbd=new D; //测试组1结果 cout<<"测试组1结果"<<endl; cout<<"Declaration [intvInt=10]type=="<<typeid(vInt).name()<<endl; cout<<"Declaration [intarr[2]={10,20}]type=="<<typeid(arr).name()<<endl; cout<<"Declaration [int*p=&vInt]type=="<<typeid(p).name()<<endl; cout<<"Declaration [int**p2p=&p]type=="<<typeid(p2p).name()<<endl; cout<<"Declaration [int(*p2arr)[2]=&arr]type=="<<typeid(p2arr).name()<<endl; //测试组2结果 cout<<"测试组2结果"<<endl; cout<<"pb's type name =="<<typeid(pb).name()<<endl; cout<<"pd's type name =="<<typeid(pd).name()<<endl; cout<<"pbd's type name =="<<typeid(pbd).name()<<endl; //测试编码后名称 cout<<"测试编码后名称"<<endl; cout<<"pb's type rawname =="<<typeid(pb).raw_name()<<endl; cout<<"pd's type rawname=="<<typeid(pd).raw_name()<<endl; cout<<"pbd's type rawname=="<<typeid(pbd).raw_name()<<endl; return0; }
程序的输出结果为:
这个程序要注意的一点是:
cout <<"Declaration [intarr[2]={10,20}]type=="<<typeid(arr).name()<<endl;
明明是一个数组确显示为一个指针,因为typeid是函数操作。所以数组名退化为指针了,个人认为在这一点上typeid的设计尚有欠缺,至少在VC++环境下是如此。
dynamic_cast用来执行继承体系中“安全的向下转型或跨系转型动作”。也就是说你可以利用dynamic_cast将“(类型为)指向基类对象的指针”转型为“(类型为)指向派生类对象的指针”,并得知转型是否成功。如果转型失败会以一个null指针(当转型对象是指针)或一个exception(当转型对象是引用)表现出来。
#include<iostream> #include<typeinfo> using namespace std; class B { public: B(){} virtualprint(){cout<<"B"<<endl;} }; class D:public B { public: D(){} virtualprint(){cout<<"D"<<endl;} }; void Print(D* p) { p->print(); } void PPrint(D& d) { d.print(); } int main() { Bb; B&rb=b; B*pb=new B; D*pd=new D; B*pbd=new D; Print(dynamic_cast<D*>(pb)); PPrint(dynamic_cast<D&>(rb)); return0; }
这个程序是异常的。
另外值得注意的是:dynamic_cast只能用于有虚函数的继承体系中!!!!!!!!
避免使用强制类型转换,强制类型转换关闭或者挂起了正常的类型检查。强烈建议程序员避免使用强制类型转换,不依赖强制类型转换也能写出很好的C++程序。这个建议在如何看待reinterpret_cast的使用时非常重要。此类强制类型转换总是非常危险的。相似地,使用const_cast也总是预示着设计缺陷。设计合理的系统应不需要使用强制类型转换抛弃const特性。其他的强制类型转换,如static_cast和dynamic_cast,各有个的用途,但都不应当频繁使用。每次使用强制转换前,程序员应该仔细考虑是否还有其他不同的方法可以达到同一目的。如果非强制转换不可,则应限制强制转换值的作用域,并且记录所有假定涉及的类型,这样能减少错误发生的机会。