码迷,mamicode.com
首页 > 其他好文 > 详细

dynamic_cast、static_cast、const_cast和reinterpret_cast的区别

时间:2016-05-06 15:34:25      阅读:197      评论:0      收藏:0      [点我收藏+]

标签:

           C++的类型转换分为两种:隐式转换和显示转换。
           一、 对于隐式转换,就是标准的转换,在很多时候,不经意间就发生了,例如int和float加法,int类型就会被隐式转换为float了,这种称为升级转换。还有就是把等号右边的值转换为左边的类型,再赋值。还有类变量作为参数传递给函数:
class B
{
	public:
		int num;
	B(int a)
	{
		num=a;
	}
}
B show(B b)
{
	return b;
}
show(5);//这里也发生了隐式转换,将5转换为B类型的(B)5;
B b=10;//也是OK的,发生了隐式转换,除非加上explicit;
二、显示转换:在C++中有四个类型转换符:static_cast 、dynamic_cast  、const_cast和 reinterpret_cast。

下面对他们--进行小小的总结:

1、static_cast(编译器在编译期处理)

        转换格式:static_cast<type-id>(expression)。

      将expression转换为type-id类型,type-id和expression必须是指针、引用、算术类型或枚举类型。一般的转换(no run-time check),不提供运行时的检查来确保转换的安全性。
主要在一下几个场合中使用:
(1).用于类层次结构中,基类和子类之间指针和引用的转换;
          当进行上行转换时:也就是把子类的指针或者引用转换为父类表示,这种转换时安全的;
          当进行下行转换时:也就是把父类的指针和引用转换成子类表示,这种转换时不安全的,需要程序员来保证;
(2)、用于基本数据之间的类型转换,例如int和float,char和int之间等,这种也是不安全的,也需要程序员的保证
(3)、把void转换为目标类型的指针,是及其不安全的;
注:static_cast不能转换掉expression为const、volatile、以及unaligned属性。
2、dynamic_cast(运行期间确定类型)

                  C程序员大概都喜欢用强制类型转换,它虽然优缺点,但是考虑到它的灵活我们还是用的不亦乐乎。大多数情况下我们都会转换成功,只有少量的情况下才会转换失败,失败了怎么办,这时候就用到了dynamic_cast了。这里的“少量情况”是这样的,如果有多继承的类对象,你在某些情况下要得到对象的指针,而你又将其转换为某种类型,但是由于C++对象类型的多态性(可以有多种类型),你又不确定一定会成功(运行的时候才确定其类型),此时可以利用dynamic_cast,充分利用C++的运行时检查机制。
例如:

class A{...}; 
class B:public A{...}; 
class C:public B{...}; 
void Fun1(B* pB) 
{ 
A* pA  = (A*)pB; 
C* pC  = (C*)pB; 
} 
       Fun1函数使用强制类型转换将pB转换为A*或C*,看出什么的问题了吗?
      如果这样调用Fun1:      Fun1(((B*)new C));的确不会有问题,

       但如果是这样呢:   Fun1(new B); 类型的转换成功,pC不会为NULL,因为强转总是会成功的,但是达不到我们的目的,因为当使用到pC指针时就程序就悲剧了,崩溃(具体解决办法后面有详细的分析).
       如果情况更糟:   Fun1((B*)0X00005678);   //0X00005678是一个随机值
