C++ - static_cast/dynamic_cast/reinterpret_cast/const_cast

时间:2022-09-11 12:10:46

thanks:http://www.cnblogs.com/chio/archive/2007/07/18/822389.html

回顾一下C++类型转换:

C++类型转换分为:隐式类型转换和显式类型转换

第1部分. 隐式类型转换

又称为“标准转换”,包括以下几种情况:
1) 算术转换(Arithmetic conversion) : 在混合类型的 算术表达式 中, 最宽的数据类型成为目标转换类型。
int  ival  =   3 ;
double  dval  =   3.14159 ;

ival 
+  dval; // ival被提升为double类型

2)一种类型表达式赋值给另一种类型的对象:目标类型是被赋值对象的类型

C++ - static_cast/dynamic_cast/reinterpret_cast/const_castint   * pi  =   0 //  0被转化为int *类型
C++ - static_cast/dynamic_cast/reinterpret_cast/const_cast
ival  =  dval;  //  double->int
C++ - static_cast/dynamic_cast/reinterpret_cast/const_cast

例外:void指针赋值给其他指定类型指针时,不存在标准转换,编译出错

3)将一个表达式作为实参传递给函数调用,此时形参和实参类型不一致:目标转换类型为形参的类型

extern   double  sqrt( double );

cout 
<<   " The square root of 2 is  "   <<  sqrt( 2 <<  endl;
// 2被提升为double类型:2.0

4)从一个函数返回一个表达式,表达式类型与返回类型不一致:目标转换类型为函数的返回类型

double  difference( int  ival1,  int  ival2)
{
    
return  ival1  -  ival2;
    
// 返回值被提升为double类型
}


第2部分. 显式类型转换

被称为“强制类型转换”(cast)
C     风格: (type-id)
C++风格:  static_cast dynamic_cast reinterpret_cast 、和 const_cast..
 
关于强制类型转换的问题,很多书都讨论过,写的最详细的是C++ 之父的《C++ 的设计和演化》。最好的解决方法就是不要使用C风格的强制类型转换,而是使用标准C++的类型转换符:static_cast, dynamic_cast。标准C++中有四个类型转换符: static_castdynamic_castreinterpret_cast、和 const_cast。下面对它们一一进行介绍。

static_cast

用法: static_cast < type-id > ( expression )


说明:该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。

来源:为什么需要static_cast强制转换?
情况1:void指针->其他类型指针
情况2:改变通常的标准转换
情况3:避免出现可能多种转换的歧义



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

dynamic_cast

用法: dynamic_cast < type-id > ( expression )

说明:该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *;如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。



thanks:http://blog.csdn.net/insistgogo/article/details/8941513

dynamic_cast在什么时候使用:

使用时间:子类和父类之间的多态类型转换

引入dynamic_cast的原因:

举例说明:

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class Base  
  5. {  
  6. public:  
  7.     virtual void BaseAAA()  
  8.     {  
  9.         cout<<"BaseAAA"<<endl;  
  10.     }  
  11.   
  12.     virtual void AAA()  
  13.     {  
  14.         cout<<"Base::VirtualAAA"<<endl;  
  15.     }  
  16. };  
  17.   
  18. class Derived : public Base  
  19. {  
  20. public:  
  21.     void DerivedAAA()  
  22.     {  
  23.         cout<<"DerivedAAA"<<endl;  
  24.     }  
  25.   
  26.     void AAA()  
  27.     {  
  28.         cout<<"Derived::VirtualAAA"<<endl;  
  29.     }  
  30. };  
  31.   
  32.   
  33. int main()  
  34. {  
  35.     //为了实现虚函数的调用,让基类指针指向了派生类对象。  
  36.     Base* pBaseOne = new Derived;  
  37.   
  38.     //使用基类指针pBaseOne,可以访问虚函数,实现多态  
  39.     pBaseOne->AAA();  //输出Derived::VirtualAAA  
  40.       
  41.     //但是问题来了,让基类指针指向派生类后,基类指针无法访问派生类的成员了。  
  42.     //pBaseOne->DerivedAAA();//操作失败  
  43.   
  44.     //这时需要对这个基类指针进行强制类型转换,获得派生类对象,之后操作派生类自己的成员  
  45.     Derived* pDerived = dynamic_cast<Derived*>(pBaseOne);  
  46.     pDerived->DerivedAAA(); //输出DerivedAAA  
  47.   
  48.     system("pause");  
  49.     return 1;  
  50. }  

