C++中的显式类型转化

 类型转化也许大家并不陌生,int i; float j; j = (float)i; i = (int)j; 像这样的显式转化其实很常见,强制类型转换可能会丢失部分数据,所以如果不加(int)做强制转换,严检查的编译会报错,宽检查的编译会报warning。在C语言中,指针是4字节或者8字节的,所以指针之间的强制转换在转换的时候就如同不同的整数类型之间的赋值,问题在于对该指针的使用上,必须确保该指针确实可以做出这样的强制转换。常见的情况是void*到不同的指针类型(比如内存分配,参数传递),char*和unsigned char*这样的转换。也有在读文件的时候,直接把某个结构映射为内存,写文件的时候,把某块内存直接映射成结构体。但其实在C++中,有用于专门用于显示类型转化的更合适更安全的语法。

  主要包括四种:static_cast、const_cast、reinterpret_cast、dynamic_cast。四种转化的用途各不相同,下面一一介绍:

一、static_cast(静态转化)

  语法:A = static_cast<typeA>(B)

  把B显式转化为typeA类型,static_cast是最常用到的转化操作符,使用它可以消除因产生类型转化而可能产生的编译器warnings,static_cast全部用于明确定义的变换,包括编译器允许我们做的不用强制转换的“安全”变换和不太安全但清楚定义的变换。static_cast包含的转化类型包括典型的非强制类型转换、窄化变化(会有信息丢失)、使用void*的强制变换、隐式类型变换和类层次的静态定位(基类和派生类之间的转换)。

  说明代码如下:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 void func(int){}
 5 
 6 int main(){
 7     int i = 0x7fff;
 8     long l;
 9     float f;
10     //情况1,向宽数据转化
11     l = i;
12     f = i;
13     //此时同样可以用static_cast
14     l = static_cast<long>(i);
15     f = static_cast<float>(i);
16     
17     cout << "l = " << l << endl;
18     cout << "f = " << f << endl;
19     
20     //情况2,向窄数据转化,可能发生精度丢失问题
21     i = l;
22     i = f;
23     cout << "i = " << i << endl;
24     //此时使用static_cast,类似于告诉编译器我清楚这种事情的发生,不用担心,也能消除警告
25     i = static_cast<int>(l);
26     i = static_cast<int>(f);
27     char c = static_cast<char>(i);
28     cout << "c = " << c << endl;
29     
30     //情况3,将void*类型强制转换为其他类型
31     void * vp = &i;
32     float* fp = (float*) vp;//这是一个危险的转换
33     fp = static_cast<float*>(vp);//这样同样危险
34     
35     //情况4,隐式类型转换
36     double d = 0.0;
37     int x = d;//自动类型转化
38     x = static_cast<int>(d);//这样声明更加明显
39     func(d);//自动类型转化
40     func(static_cast<int>(d));//这样声明更加明显
41 }

复制

  更重要的应用是在于基类与派生类之间的转换

  class Base{};   class derv:public Base{};   derv dd;   Base bb = static_cast(dd);//具有继承关系的类型之间转换

  Base *pb = new Base;   derv *pd = static_cast(pb);//基类转继承类   derv* pd1 = new derv;   Base* pb1 = static_cast(pd1);//继承类指针转父类指针 二、const_cast(常量转换)

  语法:A = const_cast<typeA>(B)

  这个运算符可以用来去除一个对象的const或volatile属性。typeA必须是一个指针或者引用。

 1 #include <iostream>
 2 using namespace std;
 3 int main(){
 4     const int i = 0;
 5     int* j = (int*)&i;//不推荐使用的方法
 6     j = const_cast<int*>(&i);
 7     cout << *j << endl;
 8     *j = 10;
 9     cout << *j << " " << i << endl;
10 }

复制