pA,pC都不会是NULL,再说一遍强制类型转换总是能够成功的,重要的事情说两遍。但使用这两个指针时程序肯定崩溃.当然你可以使用异常处理机制来处理这样的错误,不过这有点大才小用的感觉,最好能够找到一种能够检查出类型转换能否成功的办法,这时dynamic_cast就能大显身手了.详解正解请往下看。

         dynamic_cast的转换格式:dynamic_cast<type-id>(expression) 

          将expression转换为type-id类型,type-id必须是类的指针、类的引用或者是void*;并且expression和type-id的类型必须一致;

        在运行期,会检查这个转换是否可能。仅能应用于指针或者引用,不支持内置数据类型,因为只有指针和引用才能实现多态,赋值和传值就会出现对象的分割,不会实现多态。通常在基类和派生类之间转换时使用 。基本用法如下:

        T1 obj;
        T2* pObj = dynamic_cast<T2*>(&obj);    //转换为T2指针失败返回NULL
        T2& refObj = dynamic_cast<T2&>(obj);  //转换为T2引用失败抛出bad_cast异常
       注:在使用时需要注意:被转换对象obj的类型T1必须是多态类型,即T1必须公有继承自其它类,或者T1拥有虚函数(继承或自定义)。若T1为非多态类型,使用dynamic_cast会报编译错误。 
       很明显,为了让dynamic_cast能正常工作,必须让编译器支持运行期类型信息(RTTI)。
     (1)、(up cast)向上转型和static_cast转换一样,是安全的,是多态的基础,不需要任何辅助方法,总是成功的;
#include <iostream>
using namespace std;
class A{};
classB:public A{};
int main()
{
	B *b=new B;
	A *a=dynamic_cast<A*>(b);  //Safe and success!
	B *b=dynamic_cast<B*>(b);  //Safe and success!
	void *vp=dynamic_cast<void *>(c);//success vp points to an object C;
	system("pause");
	return 0;
}
    static_cast和dynamic_cast具有相同的效果,而这种上行转换也为隐式转换;和我们的定义:A *a=new C;一样;只是多加了一个dynamic_cast转换符而已。
      注意下面一种特殊的上行转换:在多继承的时候容易造成访问不明确(用虚继承解决),这时候类型的转换也会出现问题!例如:B继承A,C继承A,D继承AB,
#include <iostream>


using namespace std;
class A{};
class B:public A{};
class C:public A{};
class D:public B,public C{};
int main()
{
	D *d=new D;
	A *a=dynamic_cast<A *>(d);//这是因为BC都继承了A,那么从D转换到A就有两条路可以走,DBA或者DCA,这样的话,计算机就不知道怎么走了,那么a=NULL;
	system("pause");
	return 0;
}
(2)(down cast)向下转型:下行转换时dynamic_cast具有类型检查的功能,比static_cast安全;在多态类之间的转换主要用dynamic_cast,因为类型提供了
运行时的信息。如果expression是type-id的基类,使用dynamic_cast进行转换时,在运行时就会检查expression是否真正的指向一个type-id类型的指针,如果是则进行正确的转化,获得相应的值;如果不对,则返回NULL,如果是引用则在运行时会抛出异常;
#include <iostream>
using namespace std;
class A{};
class B:public A{};
int main()
{
	A *a=new B;
	A *aa=new A;
	B *a1=dynamic_cast<B*>(a);  // a1不为NULL,a本来指向B,Safe and success!
	B *a2=dynamic_cast<B*>(aa)  // error,aa指向A,并不指向B ,返回NULL,即a2=NULL; 
	system("pause");
	return 0;
}

看到这里,回到上面的问题:具体做法就是:

A* pA  = dynamic_cast<A*>pB;// upcast. 
if (NULL == pA){...}  //OK,不为NULL;
C* pC  = dynamic_cast<C*>pB;// downcast. 
if (NULL == pC){...}   //error,显然为空NULL;
       dynamic_cast < ObjectType-ID* > ( ObjectType*)
      如果要成功地将ObjectType*转换为ObjectType-ID*,则必须存在这种可能性才可以,也就是说ObjectType*指向的对象要"包含"ObjectType-ID*指向的对象,如此才能够成功.就上面的例子来说,C对象"包含"B对象,而B对象"包含"A对象,如果:
                      A* pA = new B;
那么
                   B* pB  = dynamic_cast<B*>pA;// OK.
                   C* pC  = dynamic_cast<C*>pA;// Fail.
如果说你不能确定这种包含关系,最好使用dynamic_cast.
 实际上可以把dynamic_cast看做是更严格检查的强制类型转换,因为"更严格"所以能够检查出来错误.
(3)(cross cast)横向转型:

