C++强制类型转换运算符

1、  C 风格(C-style)强制转型如下:

 (T) expression //cast expression to be of type T

 函数风格(Function-style)强制转型使用这样的语法:

T(expression) //cast expression to be of type T

这两种形式之间没有本质上的不同,它纯粹就是一个把括号放在哪的问题。我把这两种形式称为旧风格(old-style)的强制转型。

2、  C++的四种强制转型形式:

C++ 同时提供了四种新的强制转型形式(通常称为新风格的或 C++ 风格的强制转型):

  const_cast(expression)

  dynamic_cast(expression)

  reinterpret_cast(expression)

  static_cast(expression)

 

每一种适用于特定的目的:

·dynamic_cast 主要用于执行“安全的向下转型(safe downcasting)”,也就是说,要确定一个对象是否是一个继承体系中的一个特定类型。它是唯一不能用旧风格语法执行的强制转型,也是唯一可能有重大运行时代价的强制转型。   

·static_cast 可以被用于强制隐型转换(例如,non-const 对象转型为 const 对象,int 转型为 double,等等),它还可以用于很多这样的转换的反向转换(例如,void* 指针转型为有类型指针,基类指针转型为派生类指针),但是它不能将一个 const 对象转型为 non-const 对象(只有 const_cast 能做到),它最接近于C-style的转换。   

·const_cast 一般用于强制消除对象的常量性。它是唯一能做到这一点的 C++ 风格的强制转型。

·reinterpret_cast 是特意用于底层的强制转型,导致实现依赖(implementation-dependent)(就是说,不可移植)的结果,例如,将一个指针转型为一个整数。这样的强制转型在底层代码以外应该极为罕见。

旧风格的强制转型依然合法,但是新的形式更可取。首先,在代码中它们更容易识别(无论是人还是像 grep 这样的工具都是如此),这样就简化了在代码中寻找类型系统被破坏的地方的过程。第二,更精确地指定每一个强制转型的目的,使得编译器诊断使用错误成为可能。例如,如果你试图使用一个 const_cast 以外的新风格强制转型来消除常量性,你的代码将无法编译。

------------------------------------------------------------------------------------------------------------------------

以上为网上的教科书式概念,现在从一些具体用法上解释几者的区别。

1、dynamic_cast主要用于继承体系,而且是用于多态。将一个派生类的指针转化为基类的指针。其实这时是可以直接进行赋值的,不需要用此运算符。不过加上此运算符后,变的更加直接明了,便于检查。例如:

class A
{
public:
	virtual ~A(){}
};
class B:public A
{
};
int main()
{
	B* p = new B();
	A* q = dynamic_cast<A*>(p); //其实也可以直接写成 A*q = p;
	
//如果反向的话,b的值为NULL。即使当两个类没有继承关系,进行dynamic转换时,也不会编译错误,但返回值会是NULL值。
A* a =new A();
	B* b = dynamic_cast<B*>(a); 
}

注意:如果将类A中的virtual去掉,使其没有虚函数,那么此时dynamic_cast就会产生编译错误,dynamic_cast的使用是要借助于虚表的。而虚表是虚类产生的。

2、static_cast用于大部分的类型转换。如:

1)数据类型的转换,如int转换为char,float转换为double型等等.

2)类层次结构中基类和子类之间指针或引用的转换。

  A、进行上行转换(把子类的指针或引用转换成基类表示)是安全的;

 B、进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的。(dynamic_cast进行下行转换时返回指针为空)。

3)当两个毫不相关的类的指针进行转换时,static_cast转换会发生编译失败。

3、const_cast用于去掉对象的常量性质,这是在c++里的唯一方法。例如:

char  a = ‘a’;
const char* p = &a;
char* q = const_cast<char*>(p);
/* char *q = p; 这会发生编译错误的,因为如果这样成功的话,就可以对*q进行操作,那么就间接操作了*p,而此时*p是const型的,不能修改。所以只能通过const_cast进行强制取消常量性质。*/
*q = ‘b’; 
//此时便可以对*q进行修改,同时也修改了*p,因为q和p指向的同一个地方。但是如果写成*p = ‘b’还是不允许的。

注意:

const int a =5;

int b = a;

这样写是允许,不需要用const_cast进行转换,因为b又是一个对象,对b的操作并不会影响a。如果出现了指针则要注意一下,要看const修饰的是指针,还是修饰的是指针所指的对象。例如

char* const p = &a;

char* q = p;

这样就是合法的,因为const修饰的是p指针。表示指针不会变换。char*q = p;是分配了一个新的指针q,对指针q或*q的操作并不会影响到p。


4、reinterpret_cast运算符主要是用于指针类型的转换,以及整型与指针的转换。转化的时候在底层只是简单的将内容按比特完全复制过去,然后按另一种方式进行解析。和static_cast不一样,例如static_cast将int转换为double时会将在内存中的值进行改变的,一开始是int的表示方式,后来就变成double类型在内存中的表示方式。所以如果用reinterpret_cast将int转换为double时,只是简单的位复制,然后按double型解释,那么解释出来的值完全和原值不一样了。这样是没什么意义的,也是不安全的。所以如果要进行内置变量转换,多用static_const。但是对于指针类型转换,用reinterpret_cast是合适的,因为指针在内存里面都是占四个字节,表示某个内存的地址。在指针类型转换时,并不需要指针的值进行变换(保持指向某块内存),只需让指针解释的方式发生改变即可。