具体来说:

为了实现多态,让基类指针指向了派生类对象,但是这个基类指针无法操作派生类自己的成员了,所以就想把这个指针由基类指针转换为派生类指针,此时就可以使用dynamic_cast。即,已经有了派生类指针转换为基类指针。之后,使用dynamic_cast,把基类指针转换派生类指针。

dynamic_cast的作用:

作用:将基类类型的指针或引用安全地转换为派生类型的指针或引用。

注意事项:

(0)在dynamic_cast中,dynamic表示在类型转换时,是在运行时进行的,其与Static对应。

(1)使用dynamic_cast转换时,在expression中必有虚函数,否则编译会出错。而Static_cast没有这个限制。但是即使原来的参数和目标参数之间没有继承关系时,编译器不会报错。

原因:在类中存在虚函数时,才会有可能出现让基类指针或引用指向派生类对象的情况(虚函数使用的前提),此时转换才有意义。或者说,dynamic_cast操作时专门针对由虚函数的继承来的,它将基类指针转换为想要的子类指针,以做好操作子类中有而父类中没有的操作。由于,判断类是否有继承关系,是需要检测是否有虚函数,即在原来的参数和目标参数之间没有继承关系时,编译器不会报错,因为它由虚函数。

dynamic_cast的语法:

  1. dynamic_cast<type-id>(expression)  

说明:

(1) type_id 和 目标类型是一样的

(2) type_id 和 目标类型是引用或指针。

举例:

  1. Derived* pDerived = dynamic_cast<Derived*>(pBaseOne);//成功,返回Derived的指针,失败返回NULL  
  2. Base& BaseClass = dynamic_cast<Base&>(DerivedClass); //成功,返回Base的引用,失败抛出bad_cast异常  

使用dynamic_cast转换时,会执行两个操作:

(1)它首先验证被请求的转换是否有效,这是在编译时判断,检测expression代表的类中是否有虚函数。

(2)对操作数进行类型转换。这是在运行是判断。

dynamic_cast的分类:

dynamic_cast要求要转换的两个值是有关系的,如果转型成功,则返回转换过的指针,如果转换不成功,返回NULL。

根据类之间的关系,可以分三种情况:

(1)上行转换:一个子类对象的指针/引用转换为基类的指针/引用(子类到基类的转换)

(2)下行转换基类的指针/引用转换为一个子类的指针/引用(基类到子类的转换)

(3)交叉转换:多个基类,不同子类之间的相互转换

下面分情况说明这三种情况:

(1)上行转换:一个子类对象的指针/引用转换为基类的指针/引用(子类到基类的转换)

说明:这里dynamic_cast和static_cast以及直接赋值的效果是一样的。

