C++基础语法:RTTI(运行时识别)之二:类型转换运算符--const_cast(常量转换),static_cast(静态转换),reinterpret_cast(重解释转换)

前言
       

         "打牢基础,万事不愁" .C++的基础语法的学习."学以致用,边学边用",编程是实践性很强的技术,在运用中理解,总结.  

引入

        以<C++ Prime Plus> 6th Edition(以下称"本书")内容理解类型转换运算符

回顾C语言的类型转换

        本书P649:在C++的创始人Bjarne Stroustrup看来,C语言中的类型转换运算符太过松散。

 首先,上述3种类型转换中,哪一种有意义?除非不讲理,否则它们中没有一个是有意义的。其次,这3种类型转换中哪种是允许的呢? 在C语言中都是允许的(黑体字是原话)

----解读: C语言是最接近硬件的高级语言,他对所有的数据都一视同仁,看作是"字节"的集合(他也提供了位的访问和修改,所以也可以看成"位"集合).无论什么样的类型转换,在C语言中都是被允许的---无非就是字节打乱重新解释,至于含义有没有发生变化,靠程序员自己掌握.因此笔者给C语言的类型转换定义一个概念,叫做"无责任转换".

        C++是支持面向对象的,如果对象转换再用无责任转换,会显得更乱,因此C++设计者Bjarne Stroustrup设计出类型转换运算符来规范类型转换.

类型转换概览

       共有4个运算符:

                 dynamic_cast;

                const_cast;

                static_cast;

                reinterpret_cast

        他们的格式都是一样的:        运算符<目标指针类型> 指针

        其中dynamic_cast在前一篇帖子C++基础语法:RTTI(运行时识别)之一:dynamic_cast和typeid-优快云博客有介绍,不赘述

const_cast运算符

        const_cast运算符的意思是:const类型指针,临时可以转型为非const指针去修改数据.本书中描述:提供该运算符的原因是,有时候可能需要这样一个值,它在大多数时候是常量,而有时又是可以修改的。在这种情况下,可以将这个值声明为const,并在需要修改它的时候,使用const_cast (黑体字是原话)

==========================内容分割线=======================================

        这段话仅代表笔者个人观点.笔者认为没必要用const_cast,他的应用场景在哪里?可以定义两个指针:const指针和非const指针来替换const_cast.程序清单15.19constcast.cpp的写法会让人迷惑,change函数到底改不改数据?(笔者觉得这部分内容最重要反而是const指针转换成非const指针这段,因为笔者以前没见过这种写法)

High bar;
const High* pbar=&bar;
High* pb=(High *)(pbar);    //const指针转换成非const指针

        非const指针可以自动向上转型成const指针的.向下转型在C语言中称为强制转换.通常强制转换会让改变后的数据和原始数据发生变化,但这里的强转好像不会有.

        给出替换const_cast的理由:指针用于间接访问数据,const修饰指针时,表示只能访问;没有用const修饰时,表示可以修改指向数据.可以用多个指针指向同一个(或一组)数据.这使得数据的访问或修改十分清晰.看下面这段简单代码

#include<iostream>
using namespace std;

void ar_plusone(int* a, int length);
void print_ar(const int* a, int length);

int main(void) {
	int a[3] = { 0,1,2 };					//原始数据声明
	int* pt_a = a;							//声明非const指针
	const int* cpt_a = a;					//声明const指针
	int length = sizeof(a) / sizeof(int);	//求出数组长度
	cout << "数组内初始数据为:" << endl;
	print_ar(cpt_a, length);				//打印原始数据,传入const指针,表示不会修改元素
	ar_plusone(pt_a, length);				//数组元素加1,传入非const指针pt_a,传入数组名a也行		
	cout << "数组内元素加1后数据为:" << endl;
	print_ar(cpt_a, length);				//打印修改后的数组元素
}

void ar_plusone(int* a,int length) {		//数组元素加1,使用非const指针
	for (int i = 0; i < length; i++) 
		a[i] += 1;

}

void print_ar(const int* a, int length) {    //打印数组元素,使用const指针
	for (int i = 0; i < length; i++)
		cout << "第" << i+1 << "个元素是:" << a[i] << endl;
}

        再想一想,不管是const_cast转换,还是强制类型转换,每次操作数据都只能是一个指针,从这个角度上来讲,多定义一个指针,比起使用有些"绕"的const_cast来得容易.

        还有面对多态,用类型转换随时得到需要的指针.