举例:

int a = 83;
int* s = &a;
char* p = reinterpret_cast<char*>(s); //将整型指针转化为字符指针
int b = reinterpret_cast<int>(s); //将指针型转换为整型
float p = reinterpret_cast<float&>(a);//如果要进行内置数据类型的转换,则必须加上&符号,不知道为什么?

 

补充:(摘抄于参考文献3)

1、  static_cast和reinterpret_cast 的区别:

两者都可以用于指针、引用类型的转换,但static_cast主要用于“有关系(如继承关系)”的指针或引用之间的转换,多半是继承关系,而没有关系的指针之间的转换会发生编译错误。但如果是“有关系”的指针、引用类型进行转换时,我们又多采用dynamic_cast进行转换(可以进行安全性检查)。而reinterpret_cast可以用于任何指针之间的转换。前述三者都是不能取消对象的const属性,只能通过const_cast。

所以总结如下:

dynamic_cast用于有继承关系的指针或引用之间的转换。

static_cast用于基本数据类型或对象之间的转换。

reinterpret_cast用户没有关系的指针或引用之间的转换

const_cast用于去掉对象的const属性。

2、  对static_const不要望文生义,认为和const_cast是取消const一样,取消static属性。

3、  举一个例子:

#include <iostream>
using namespace std;
unsigned short Hash( void *p ) {
	unsigned long val = reinterpret_cast<unsigned long>( p );
	return ( unsigned short )( val ^ (val >> 16));
}

class Something
{
	/* Some codes here */
};

class Otherthing
{
	/* Some codes here */
};

int main() {
	
	typedef unsigned short (*FuncPointer)( void *) ;
	FuncPointer fp = Hash;	//right, this is what we want

	int a[10];
	const int* ch = a; //right, array is just like pointer
	char chArray[4] = {'a','b','c','d'};
	fp = reinterpret_cast<FuncPointer> (ch); //no error, but does not make sense
	ch = reinterpret_cast<int*> (chArray);	//no error
	
	cout <<hex<< *ch;	//output: 64636261	//it really reinterpret the pointer

	
	Something * st = new Something();
	Otherthing * ot = reinterpret_cast<Otherthing*> (st); //cast between objects with on relationship
}

而以上转换,都是static_cast所不能完成的任务,也就是说把上边程序里所有的reinterpret_cast换成static_cast的话,就会立即得到编译错误,因为目标指针和原始指针之间不存在"关系"

从上边的程序,也就一下子看出来了reinterpret_cast和static_cast之间最本质的区别。

而以上转换,都是static_cast所不能完成的任务,也就是说把上边程序里所有的reinterpret_cast换成static_cast的话,就会立即得到编译错误,因为目标指针和原始指针之间不存在"关系"

从上边的程序,也就一下子看出来了reinterpret_cast和static_cast之间最本质的区别。

对于static_cast所需要的关系,"继承"绝对是其中之一,所以static_cast支持指向基类的指针和指向子类的指针之间的互相转换:

class Parents
{
public:
	virtual ~Parents(){}
	/*codes here*/
};

class Children : public Parents
{
	/*codes here*/
};

int main() 
{	
	Children * daughter = new Children();
	Parents * mother = static_cast<Parents*> (daughter); //right, cast with polymorphism
	
	Parents * father = new Parents();
	Children * son = static_cast<Children*> (father); //no error, but not safe
}

但是从基类到子类的转换,用static_cast并不是安全的,具体的问题会在dynamic_cast一篇阐述。

在指针和引用方便,似乎也只有继承关系是可以被static_cast接受的,其他情况的指针和引用转换都会被static_cast直接扔出编译错误,而这层关系上的转换又几乎都可以被dynamic_cast所代替。这样看起来static_cast运算符的作用就太小了。

实际上static_cast真正用处并不在指针和引用上,而在基础类型和对象的转换上 。 而基于基础类型和对象的转换都是其他三个转换运算符所办不到的。

【参考】

1.        http://www.cnblogs.com/ider/archive/2011/07/22/cpp_cast_operator_part2.html  C++标准转换运算符const_cast

2.        http://www.cnblogs.com/ider/archive/2011/07/30/cpp_cast_operator_part3.html  C++标准转换运算符reinterpret_cast

3.        http://www.cnblogs.com/ider/archive/2011/07/31/cpp_cast_operator_part4.html  C++标准转换运算符static_cast

4.        http://www.cnblogs.com/ider/archive/2011/08/01/cpp_cast_operator_part5.html  C++标准转换运算符dynamic_cast

5.        http://blog.csdn.net/wingfiring/article/details/633033   dynamic_cast详解



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值