显示转换static_cast、const_cast、reinterpret_cast、dynamic_cast详解

来源:互联网 发布:linux系统添加用户组 编辑:程序博客网 时间:2024/06/06 04:16
1)  static_cast——静态类型检查
用法:static_cast <typeid> (expression)
说明:该运算符把expression转换为typeid类型,编译时会做类型检查,但没有运行时类型检查来确保转换的安全性。
来源:为什么需要static_cast强制转换?
情况1:void指针->其他类型指针
情况2:改变通常的标准转换
情况3:避免出现可能多种转换的歧义

用途:
a)用于类层次结构中基类和子类之间指针或引用的转换。进行上行转换(把子类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成子类指针或引用)时,由于没有动态类型检查,所以是不安全的。
b)用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
c)把void指针转换成目标类型的指针(不安全!!)。
d)把任何类型的表达式转换成void类型。
注意:static_cast不能转换掉 expression 的 const、volitale、或者__unaligned属性。

2)dynamic_cast
用法:dynamic_cast <typeid> (expression)
说明:该运算符把expression转换成typeid类型的对象。typeid必须是类的指针、类的引用或者void*。如果typeid是类的指针类型,那么expression也必须是指针,如果typeid是一个引用,那么expression也必须是一个引用。一般情况下,dynamic_cast用于具有多态性的类(即有虚函数的类)的类型转换。
dynamic_cast依赖于RTTI(Run-Time Type Information)信息,其次,在转换时,dynamic_cast会检查转换的source对象是否真的可以转换成target类型,这种检查不是语法上的,而是真实情况的检查。先看RTTI相关部分,通常,许多编译器都是通过vtable找到对象的RTTI信息的,这也就意味着,如果基类没有虚方法,也就无法判断一个基类指针变量所指对象的真实类型,这时候,dynamic_cast只能用来做安全的转换,例如从派生类指针转换成基类指针。而这种转换其实并不需要dynamic_cast参与。也就是说,dynamic_cast是根据RTTI记载的信息来判断类型转换是否合法的。
用途:主要用于类层次之间的up-casting和down-casting,还可以用于类之间的交叉转换。在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。检测在运行时进行。如果被转换的指针不是一个被请求的有效完整的对象指针,返回值为NULL。当用于多态类型时,它允许任意的隐式类型转换以及相反过程。不过,与static_cast不同,在后一种情况里(注:即隐式转 换的相反过程),dynamic_cast会检查操作是否有效。也就是说,它会检查转换是否会返回一个被请求的有效的完整对象。
注意:dynamic_cast不能转换掉expression的const、volitale或者__unaligned属性。
来源:为什么需要dynamic_cast强制转换?
简单的说,当无法使用virtual函数的时候。


典型案例:
Wicrosoft公司提供给我们一个类库,其中提供一个类Employee.以头文件Eemployee.h和类库.lib分发给用户,显然我们并无法得到类的实现的源代码。
//Emplyee.hclass Employee {public:    virtual int salary();};class Manager : public Employee{public:     int salary();};class Programmer : public Employee{public:    int salary();};

