博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
显示转换static_cast、const_cast、reinterpret_cast、dynamic_cast详解
阅读量:4124 次
发布时间:2019-05-25

本文共 6776 字,大约阅读时间需要 22 分钟。

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
(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
(pb); Derived *pd2 = dynamic_cast
(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
(pd1); //compile error Derived2 *pd2 = dynamic_cast
(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(b1);b2. m_iNum = 200; //fine}
上面的代码编译时会报错,因为b1是一个常量对象,不能对它进行改变;使用const_cast把它转换成一个常量对象,就可以对它的数据成员任意改变。注意:b1和b2是两个不同的对象。

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

#include 
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
(&b1); // down-casting 不安全 Base* pb = static_cast
(&d1); // up-casting 安全 Derived& d = static_cast
(b1); // down-casting 不安全 Base& b = static_cast
(d1); // up-casting 安全 aInt = static_cast
(aFloat); // 基本数据类型转换 void* sth = static_cast
(&aDouble); // 将double指针类型转换成void指针类型 double* bDouble = static_cast
(sth); // 将void指针类型转换成double指针类型 cout << *bDouble << endl; Base* pb1 = dynamic_cast
(&d1); //Derived* pd1 = dynamic_cast
(&b1); // 编译时有warning,运行时出错 int bInt = reinterpret_cast
(pb1); // 将地址或指针转换成整数 cout << bInt << endl; pb1 = reinterpret_cast
(bInt); // 将整数转换成地址或指针 int* cInt = reinterpret_cast
(&aFloat); // 这个转换的结果会出乎意料 cout << (int)*cInt << endl; const Base* bBase = new Base(); Base* cBase = const_cast
(bBase); //Base* dBase = dynamic_cast
(bBase); // 不能通过编译 //Base* eBase = static_cast
(bBase); // 不能通过编译 //Base* fBase = reinterpret_cast
(bBase); // 不能通过编译 return 0;}

转载地址:http://hzopi.baihongyu.com/

你可能感兴趣的文章
Jump Game 动态规划
查看>>
Binary Tree Maximum Path Sum 自底向上求解(重重重重)
查看>>
Subsets 深搜
查看>>
Subsets II
查看>>
Edit Distance 字符串距离(重重)
查看>>
Gray Code 格雷码
查看>>
对话周鸿袆:从程序员创业谈起
查看>>
web.py 0.3 新手指南 - 如何用Gmail发送邮件
查看>>
web.py 0.3 新手指南 - RESTful doctesting using app.request
查看>>
web.py 0.3 新手指南 - 使用db.query进行高级数据库查询
查看>>
web.py 0.3 新手指南 - 多数据库使用
查看>>
一步步开发 Spring MVC 应用
查看>>
python: extend (扩展) 与 append (追加) 的差别
查看>>
「译」在 python 中,如果 x 是 list,为什么 x += "ha" 可以运行,而 x = x + "ha" 却抛出异常呢?...
查看>>
浅谈JavaScript的语言特性
查看>>
LeetCode第39题思悟——组合总和(combination-sum)
查看>>
LeetCode第43题思悟——字符串相乘(multiply-strings)
查看>>
LeetCode第44题思悟——通配符匹配(wildcard-matching)
查看>>
LeetCode第45题思悟——跳跃游戏(jump-game-ii)
查看>>
LeetCode第46题思悟——全排列(permutations)
查看>>