==========================内容分割线=======================================

static_cast运算符

运用一:使用static_cast运算符作对象类型转换

        首先回顾对象指针向上转换和向下转换

/*已测试*/
/*指针向上转换和向下转换*/
#include<iostream>
using namespace std;

class Base {										//基类定义
public:
	virtual void fun() { 
		cout << "这是基类方法" << endl;
	}
};

class Baseplus :public Base {						//派生类定义
public:
	void fun() { 
		Base::fun(); 
		cout << "这也是派生类方法" << endl;
	}
};

int main(void) {
	Base* base_pt = new Base();
	Baseplus* baseplus_pt = new Baseplus();
	base_pt = baseplus_pt;							//派生类指针向上转换(隐式,自动转换)
	base_pt->fun();
	cout << "============分割线============" << endl;

	Base* base_pt2 = new Base();
	Baseplus* baseplus_pt2 = new Baseplus();
	baseplus_pt2 = (Baseplus*)base_pt2;				//基类指针向下转换(显式,强制转换)
	baseplus_pt2->fun();
}

        说明:指针向上转换是隐式转换,是实现多态的必要条件.

                 指针向下转换一般来说是无意义的,只能调用基类自身方法,无法实现多态,但语法正确.

         static_cast的定义:用统一的格式,表达向上和向下转换.

        上述测试代码可以改成:

int main(void) {
	Baseplus* baseplus_pt = new Baseplus();
	//Base* base_pt = new Base();
	//base_pt = baseplus_pt;							//派生类指针向上转换(隐式,自动转换)
	Base* base_pt = static_cast<Base*> (baseplus_pt);	//用static_cast替换上面两行代码
	base_pt->fun();
	cout << "============分割线============" << endl;

	Base* base_pt2 = new Base();
	//Baseplus* baseplus_pt2 = new Baseplus();
	//baseplus_pt2 = (Baseplus*)base_pt2;				//基类指针向下转换(显式,强制转换)
	Baseplus* baseplus_pt2 = static_cast<Baseplus*>(base_pt2);	//用static_cast替换上面两行代码
	baseplus_pt2->fun();
}

         具体效果体现出来,减少一行代码,写法比较优雅.

         一个典型应用场景:当继承序列比较深时,使用static_cast<>可以简化代码.

        类型向上转换时,普通转换每次只转一层,使用static_cast<>可以一次转化到位.例如:假设Baseplus的派生类Baseplus也有派生类Baseplusplus,则可以使用

Baseplusplus baseplusplus;
Base & base=static_cast<Base&>(baseplusplus);

运用二:使用static_cast运算符作枚举和整型值转换,以及其他类型之间的转换 

        回顾枚举

        枚举是自定义数据类型,声明枚举值,多用于描述事物所处状态.枚举值用整型值来表示,通常作为状态的一种附加信息.举个例子:描述空调有三种状态:工作(work),断电(poweroff),待机(standby),建立以下枚举

//默认枚举定义方式
enum class AIR_CONDITIONER_STA{WORK,POWEROFF,STANDBY}
//另一种定义方式
enum class AIR_CONDITIONER_STA{WORK=1,POWEROFF=2,STANDBY=3}

        建立了枚举类型并声明取值,后面可以用枚举变量和值来表示.写法和任意一种数据类型的使用相同---具备了数据类型,变量和值这三个要素.所以枚举值的整型值表示是一种附加信息.结论是一句话:如果枚举值需要带其他信息,可以使用带整型值的形式.否则枚举值和整型值不要发生联系

        本书P652:同理,由于无需进行类型转换,枚举值就可以被转换为整型,所以可以用static_cast将整型转换为枚举值。同样,可以使用static_cast将double转换为int、将float转换为long以及其他各种数值转换(黑体字是原话)

     ----解读:无需类型转换,枚举值可以转换为整型.下面代码简单描述了这一过程

/*已测试*/
/*static_cast用于枚举值和整型值之间的*/
#include<iostream>
using namespace std;

enum SIZE {SMALL,MIDDLE,BIG};						//非class枚举声明
enum class COLOR {GREEN,RED,BLUE};					//class枚举声明

