【C++Primer Ch5表达式】类型转换:显示转换


显示转换也称强制转换,有四种:static_cast、dynamic_cast、const_cast和reinterpret_cast。
用法:cast-name<type>( expression ),expresssion是强制转换的值。
虽然有时需要强制转换,但它们本质上都是非常危险的,如使用const_cast总是预示着设计缺陷。
(1)static_cast
总结:static_cast是C类型转换在C++中的表现。
用法:static_cast < type-id > ( expression )
说明:该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。
它主要有如下几种用法:
① 用于类层次结构中基类和子类之间指针或引用的转换.
进行上行转换(把子类的指针或引用转换成基类表示)是安全的;
进行下行转换(把基类指针或引用转换成子类指针或引用)时,由于没有动态类型检查,所以是不安全的。
② 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
③ 把void指针转换成目标类型的指针(不安全!!)
④ 把任何类型的表达式转换成void类型。
即:void指针<--->其他类型指针互相转换。
注意static_cast不能转换掉expression的const属性。
(2)dynamic_cast
总结:dynamic_cast支持运行时识别指针或引用所指向的对象,
所以基类要有虚函数来提供虚函数表,以为提供运行时类型信息检查。
可以把dynamic_cast视作static_cast类型进行父类到子类转换的不足。
作用:将一个基类对象指提供或引用类型信息t到继承类指针,dynamic_cast会根据基类指针
是否真正指向继承类指针来做相应处理, 即会作一定的判断。 
对指针进行dynamic_cast,失败返回null,成功返回正常cast后的对象指针;
对引用进行dynamic_cast,失败抛出一个异常,成功返回正常cast后的对象引用。
  
注意:dynamic_cast在将父类cast到子类时,父类必须要有虚函数,以提供多态性。否则编译错误。
对编译器的要求: 
dynamic_cast<> 会用到RTTI技术,因此需要启动“运行时类型信息”这一选项.
术语:RTTI(Run-Time Type Inf术语:ation,通过运行时类型信息)程序能够使用
基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。
(3)const_cast
只有const_cast才能将const性质转换掉。type_id和expression的类型是一样的type_id和expression的类型是一样的。
① 常量指针被转化成非常量指针,并且仍然指向原来的对象;
② 常量引用被转换成非常量引用,并且仍然指向原来的对象;
③ 常量对象被转换成非常量对象。
(4)reinterpret_cast
用法:reinpreter_cast<type-id> (expression)
说明:type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。
它可以把一个指针转换成一个整数,也可以把一个整换成一个指针
(先把一个指针转换成一个整数,在把该整数转换成型的指针,还可以得到原先的指针值)。

该运算符的用法比较多。


====================================================================================

【四种类型总结】
(1)所涉及转换
整数<===>指针:reinterpret_cast
void指针<===>其他类型指针:static_cast,
                        另包括基类和子类之间指针或引用的转换.
                        基本数据类型之间的转换
父类指针--->子类指针(带安全检查):dynamic_cast
去除const属性:const_cast
(2)作用对比
const_cast 用来移除 const,这个没什么好说的.
dynamic_cast 需要 RTTI 支持, 主要用于把基类指针转换为派生类指针.
这里的基类指针其实是指向一个派生类实例,只是类型为基类.
static_cast 运算符完成“相关类型”之间的转换. 
reinterpret_cast 处理“互不相关的类型”之间的转换.


所谓"相关类型"指的是从逻辑上来说,多多少少还有那么一点联系的类型,
比如从 double 到 int,我们知道它们之间还是有联系的,只是精度差异而已,
使用 static_cast 就是告诉编译器:我知道会引起精度损失,但是我不在乎. 
又如从 void* 到 具体类型指针像 char*,从语义上我们知道 void* 可以是任意类型的指针,
当然也有可能是 char* 型的指针,这就是所谓的"多多少少还有那么一点联系"的意思. 
又如从派生类层次中的上行转换(即从派生类指针到基类指针,因为是安全的,所以可以用隐式类型转换)
或者下行转换(不安全,应该用 dynamic_cast 代替).

对于static_cast操作符,如果需要截断,补齐或者指针偏移编译器都会自动完成.
注意这一点,是和 reinterpret_cast 的一个根本区别.


