将一种类型转换为另一种类型,转换后的变量,它的内部数据存储方式也随之发生改变,c++有四个显示的类型转换函数,reinterpret_cast,const_cast,static_cast,dynamic_cast.前面三个是在编译期间实现转换的,最后一个是在运行时实现转换,还可返回转换成功与否标志。
转换函数的原型如下:
reinterpret_cast<new_type>(expression)
const_cast<new_type>(expression)
static_cast<new_type>(expression)
dynamic_cast<new_type>(expression)
将expression表达式的值转换为new_type类型的值。
static_cast:
用于基本类型和具有继承关系的类型之间的转换。会改变变量的内部表示方式。继承类和基类的指针可以相互转换,继承类可转换为基类,不可转化基本类型的指针,基类不可转换为继承类。下面分别谈谈static_cast的几种常见用法。
①用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。
例如:
- class A{};
- class B:public A
- {};
- /*类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。*/
- A * pointer_a;
- B b;
- pointer_a=static_cast<A *>(&b);
②用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
例如:
- /*基本类型之间的强制转化*/
- int i;
- float f=1.2f;
- i=static_cast<int>(f);
③把空指针转换成目标类型的空指针。
例如:
- /*把空指针转换成目标类型的空指针*/
- void * vp;
- char * cp=static_cast<char *>(vp);
reinterpret_cast:
将一个类型的指针转换为另一个类型的指针,不修改指针变量值数据存放格式,只在编译时重新解释指针的类型。不能用于非指针类型的转换。还可将一个指针值转换为整型数。即expression必须是个指针类型。不能将const指针转换为void*指针。
例如:
- /*reinterpret_cast主要是将数据从一种指针类型的转换为另一种指针类型。*/
- char * cp;
- int * ip;
- ip=reinterpret_cast<int *>(cp);
const_cast:
去除指针变量的常量属性,将它转换为一个对应指针类型的普通变量。也可将一个非常量的指针变量转换为一个常指针变量。不能将非指针的常量变为普通变量。下面分别谈谈const_cast的几种常见用法。
①常量指针被转化成非常量指针,并且仍然指向原来的对象;
例如:
- const char * const_cp;
- char * nonconst_cp=const_cast<char *>(const_cp);
②常量引用被转换成非常量引用,并且仍然指向原来的对象;
例如:
- const int const_var=10;
- int &var=const_cast<int&>(const_var);
③常量对象被转换成非常量对象。
例如:
- class A
- {
- public:
- int a;
- A(int value):a(value){}//构造函数
- };
- int main()
- {
- const A const_a(10);
- A & nonconst_a=const_cast<A &>(const_a);
- nonconst_a.a=20;
- cout<<"The value of const_a.a is: "<<const_a.a<<endl;
- return 0;
- }
注意:任何尝试通过const_cast去修改一个const常量值的操纵都是徒劳的。
例如:
- int a=34;
- const int * pcint=&a;
- int * pint=const_cast<int*>(pcint);
- *pint=0;
这样就修改了a的值(a==0),这里如果改为const int a=34;那么虽然编译依然能通过,运行依然OK,但是实际上当你用*pint来修改a的值时,a的值依然为34,而*pint确实为0了,更为有趣的是此时pint=&a依然成立,虽然pint指向a,但是取出它的值却不等于a了。
dynamic_cast:
只能在继承类对象的指针之间或引用之间进行转换。
vc++编译器默认禁止使用RTTI信息,若要使用dynamic_cast,需执行“工程-设置”在“c++”选项卡中,选择“c++language”,勾选“允许运行时类型信息RTTI”。
用法:dynamic_cast < type-id > ( expression )
该运算符把expression转换成type-id类型的对象。type-id必须是类的指针、类的引用或者void *; 如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。 在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的; 在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
例如:
- class B
- {
- public:
- int m_iNum;
- virtual void foo();
- };
- class D:public B
- {
- public:
- char *m_szName[100];
- };
- void func(B *pb)
- {
- D *pd1 = static_cast<D *>(pb);
- D *pd2 = dynamic_cast<D *>(pb);
- }
在上面的代码段中,如果pb指向一个D类型的对象,pd1和pd2是一样的,并且对这两个指针执行D类型的任何操作都是安全的;但是,如果pb指向的是一个B类型的对象,那么pd1将是一个指向该对象的指针,对它进行D类型的操作将是不安全的(如访问m_szName),而pd2将是一个空指针。另外要注意:B要有虚函数,否则会编译出错;static_cast则没有这个限制。B中需要检测有虚函数的原因:类中存在虚函数,就说明它有想要让基类指针或引用指向派生类对象的情况,此时转换才有意义。这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(关于虚函数表的概念,详细可见<Inside c++ object model>)中,只有定义了虚函数的类才有虚函数表,没有定义虚函数的类是没有虚函数表的。另外,dynamic_cast还支持交叉转换(cross cast)。如下代码所示。
- class A
- {
- public:
- int m_iNum;
- virtual void f(){}
- };
- class B:public A{};
- class D:public A{};
- void foo()
- {
- B *pb = new B;
- pb->m_iNum = 100;
- D *pd1 = static_cast<D *>(pb); //compile error
- D *pd2 = dynamic_cast<D *>(pb); //pd2 is NULL
- delete pb;
- }
在函数foo中,使用static_cast进行转换是不被允许的,将在编译时出错;而使用 dynamic_cast的转换则是允许的,结果是空指针。