我们公司在开发的时候建立有如下类:
class MyCompany{public:    void payroll(Employee *pe);    //};void MyCompany::payroll(Employee *pe){    //do something}

但是开发到后期,我们希望能增加一个bonus()的成员函数到W$公司提供的类层次中。
假设我们知道源代码的情况下,很简单,增加虚函数:
//Emplyee.hclass Employee {public:    virtual int salary();    virtual int bonus();};class Manager : public Employee{public:     int salary();};class Programmer : public Employee{public:    int salary();    int bonus();};//Emplyee.cppint Programmer::bonus(){    //}

payroll()通过多态来调用bonus():

class MyCompany{public:    void payroll(Employee *pe);    //};void MyCompany::payroll(Employee *pe){    //do something    //pe->bonus();}

但是现在情况是,我们并不能修改源代码,怎么办?dynamic_cast华丽登场了!
在Employee.h中增加bonus()声明,在另一个地方定义此函数,修改调用函数payroll().重新编译,ok。
//Emplyee.hclass Employee {public:    virtual int salary();};class Manager : public Employee{public:     int salary();};class Programmer : public Employee{public:    int salary();    int bonus();//直接在这里扩展};//somewhere.cppint Programmer::bonus(){    //define}

class MyCompany{public:    void payroll(Employee *pe);    //};void MyCompany::payroll(Employee *pe){    Programmer *pm = dynamic_cast<Programmer *>(pe);        //如果pe实际指向一个Programmer对象,dynamic_cast成功,并且开始指向Programmer对象起始处    if(pm)    {        //call Programmer::bonus()    }    //如果pe不是实际指向Programmer对象,dynamic_cast失败,并且pm = 0    else    {        //use Employee member functions    }}

dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

class Base{public:    int m_iNum;    virtual void foo();};class Derived:public Base{public:    char *m_szName[100];};void func(Base *pb){    Derived *pd1 = static_cast<Derived *>(pb);    Derived *pd2 = dynamic_cast<Derived *>(pb);}

在上面的代码段中,
如果pb实际指向一个Derived类型的对象,pd1和pd2是一样的,并且对这两个指针执行Derived类型的任何操作都是安全的;
如果pb实际指向的是一个Base类型的对象,那么pd1将是一个指向该对象的指针,对它进行Derived类型的操作将是不安全的(如访问m_szName),而pd2将是一个空指针(即0,因为dynamic_cast失败)。
另外要注意:Base要有虚函数,否则会编译出错;static_cast则没有这个限制。这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(关于虚函数表的概念,详细可见<Inside c++ object model>)中,只有定义了虚函数的类才有虚函数表,没有定义虚函数的类是没有虚函数表的。

另外,dynamic_cast还支持交叉转换(cross cast)。如下代码所示。
class Base{public:    int m_iNum;    virtual void f(){}};class Derived1 : public Base{};class Derived2 : public Base{};void foo(){    derived1 *pd1 = new Drived1;    pd1->m_iNum = 100;    Derived2 *pd2 = static_cast<Derived2 *>(pd1); //compile error    Derived2 *pd2 = dynamic_cast<Derived2 *>(pd1); //pd2 is NULL    delete pd1;}
在函数foo中,使用static_cast进行转换是不被允许的,将在编译时出错;而使用 dynamic_cast的转换则是允许的,结果是空指针。

3) reinterpret_cast
用法:reinterpret_cast <typeid>(expression)
说明:转换一个指针为其他类型的指针,也允许将一个指针转换为整数类型,反之亦然。这个操作符能够在非相关的类型之间进行转换。操作结果只是简单的从一个指针到别的指针的值的二进制拷贝,在类型之间指向的内容不做任何类型的检查和转换。这是一个强制转换。使用时有很大的风险,慎用之。
注意:reinterpret _cast不能转换掉expression的const、volitale或者__unaligned属性。

4)const_cast
用法:const_cast<typeid>(expression)
说明:这个类型操纵传递对象的const属性,或者是设置或者是移除。如:
Class C{…}
const C* a = new C;
C* b = const_cast<C*>(a);
如果将上面的const_cast转换成其他任何其他的转换,编译都不能通过,出错的信心大致如下:

“…cannot convert from 'const class C *' to 'class C *'”。

常量指针被转化成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。

//Voiatile和const类试。举如下一例:class B{public:int m_iNum;}void foo(){const B b1;b1.m_iNum = 100; //comile errorB b2 = const_cast<B>(b1);b2. m_iNum = 200; //fine}
上面的代码编译时会报错,因为b1是一个常量对象,不能对它进行改变;使用const_cast把它转换成一个常量对象,就可以对它的数据成员任意改变。注意:b1和b2是两个不同的对象。

四种cast方法的典型用法示例:

#include <iostream>using namespace std;class Base{public:         int _base;         virtual void printinfo()         {              cout << _base << endl;         }};class Derived : public Base     {     public:         int _derived;         virtual void printinfo()         {              cout << _derived << endl;         }     };     int main(void)     {         Base b1;         Derived d1;         int aInt = 10;         long aLong = 11;         float aFloat = 11.11f;         double aDouble = 12.12;         Derived* pd = static_cast<Derived*>(&b1);                           // down-casting          不安全         Base* pb = static_cast<Base*>(&d1);                                 // up-casting             安全         Derived& d = static_cast<Derived&>(b1);                             // down-casting          不安全         Base& b = static_cast<Base&>(d1);                                   // up-casting             安全         aInt = static_cast<int>(aFloat);                                    // 基本数据类型转换         void* sth = static_cast<void*>(&aDouble);                           // 将double指针类型转换成void指针类型         double* bDouble = static_cast<double*>(sth);                    // 将void指针类型转换成double指针类型         cout << *bDouble << endl;         Base* pb1 = dynamic_cast<Base*>(&d1);         //Derived* pd1 = dynamic_cast<Derived*>(&b1);                   // 编译时有warning,运行时出错         int bInt = reinterpret_cast<int>(pb1);                                    // 将地址或指针转换成整数         cout << bInt << endl;         pb1 = reinterpret_cast<Base*>(bInt);                                      // 将整数转换成地址或指针         int* cInt = reinterpret_cast<int*>(&aFloat);                              // 这个转换的结果会出乎意料         cout << (int)*cInt << endl;         const Base* bBase = new Base();         Base* cBase = const_cast<Base*>(bBase);         //Base* dBase = dynamic_cast<Base*>(bBase);                 // 不能通过编译         //Base* eBase = static_cast<Base*>(bBase);                  // 不能通过编译         //Base* fBase = reinterpret_cast<Base*>(bBase);             // 不能通过编译         return 0;}     









0 0
原创粉丝点击