三、reinterpret_cast(重解释转换)

  语法:A = reinterpret_cast<typeA>(B)

  这是一种最不安全的转换,最有可能出现问题,reinterpret_cast把对象假想为模式,仿佛它是一个完全不同类型的对象,这是低级的位操作,修改了操作数类型,但仅仅重新解释了对象的比特模型而没有进行二进制转换,在使用reinterpret_cast做任何事情之前,实际上总是需要它回到原来的类型。

  从语法上看,这个操作符仅用于指针类型的转换(返回值是指针)。它用来将一个类型指针转换为另一个类型指针,它只需在编译时重新解释指针的类型。

  这个操作符基本不考虑转换类型之间是否是相关的。

  reinterpret_cast的本质。我喜欢从C语言的角度来理解这个操作符,就像C语言中的指针强制转换,其实只是把地址赋给了新的指针,其它的不做改变,只在新的指针使用的时候,进行不一样的解释。看如下的例子:

 1 #include <iostream>
 2 using namespace std;
 3 const int sz = 100;
 4 
 5 struct X {
 6     int a[sz];
 7 };
 8 
 9 void print(X* x){
10     for(int i = 0; i < sz; i++)
11         cout << x->a[i] << ' ';
12     cout << endl << "-------------------------" << endl;
13 }
14 
15 int main(){
16     X x;
17     print(&x);//输出尚未初始化的结构体内数组
18     int* xp = reinterpret_cast<int*>(&x);//重解释转换,取得x的地址并转换成一个整数指针
19     for(int* i = xp; i < xp + sz; i++)//然后用该指针遍历这个数组,置每个整数元素为0
20         *i = 0;
21     print(reinterpret_cast<X*>(xp));
22     print(&x);
23 }

复制

  reinterpret_cast的思想就是当需要使用的时候,得到的东西已经转换成不同的类型了,以至于它不能用于类型原来的目的,除非再次把它转换回来。这里打印调用中转换回X*。xp只有作为int*才有用,这是对原来的X的重新解释。使用renterpret_cast通常不是一个明智的做法,但是当需要用到的时候,它是十分有用的。

  reinterpret_cast常用的场景如下:

  1)普通指针转换,T*—>U*—>T*,保证T*经过一些列转换值不变

  比如将不同类型的指针存在一个容器里,vector可以存int*,char*,string*等各种指针,只要有别的方式确定某个void*当初的类型是T*,标准保证reinterpret_cast(v[i])可以得到当初的值。

  2)自己做memory allocator,可以将T*转换为U*,这个时候可能要注意字节对其的问题。

四、dynamic_cast(动态转换)

  语法:A=dynamic_cast<typeA>(B)

  该运算符把B转换成typeA类型的对象。TypeA必须是类的指针、类的引用或者void *;

  dynamic_cast的转换是在运行时进行的,它的一个好处是会在运行是做类型检查,如果对象的类型不是期望的类型,它会在指针转换的时候返回NULL,并在引用转换的时候抛出一个std::bad_cast异常。

  dynamic_cast一般只在继承类对象的指针之间或引用之间进行类型转换。如果没有继承关系,则被转化的类具有虚函数对象的指针进行转换。

 1  struct A {
 2     virtual void f() { }
 3   };
 4   struct B : public A { };
 5   struct C { };
 6  
 7   void f () {
 8     A a;
 9     B b;
10  
11     A* ap = &b;
12     B* b1 = dynamic_cast (&a);  // NULL, because 'a' is not a 'B'
13     B* b2 = dynamic_cast (ap);  // 'b'
14     C* c = dynamic_cast (ap);   // NULL.
15  
16     A& ar = dynamic_cast (*ap); // Ok.
17     B& br = dynamic_cast (*ap); // Ok.
18     C& cr = dynamic_cast (*ap); // std::bad_cast
19   }