int main() {
	SIZE VERY_BIG = static_cast<SIZE>(4);			//整型转换成枚举值,新增了枚举值VERY_BIG
	SIZE size = VERY_BIG;
	cout << "枚举SIZE添加的VERY_BIG表示的整型值是:" <<size<< endl;

	COLOR BROWN = static_cast<COLOR>(4);			//整型转换成枚举值
	COLOR color = BROWN;
	cout << "枚举COLOR添加BROWN表示的整型值是:" << (int)color << endl;
	cout << "=========整型转换为枚举型破坏封装性=========" << endl;
	cout << endl;

	int size2 = static_cast<int>(VERY_BIG);			//static_cast转换枚举为整型
	cout << "VERY_BIG代表的整型值是:" << size2 << endl;
	int color2 = static_cast<int>(BROWN);			//static_cast转换枚举为整型
	cout << "BROWN代表的整型值是:" << color2 << endl;
	int color3 = (int)BROWN;						//强转枚举值为整型
	cout << "BROWN代表的整型值是:" << color3 << endl;
	cout << endl;

	double d = 1.5;
	int int_d = static_cast<int> (d);
	cout << "double型1.5转换为int后的值为:" << int_d<<endl;
}

代码说明:

        1.整型转换成枚举值破坏枚举封装性,不推荐使用.

         枚举定义好之后,外面还可以添加了枚举值.从设计角度上来说脱离了控制.所以不要这样写.

        2.枚举值转换为整型值.

        直接取出枚举值的整型值表示,当然强制转换也可以办到.

reinterpret_cast(重解释转换) 

         简单的理解:reinterpret_cast和C语言中的类型强制转换是同样的意思,即上面提到的无责任转换.本书作者(或译者)对这个语法表达出的态度是十分谨慎的,下面黑体字是本书原话:

        reinterpret_cast运算符用于天生危险的类型转换。它不允许删除const,但会执行其他令人生厌的操作。有时程序员必须做一些依赖于实现的、令人生厌的操作,使用reinterpret_cast运算符可以简化对这种行为的跟踪工作.

        先了解reinterpret_cast语法限制:然而,reinterprete_cast运算符并不支持所有的类型转换。例如,可以将指针类型转换为足以存储指针表示的整型,但不能将指针转换为更小的整型或浮点型。另一个限制是,不能将函数指针转换为数据指针, 反之亦然。(黑体字是本书原话)

#include<iostream>
using namespace std;

class Demo {
	string name;
	int age;
};

int main(void) {
	int a = sizeof(char*);
	int b = sizeof(double*);
	cout << "字符指针大小为:" << a << endl;
	cout << "double型指针大小为:" << b<< endl;
	Demo* de = new Demo;
	int c = sizeof(de);
	cout << "class类指针大小为:" <<c << endl;
}

运行结果:

字符指针大小为:4
double型指针大小为:4
class类指针大小为:4

        1.参加转换的数字(实际是地址值)使用4字节(32位)或比这个类型(4字节)大的数据类型,本书示例用了long型表示long value=0xA224B118.

        2.不要将函数指针和数据指针作转换.这个容易理解,本来属于两个体系不能互换.

        reinterpret_cast同样可以用类型强制转换来完成.还没有那么多限制,所以本质上可以不用,由程序员自己掌握.reinterpret_cast的演示要访问内存,这可能出错,不便演示,所以没有代码说明.

        对reinterpret_cast的理解,不在于他的语法,而是他作的事:地址转换.是编程中相当重要的一个环节,涉及到数据和硬件的交互(通过操作系统),简单描述其过程如下:

        1.操作系统给出虚拟地址值.

        2.编程语言做地址映射.类似本书

dat *pd=reinterpret_cast<dat*> (&value);

        3.代码读取或修改数据.类似以下写法:

pd->a=0xA238;        //short类型赋值

        虚拟地址和硬件地址的转换由操作系统完成,这属于另一个课目--操作系统的内容. 

小结

        对const_cast(常量转换),static_cast(静态转换),reinterpret_cast(重解释转换)做了简单分析.他们的意义其实不大,都可以用类型转换来解决.最重要的是dynamic_cast(动态转换)作类型识别.

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

重庆彭枫

你的鼓励是我创作的动力,谢谢

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值