横向转型的时候一定要注意是否有继承关系,如果没有继承关系的两个类是不能转换的。

例子如下:

class Shape {};
class Rollable {};
class Circle : public Shape, public Rollable {};
class Square : public Shape {};

//cross cast fail
Shape *pShape1 = new Square();
Rollable *pRollable1 = dynamic_cast<Rollable*>(pShape1);//pRollable为NULL

//cross cast success
Shape *pShape2 = new Circle();
Rollable *pRollable2 = dynamic_cast<Rollable*>(pShape2);//pRollable不为NULL

3、const_cast (编译器在编译期处理)

const_cast的转换格式:const_cast <type-id> (expression)
const_cast用来将类型的const、volatile和__unaligned属性移除,主要针对const和volatile的转换。常量指针被转换成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然引用原来的对象。

注意常量对象只能调用常量函数,常量函数只能输出,不能改变成员属性的值,所以这里需要const_cast来转换我非常量的指针对象来进行下一步的操作。

#include <iostream>
using namespace std;
class A
{
public:
	A():num(10)
	{
		cout<<"A"<<endl;
	}
	int num;
};
int main()
{
	const A *a=new A;
	A *a3=new A;
	cout<<a->num<<endl;
	//a->num=100; //error;因为常量对象不允许改变类的成员属性;
	A *aa=const_cast<A *>(a);
	aa->num=100;//OK,经过const_cast的转化,aa不是常量对象;
	cout<<a->num<<endl;

	const A &a1=*a;
	//a1->num=200; //error;常量对象的引用也不能改变成员属性;
	A &a2=const_cast<A&>(*a);
	a2.num=200;
	cout<<a2.num<<endl;//OK,经过const_cast的转化,a2不是常量对象的引用;
	//现在a2和aa都是指向同一个对象。
	
	//A a4=const_cast<A>(*a3);  //error,,不能直接使用const_cast对非const,volatile和nuligned去掉该属性。
	const char* p = "123";   
	char* c = const_cast<char*>(p);   <span>
</span><pre name="code" class="cpp">        c[0] = 1;   //error,表面上通过编译去掉了const性,但是操作其地址时系统依然不允许这么做。
char *pp="keepgoing";const char *p1=const_cast<const char *>(pp);system("pause");return 0;}

运行结果:

技术分享      

  对于本身定义时为const的类型,即使你去掉const性,在你操作这片内容时候也要小心,只能r不能w操作,否则还是会出错。

      const_cast操作不能在不同的种类间转换。相反,它仅仅把一个它作用的表达式转换成常量。它可以使一个本来不是const类型的数据转换成const类型的,或者把const属性去掉。尽量不要使用const_cast,如果发现调用自己的函数,竟然使用了const_cast,那就赶紧打住,重新考虑一下设计吧。
(4)reinterpret_cast (编译器在编译期处理)

reinterpret_cast的转换格式:reinterpret_cast <type-id> (expression)

         用于进行没有任何关联之间的转换,比如一个字符指针转换为一个整形数。允许将任何指针类型转换为其它的指针类型;听起来很强大,但是也很不靠谱。它主要用于将一种数据类型从一种类型转换为另一种类型。它可以将一个指针转换成一个整数,也可以将一个整数转换成一个指针,在实际开发中,先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原来的指针值;特别是开辟了系统全局的内存空间,需要在多个应用程序之间使用时,需要彼此共享,传递这个内存空间的指针时,就可以将指针转换成整数值,得到以后,再将整数值转换成指针,进行对应的操作。


博文资料参考:

http://riddickbryant.iteye.com/blog/547361

http://www.jb51.net/article/55968.htm

http://www.jb51.net/article/55885.htm

http://bbs.pediy.com/showthread.php?t=196996

http://www.cnblogs.com/weidagang2046/archive/2010/04/10/1709226.html

http://jetyi.blog.51cto.com/1460128/671256


感谢博主的分享!!

dynamic_cast、static_cast、const_cast和reinterpret_cast的区别

标签:

原文地址:http://blog.csdn.net/gogokongyin/article/details/51317975

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!