举例:

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class Base  
  5. {  
  6. public:  
  7.     virtual void BaseAAA()  
  8.     {  
  9.         cout<<"BaseAAA"<<endl;  
  10.     }  
  11.   
  12.     virtual void AAA()  
  13.     {  
  14.         cout<<"BaseAAA::VirtualAAA"<<endl;  
  15.     }  
  16. };  
  17.   
  18. class Derived : public Base  
  19. {  
  20. public:  
  21.     virtual void DerivedAAA()  
  22.     {  
  23.         cout<<"DerivedAAA"<<endl;  
  24.     }  
  25.   
  26.     virtual void AAA()  
  27.     {  
  28.         cout<<"Derived::VirtualAAA"<<endl;  
  29.     }  
  30. };  
  31.   
  32.   
  33.   
  34. int main()  
  35. {  
  36.     Derived* pD = new Derived;  
  37.       
  38.     //正常情况-dynamic_cast  
  39.     Base* pB = dynamic_cast<Derived*>(pD);  
  40.     pB->BaseAAA(); //输出BaseAAA  
  41.     pB->AAA(); //输出Derived::VirtualAAA  
  42.   
  43.     //正常情况-static_cast  
  44.     Base* pBB = static_cast<Derived*>(pD);  
  45.     pBB->BaseAAA(); //输出BaseAAA  
  46.     pBB->AAA(); //输出Derived::VirtualAAA  
  47.   
  48.     //不要使用转换  
  49.     Base* pBBB = pD;  
  50.     pBBB->BaseAAA(); //输出BaseAAA  
  51.     pBBB->AAA(); //输出Derived::VirtualAAA  
  52.   
  53.     system("pause");  
  54.     return 1;  
  55. }  

