前言
"打牢基础,万事不愁" .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(动态转换)作类型识别.
259

被折叠的 条评论
为什么被折叠?



