天天看点

dynamic_cast、static_cast、const_cast和reinterpret_cast的区别

           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;}

运行结果:

dynamic_cast、static_cast、const_cast和reinterpret_cast的区别

  对于本身定义时为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

感谢博主的分享!!