(2)下行转换基类的指针/引用转换为一个子类的指针/引用(基类到子类的转换

说明:这是dynamic_cast的特长。比static_cast安全。

原因:dynamic_cast能够在运行是类型进行检查如果绑定到引用或指针的对象不是目标类型的对象,则dynamic_cast 失败。

dynamic_cast在什么时候能成功?

如果基类指针或引用本来原来是指向或引用一个派生类的情况下,会成功。

失败后的行为:

使用dynamic_cast转换:

如果,基类指针或引用的值在转换前就是指向或引用派生类的情况下,则在转换后,派生类指针或引用就重新获得了一个派生类指针或引用。

如果,基类指针或引用的值在转换前就是指向或引用基类的情况下,在语句执行时,会立即出现错误,并返回不同的状态。

dynamic_cast失败后的状态:

如果,待转换的参数为指针类型,则返回NULL。

如果,待转换的参数为引用类型,则抛出一个bad_cast 类型的异常。

使用static_cast转换:

如果,基类指针或引用的值在转换前就是指向或引用派生类的情况下,则在转换后,派生类指针或引用就重新获得了一个派生类指针或引用。

如果,基类指针或引用的值在转换前就是指向或引用基类的情况下,在语句执行时,不会立即出现错误,而是会在使用的时候才会出现错误。

static_cast失败的情况:

如果,使用转换后的指针或引用调用了派生类的成员,则会报错。

如果,仍然调用基类(自己)的成员,则不会报错。即会隐含错误。

举例:

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class Base  
  5. {  
  6. public:  
  7.     virtual void BaseAAA()  
  8.     {  
  9.         cout<<"BaseAAA"<<endl;  
  10.     }  
  11.   
  12.     virtual void AAA()  
  13.     {  
  14.         cout<<"BaseAAA::VirtualAAA"<<endl;  
  15.     }  
  16. };  
  17.   
  18. class Derived : public Base  
  19. {  
  20. public:  
  21.     virtual void DerivedAAA()  
  22.     {  
  23.         cout<<"DerivedAAA"<<endl;  
  24.     }  
  25.   
  26.     virtual void AAA()  
  27.     {  
  28.         cout<<"Derived::VirtualAAA"<<endl;  
  29.     }  
  30. };  
  31. int main()  
  32. {  
  33.     Derived* pD = NULL;  
  34.     Base* pB = new Derived;  
  35.   
  36.     //正常情况-dynamic_cast  
  37.     pD = dynamic_cast<Derived*>(pB);  
  38.     pD->DerivedAAA(); //输出DerivedAAA  
  39.     pD->AAA(); //输出Derived::VirtualAAA  
  40.   
  41.     //正常情况-static_cast  
  42.     Derived* pD1 = static_cast<Derived*>(pB);  
  43.     pD1->DerivedAAA(); //输出DerivedAAA  
  44.     pD1->AAA(); //输出Derived::VirtualAAA  
  45.   
  46.       
  47.     Base* pBB = new Base;  
  48.       
  49.     //失败情况  
  50.     Derived* pDD = dynamic_cast<Derived*>(pBB);//此时pDD返回NULL  
  51.     //pDD->DerivedAAA(); //出错  
  52.     //pDD->AAA(); //出错  
  53.   
  54.     //失败情况-static_cast  
  55.     Derived* pDD1 = static_cast<Derived*>(pBB);  
  56.     //pDD1->DerivedAAA(); //出错-用到不是自己的成员时,且该成员是虚函数时才报错。  
  57.     pDD1->AAA(); //输出Base::VirtualAAA   
  58.   
  59.     system("pause");  
  60.     return 1;  
  61. }  
B要有虚函数,否则会编译出错;static_cast则没有这个限制。

B中需要检测有虚函数的原因:类中存在虚函数,就说明它有想要让基类指针或引用指向派生类对象的情况,此时转换才有意义。
这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(关于虚函数表的概念,详细可见<Inside c++ object model>)中,只有定义了虚函数的类才有虚函数表,没有定义虚函数的类是没有虚函数表的。

(3)交叉转换多个基类,不同子类之间的相互转换

说明:dynamic_cast可以做,但是static_cast不可以做,编译都通不过。

举例:

C++ - static_cast/dynamic_cast/reinterpret_cast/const_cast

dynamic_cast在什么时候能成功转换?

如果一个派生类DerivedTwo是由两个基类BaseOne和BaseTwo派生。

而且把DerivedTwo对象的指针给了其中一个基类BaseOne,那么可以对BaseOne的指针进行dynamic_cast转换,转换为BaseTwo的指针。

  1. BaseOne* pBaseOne = new DerivedTwo;  
  2. BaseTwo* pBaseTwo = dynamic_cast<BaseTwo*>(pBaseOne);  
  3. pBaseTwo->AAA();  //正确执行  
  4. pBaseTwo->BaseTwoAAA(); //正确执行  

为什么dynamic_cast能成功转换?

这是由于在定义指针pBaseOne的指向时,其指向的内容本来就包含两个基类的内容,所以可以转换成功。

给出不成功的情况:

  1. BaseOne* pBaseOne = new DerivedOne;  
  2. BaseTwo* pBaseTwo = dynamic_cast<BaseTwo*>(pBaseOne);  
  3. pBaseTwo->AAA();//报错  
  4. pBaseTwo->BaseTwoAAA();//报错  

dynamic_cast不能转换的原因?

这是由于在定义指针pBaseOne的指向时,其指向的内容只包含一个基类BaseOne的内容,不含有BaseTwo的内容,所以不能成功,此时pBaseTwo的值为NULL。

举例:

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class BaseOne  
  5. {  
  6. public:  
  7.     virtual void BaseOneAAA()  
  8.     {  
  9.         cout<<"BaseOneAAA"<<endl;  
  10.     }  
  11.   
  12.     virtual void AAA()  
  13.     {  
  14.         cout<<"BaseOne::VirtualAAA"<<endl;  
  15.     }  
  16. };  
  17.   
  18. class BaseTwo  
  19. {  
  20. public:  
  21.     virtual void BaseTwoAAA()  
  22.     {  
  23.         cout<<"BaseTwoAAA"<<endl;  
  24.     }  
  25.   
  26.     virtual void AAA()  
  27.     {  
  28.         cout<<"BaseTwo::VirtualAAA"<<endl;  
  29.     }  
  30. };  
  31.   
  32. class DerivedOne : public BaseOne  
  33. {  
  34. public:  
  35.     virtual void DerivedOneAAA()  
  36.     {  
  37.         cout<<"DerivedOneAAA"<<endl;  
  38.     }  
  39.   
  40.     void AAA()  
  41.     {  
  42.         cout<<"DerivedOne::VirtualAAA"<<endl;  
  43.     }  
  44. };  
  45.   
  46. class DerivedTwo : public BaseOne,public BaseTwo  
  47. {  
  48. public:  
  49.     virtual void DerivedTwoAAA()  
  50.     {  
  51.         cout<<"DerivedTwoAAA"<<endl;  
  52.     }  
  53.   
  54.     void AAA()  
  55.     {  
  56.         cout<<"DerivedTwo::VirtualAAA"<<endl;  
  57.     }  
  58. };  
  59. int main()  
  60. {  
  61.     BaseOne* pBaseOne = NULL;  
  62.     BaseTwo* pBaseTwo = NULL;  
  63.   
  64.     //dynamic_cast转换成功的情况  
  65.     pBaseOne = new DerivedTwo;  
  66.     pBaseTwo = dynamic_cast<BaseTwo*>(pBaseOne);  
  67.     pBaseTwo->AAA();  //输出DerivedTwo::VirtualAAA  
  68.     pBaseTwo->BaseTwoAAA(); //输出BaseTwoAAA  
  69.   
  70.     //dynamic_cast转换失败的情况  
  71.     pBaseOne = new DerivedOne;  
  72.     pBaseTwo = dynamic_cast<BaseTwo*>(pBaseOne);  
  73.     pBaseTwo->AAA();  
  74.     pBaseTwo->BaseTwoAAA();  
  75.   
  76.     ////static_cast转换失败的情况  
  77.     ////直接是编译不通过:static_cast无法实现BaseOne* 到BaseTwo* 的转换。  
  78.   
  79.     //pBaseOne = new DerivedOne;  
  80.     //pBaseTwo = static_cast<BaseTwo*>(pBaseOne);  
  81.     //pBaseTwo->AAA();  
  82.     //pBaseTwo->BaseTwoAAA();  
  83.   
  84.     //pBaseOne = new DerivedTwo;  
  85.     //pBaseTwo = static_cast<BaseTwo*>(pBaseOne);  
  86.     //pBaseTwo->AAA();   
  87.     //pBaseTwo->BaseTwoAAA();   
  88.   
  89.   
  90.     system("pause");  
  91.     return 1;  
  92. }  


源自:http://blog.csdn.net/ss0429/article/details/8929341

dynamic_cast <new_type> (expression) 动态转换
 动态转换确保类指针的转换是合适完整的,它有两个重要的约束条件,

其一是要求new_type为指针或引用,其二是下行转换时要求基类是多态的(基类中包含至少一个虚函数)。
看一下下面的例子:

[cpp]  view plain copy
  1. #include <iostream>  
  2. using namespace std;  
  3. class CBase { };  
  4. class CDerived: public CBase { };  
  5.   
  6. int main() {  
  7.     CBase b; CBase* pb;  
  8.     CDerived d; CDerived* pd;  
  9.   
  10.     pb = dynamic_cast<CBase*>(&d);     // ok: derived-to-base  
  11.     pd = dynamic_cast<CDerived*>(&b);  // wrong: base-to-derived   
  12. }  

在最后一行代码有问题,编译器给的错误提示如下图所示:

 C++ - static_cast/dynamic_cast/reinterpret_cast/const_cast

把类的定义改成:
class CBase { virtual void dummy() {} };
class CDerived: public CBase {};
再编译,结果如下图所示:

C++ - static_cast/dynamic_cast/reinterpret_cast/const_cast

编译可以顺利通过了,然后我们在main函数的最后添加两句话:
cout << pb << endl;
cout << pd << endl;
输出pb和pd的指针值,结果如下:

 C++ - static_cast/dynamic_cast/reinterpret_cast/const_cast

我们看到一个奇怪的现象,将父类经过dynamic_cast转成子类的指针竟然是空指针!这正是dynamic_cast提升安全性的功能,dynamic_cast可以识别出不安全的下行转换,但并不抛出异常,而是将转换的结果设置成null(空指针)。
再举一个例子:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <exception>  
  3. using namespace std;  
  4.   
  5. class CBase { virtual void dummy() {} };  
  6. class CDerived: public CBase { int a; };  
  7.   
  8. int main () {  
  9.     try {  
  10.         CBase * pba = new CDerived;  
  11.         CBase * pbb = new CBase;  
  12.         CDerived * pd;  
  13.   
  14.         pd = dynamic_cast<CDerived*>(pba);  
  15.         if (pd==0) cout << "Null pointer on first type-cast" << endl;  
  16.   
  17.         pd = dynamic_cast<CDerived*>(pbb);  
  18.         if (pd==0) cout << "Null pointer on second type-cast" << endl;  
  19.   
  20.     } catch (exception& e) {cout << "Exception: " << e.what();}  
  21.     return 0;  
  22. }  

输出结果是:Null pointer on second type-cast
两个dynamic_cast都是下行转换,第一个转换是安全的,因为指向对象的本质是子类,转换的结果使子类指针指向子类,天经地义;

第二个转换是不安全的,因为指向对象的本质是父类,“指鹿为马”或指向不存在的空间很可能发生!
最后补充一个特殊情况,当待转换指针是void*或者转换目标指针是void*时,dynamic_cast总是认为是安全的,举个例子:

[cpp]  view plain copy
  1. #include <iostream>  
  2. using namespace std;  
  3. class A {virtual void f(){}};  
  4. class B {virtual void f(){}};  
  5.   
  6. int main() {  
  7.     A* pa = new A;  
  8.     B* pb = new B;  
  9.     void* pv = dynamic_cast<void*>(pa);  
  10.     cout << pv << endl;  
  11.     // pv now points to an object of type A  
  12.   
  13.     pv = dynamic_cast<void*>(pb);  
  14.     cout << pv << endl;  
  15.     // pv now points to an object of type B  
  16. }  

运行结果如下:

C++ - static_cast/dynamic_cast/reinterpret_cast/const_cast
可见dynamic_cast认为空指针的转换安全的,但这里类A和类B必须是多态的,包含虚函数,若不是,则会编译报错。

reinterpret_cast <new_type> (expression) 重解释转换
这个转换是最“不安全”的,两个没有任何关系的类指针之间转换都可以用这个转换实现,举个例子:

[cpp]  view plain copy
  1. class A {};  
  2. class B {};  
  3. A *a = new A;  
  4. B *b = reinterpret_cast<B*>(a);//correct!  

更厉害的是,reinterpret_cast可以把整型数转换成地址(指针),这种转换在系统底层的操作,有极强的平台依赖性,移植性不好。
它同样要求new_type是指针引或用,下面的例子是通不过编译的:

[cpp]  view plain copy
  1. double a=2000.3;  
  2. short b;  
  3. b = reinterpret_cast<short> (a); //compile error!  

const_cast <new_type> (expression) 常量向非常量转换

这个转换好理解,可以将常量转成非常量。

[cpp]  view plain copy
  1. // const_cast  
  2. #include <iostream>  
  3. using namespace std;  
  4.   
  5. void print (char * str) {  
  6.     cout << str << endl;  
  7. }  
  8.   
  9. int main () {  
  10.     const char * c = "sample text";  
  11.     char *cc = const_cast<char *> (c) ;  
  12.     Print(cc);  
  13.     return 0;  
  14. }  

char *cc = const_cast<char *>(c)可以看出了这个转换的作用了,但切记,

这个转换并不转换原常量本身,即c还是常量,只是它返回的结果cc是非常量了

总结
        C风格转换是“万能的转换”,但需要程序员把握转换的安全性,编译器无能为力;static_cast最接近于C风格转换,但在无关类指针转换时,编译器会报错,提升了安全性;dynamic_cast要求转换类型必须是指针或引用,且在下行转换时要求基类是多态的,如果发现下行转换不安全,dynamic_cast返回一个null指针,dynamic_cast总是认为void*之间的转换是安全的;reinterpret_cast同样要求转换类型必须是指针或引用,可以对无关类指针进行转换,甚至可以直接将整型值转成指针,这种转换是底层的,有较强的平台依赖性,可移植性差;const_cast可以将常量转成非常量,但不会破坏原常量的const属性,只是返回一个去掉const的变量。