C++中的显式类型转化

简介:   类型转化也许大家并不陌生,int i; float j; j = (float)i; i = (int)j; 像这样的显式转化其实很常见,强制类型转换可能会丢失部分数据,所以如果不加(int)做强制转换,严检查的编译会报错,宽检查的编译会报warning。

  类型转化也许大家并不陌生,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   }
 

 

真正重要的东西,用眼睛是看不见的。
相关文章
|
5月前
|
存储 程序员 C语言
c++primer plus 6 读书笔记 第四章 复合类型
c++primer plus 6 读书笔记 第四章 复合类型
|
1月前
|
存储 编译器 程序员
C++类型参数化
【10月更文挑战第1天】在 C++ 中,模板是实现类型参数化的主要工具,用于编写能处理多种数据类型的代码。模板分为函数模板和类模板。函数模板以 `template` 关键字定义,允许使用任意类型参数 `T`,并在调用时自动推导具体类型。类模板则定义泛型类,如动态数组,可在实例化时指定具体类型。模板还支持特化,为特定类型提供定制实现。模板在编译时实例化,需放置在头文件中以确保编译器可见。
30 11
|
2月前
|
安全 程序员 C语言
C++(四)类型强转
本文详细介绍了C++中的四种类型强制转换:`static_cast`、`reinterpret_cast`、`const_cast`和`dynamic_cast`。每种转换都有其特定用途和适用场景,如`static_cast`用于相关类型间的显式转换,`reinterpret_cast`用于低层内存布局操作,`const_cast`用于添加或移除`const`限定符,而`dynamic_cast`则用于运行时的类型检查和转换。通过具体示例展示了如何正确使用这四种转换操作符,帮助开发者更好地理解和掌握C++中的类型转换机制。
|
3月前
|
C++
使用 QML 类型系统注册 C++ 类型
使用 QML 类型系统注册 C++ 类型
50 0
|
4月前
|
编译器 C++ 运维
开发与运维函数问题之函数的返回类型如何解决
开发与运维函数问题之函数的返回类型如何解决
36 6
|
3月前
|
存储 C++
【C/C++学习笔记】string 类型的输入操作符和 getline 函数分别如何处理空白字符
【C/C++学习笔记】string 类型的输入操作符和 getline 函数分别如何处理空白字符
38 0
|
4月前
|
安全 编译器 C++
C++一分钟之-模板元编程实例:类型 traits
【7月更文挑战第15天】C++的模板元编程利用编译时计算提升性能,类型traits是其中的关键,用于查询和修改类型信息。文章探讨了如何使用和避免过度复杂化、误用模板特化及依赖特定编译器的问题。示例展示了`is_same`类型trait的实现,用于检查类型相等。通过`add_pointer`和`remove_reference`等traits,可以构建更复杂的类型转换逻辑。类型traits增强了代码效率和安全性,是深入C++编程的必备工具。
69 11
|
4月前
|
C++
C++一分钟之-类型别名与using声明
【7月更文挑战第20天】在C++中,类型别名和`using`声明提升代码清晰度与管理。类型别名简化复杂类型,如`using ComplexType = std::vector&lt;std::shared_ptr&lt;int&gt;&gt;;`,需注意命名清晰与适度使用。`using`声明引入命名空间成员,避免`using namespace std;`全局污染,宜局部与具体引入,如`using math::pi;`。恰当应用增强代码质量,规避常见陷阱。
66 5
|
3月前
|
设计模式 安全 IDE
C++从静态类型到单例模式
C++从静态类型到单例模式
35 0
|
4月前
|
C++ 开发者
C++一分钟之-概念(concepts):C++20的类型约束
【7月更文挑战第4天】C++20引入了Concepts,提升模板编程的类型约束和可读性。概念定义了模板参数需遵循的规则。常见问题包括过度约束、约束不完整和重载决议复杂性。避免问题的关键在于适度约束、全面覆盖约束条件和理解重载决议。示例展示了如何用Concepts限制模板函数接受的类型。概念将增强模板的安全性和灵活性,但需谨慎使用以防止错误。随着C++的发展,Concepts将成为必备工具。
91 2