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的本质(http://blog.csdn.net/coding_hello/archive/2008/03/24/2211466.aspx)一文做的试验很好的解释了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 条评论
登录 后参与评论

相关文章

来自专栏用户3030674的专栏

java接口

接口中常量的修饰关键字:public,static,final(常量) 函数的修饰关键字:public,abstract 如果没有写全,系统在编译时会自动加上 ...

15520
来自专栏Play & Scala 技术分享

Scala基础 - 柯里化(Currying)及其应用

36090
来自专栏派森公园

Scala中的闭包

除此之外,Scala还支持引用其他地方定义的变量:(x: Int) => x + more,这个函数将more也作为入参,不过这个参数是哪里来的?从这个函数的角...

5910
来自专栏Vamei实验室

Java基础11 对象引用

我们之前一直在使用“对象”这个概念,但没有探讨对象在内存中的具体存储方式。这方面的讨论将引出“对象引用”(object reference)这一重要概念。 ...

21580
来自专栏北京马哥教育

一篇搞定Python正则表达式

1. 正则表达式语法 1.1 字符与字符类     1 特殊字符:.^$?+*{}[]()|       以上特殊字符要想使用字面值,必须使用进行转义    ...

34960
来自专栏java一日一条

java提高篇之详解内部类

内部类是一个非常有用的特性但又比较难理解使用的特性(鄙人到现在都没有怎么使用过内部类,对内部类也只是略知一二)。

10720
来自专栏Brian

C++11基础学习系列二

---- 概述 在C++11基础学习系列一中介绍一些c++11一些基础知识。基础学习系列二进一步讲解C++11. string string不可思议,在C++中...

27150
来自专栏猿人谷

和为S的两个数字VS和为s的连续正数序列

题目:输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,输出任意一对即可。 例如输入数组1、2、4、7、1...

19550
来自专栏代码世界

Python之函数基础

1、函数的定义与调用 函数从大方针上考虑总共分为两种:一种是内置函数,另一种是自定义函数。今天主要讲的是自定义函数。 s = '金老板小护士' #len(s) ...

30290
来自专栏和蔼的张星的图像处理专栏

c++ primer2 变量和基本类型。

这四种初始化方式c++11都是支持的。c++11中用花括号来初始化变量得到了全面应用。

12510

扫码关注云+社区

领取腾讯云代金券