复制

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
4.3 if 语句 if语句是C++/C语言最简单、最常用的语句,然而很多程序员用隐含错误的方式写if语句。本节以“与零值比较”为例,展开讨论。 4.3.1 布尔变量与零值比较 l 【规则4-3-1】不可将布尔变量直接与TRUE、FALSE或者1、0进行比较。 根据布尔类型的语义,零值为“假”(记为FALSE),任何非零值都是“真”(记为TRUE)。TRUE的值究竟是什么并没有统一的标准。例如Visual C++ 将TRUE定义为1,而Visual Basic则将TRUE定义为-1。 假设布尔变量名字为flag,它与零值比较的标准if语句如下: if (flag) // 表示flag为真 if (!flag) // 表示flag为假 其它的用法都属于不良风格,例如: if (flag == TRUE) if (flag == 1 ) if (flag == FALSE) if (flag == 0) 4.3.2 整型变量与零值比较 l 【规则4-3-2】应当将整型变量用“==”或“!=”直接与0比较。 假设整型变量的名字为value,它与零值比较的标准if语句如下: if (value == 0) if (value != 0) 不可模仿布尔变量的风格而写成 if (value) // 会让人误解 value是布尔变量 if (!value) 4.3.3 浮点变量与零值比较 l 【规则4-3-3】不可将浮点变量用“==”或“!=”与任何数字比较。 千万要留意,无论是float还是double类型的变量,都有精度限制。所以一定要避免将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。 假设浮点变量的名字为x,应当将 if (x == 0.0) // 隐含错误的比较 转化为 if ((x>=-EPSINON) && (x<=EPSINON)) 其EPSINON是允许的误差(即精度)。 4.3.4 指针变量与零值比较 l 【规则4-3-4】应当将指针变量用“==”或“!=”与NULL比较。 指针变量的零值是“空”(记为NULL)。尽管NULL的值与0相同,但是两者意义不同。假设指针变量的名字为p,它与零值比较的标准if语句如下: if (p == NULL) // p与NULL显式比较,强调p是指针变量 if (p != NULL) 不要写成 if (p == 0) // 容易让人误解p是整型变量 if (p != 0) 或者 if (p) // 容易让人误解p是布尔变量 if (!p) 4.3.5 对if语句的补充说明 有时候我们可能会看到 if (NULL == p) 这样古怪的格式。不是程序写错了,是程序员为了防止将 if (p == NULL) 误写成 if (p = NULL),而有意把p和NULL颠倒。编译器认为 if (p = NULL) 是合法的,但是会指出 if (NULL = p)是错误的,因为NULL不能被赋值。 程序有时会遇到if/else/return的组合,应该将如下不良风格的程序 if (condition) return x; return y; 改写为 if (condition) { return x; } else { return y; } 或者改写成更加简练的 return (condition ? x : y);
【资源说明】 人工智能作业基于C++实现的拼音输入法源码+拼音输入法实验报告+项目部署说明.zip 1、该资源内项目代码都是经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载使用,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能。 这个文件夹包含了我实现的拼音输入法作业,其包含五个目录。 + bin/ 目录下包含了项目用到的可执行文件,主要为 `pinyin` 和 `evaluate`,分别为项目的主程序和评估程序; + data/ 目录下包含了项目用到的输入输出文件; + model/ 目录下包含了项目预先训练好的模型文件; + src/ 目录包含了项目的源代码,其 `main.cpp` 为主程序文件,`model.cpp` 为模型建立文件,`predict.cpp` 为预测文件; + test/ 目录下是一些开发过程用到的测试文件。 由于项目使用到的模型文件太大,无法将其上传到 github 的项目仓库。我将其保存在了我的云盘,您可以通过以下命令将其下载到 `model/` 文件夹下: ``` wget https://cloud.tsinghua.edu.cn/f/fce169ed08ec4c0b8187/?dl=1 -O model/posterior3zip2.txt ``` 为了运行本项目,您需要使用 Linux 系统,并且在项目的主文件夹下(README.md 所在的文件夹),运行以下命令 ``` bin/pinyin data/test_input.txt data/test_output.txt ``` 该命令会默认使用<strong>三元模型</strong>作为拼音输入法的模型,对于一千条测试语句,该命令大约需要执行两分钟。同时,本项目也支持使用二元模型,您可以通过显式指定 ``` bin/pinyin -n2 data/test_input.txt data/test_output.txt ``` 来完成这一点。显然,也可以使用 `-n3` 来显式指定使用三元模型。 运行完成后,转化的结果被存储在了 `data/test_output.txt` 文件。本项目还提供了 `evaluate` 命令,来评估转化结果的好坏 ``` bin/evaluate data/test_output.txt data/test_ans.output ``` 该命令会打印转化的字准确率与行准确率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

入伍击寇

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值