"互不相关的类型"指的是两种完全不同的类型,如从整型到指针类型,
或者从一个指针到另一个毫不相干的指针.

reinterpret_cast 操作执行的是比特位拷贝,就好像用 memcpy() 一样.


====================================================================================

【reinterpret_cast存在的绝佳理由】

——int 型和指针类型间的相互转换用 reinterpret_cast.
比如我写代码的时候经常这样做: new 一个 struct,然后把指针返回给外部函数作为一个"句柄",我不希望外部函数知道这是一个指针,只需要外部函数在调用相关函数时把这个"句柄"重新传回来.这时,就可以把指针转换为一个 int 型返回. 这是 reinterpret_cast 存在的绝佳理由.

struct car
{
    int doors;
    int height;
    int length;
    float weight; 
};

int create_car()
{
    car *c = new car;
    return reinterpret_cast<int>(c);
}

int get_car_doors(int car_id)
{
    car *c = reinterpret_cast<car*>(car_id);
    return c->doors;
}

void destroy_car(int car_id)
{
    car *c = reinterpret_cast<car*>(car_id);
    delete c;
}

如上,外部函数不需要知道 struct car 的具体定义,只需要调用 create_car() 得到一个 car id,

然后用此 car_id 调用其他相关函数即可,至于 car_id 是什么,根本没必要关心.

====================================================================================

#if 1
#include <iostream>

using namespace std;

int main( void )
{
	double d = 97.0;
	// static_cast应用场景
	char ch = static_cast<char>( d ); // 基本数据类型之间的转换,ch:a
	char *pc = &ch; 
	void *v_static = static_cast<void*>( pc );
	int *i_static = static_cast<int*>( v_static ); // *pc:97
	// int *i_static = static_cast<int*>( pc );// error: 类型转换无效
	// reinterpret应用场景
	int *i_reinterpret = reinterpret_cast<int*>( pc );// ok: 毫不相关的类型间转换也行
	char *c_reinterpret = reinterpret_cast<char*>( pc );
	const int *a = reinterpret_cast<int*>(1);
	// 三者都是ch的地址
	cout << i_reinterpret << endl;
	cout << v_static << endl;
	cout << i_static << endl;
	// 解引用
	cout << *i_reinterpret << endl; // *i_reinterpret:无意义
	cout << *i_static << endl; // *i_static:无意义
	cout << *c_reinterpret << endl; // *c_reinterpret:有意义

	return 0;
}

#elif 0
#include <iostream>

using namespace std;

int main( void )
{
	const double pi = 3.14;
	// double *p = π// error
	const double *ptr = π// ok
	double *p = const_cast<double*>(ptr);
	cout << *p << endl; // *p: 3.14
	*p += 1;
	cout << *p << endl; // *p: 4.14
	cout << *ptr << endl; // *ptr 4.14
	cout << pi << endl; // pi: 4.14

	return 0;
}

#elif 0

#include <iostream>
using namespace std;

class CBasic{ 
public: 
     virtual int test(){return 0;} // 一定要是 virtual 
};

class CDerived : public CBasic{
public:
     virtual int test(){	return 1;	} 
}; 
  
int main() 
{ 
     CBasic        cBasic; 
     CDerived    cDerived; 
      
     CBasic * pB1 = new CBasic; 
     CBasic * pB2 = new CDerived; 
  
     //dynamic cast failed, so pD1 is null. 
     CDerived * pD1 = dynamic_cast<CDerived * > (pB1);
	 if( pD1 != NULL )
		 cout << pD1->test() << endl;
	 else
		 cout << "pD1 is null" << endl;

     //dynamic cast succeeded, so pD2 points to  CDerived object                                         
     CDerived * pD2 = dynamic_cast<CDerived * > (pB2);
	 cout << pD2->test() << endl;
     // dynamic_cast failed, so throw an exception.
	 //CDerived & rD1 = dynamic_cast<CDerived &> (*pB1);
	 // dynamic cast succeeded, so rD2 references to CDerived object. 
     CDerived & rD2 = dynamic_cast<CDerived &> (*pB2);
	 cout << rD2.test() << endl;
     return 0;
} 

#endif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值