C++类型转换基本概念浅谈

C++编程语言的应用,可以帮助开发人员大大提高程序开发效率。我们今天为大家介绍的有关C++类型转换的相关内容,就是其中一个基础应用技巧。希望大家可以充分掌握这方面的知识,以方便将来的引用。

下面讨论假设你已经了解C语言程序设计和初步的C++的知识。一般来说,类型转换分为两种,即显式(Explicit)和隐式(Implicit)。

隐式类型转换:其中,隐式的C++类型转换相信大家都曾经用过甚至乎经常用,例如说把一个整形的变量赋给一个浮点数,或者在一个声明采用整形参数的函数中,以浮点数作为参数。在这些类型中,也许你可能忽略掉,但实际上你是在做类型转换,这不过是由系统自动完成而已。

显式类型转换:在C里面,你可以用(<data_type>)<Variable>这样的形式,例如用(int)('c')这样的手段来将一个字符变量或常量转换成一个整形。这种称为显式类型转换。

然后是类型兼容的问题,相信在所有的C或者C++入门教程里面都会涉及到这样的课题,例如说你可以把一个浮点数转换成一个整型来用,但是你不可以将一个整数指针转换成一个浮点数来用,等等。实际上,对于类型转换之间,转换的兼容性是一个比较复杂的概念,但是由于这些复杂的类型转换机构,使得C编程获得了更大的灵活性。但是,在C++的OOP(面向对象编程)观点看来,这无疑是一个很严重的漏洞,利用它,可以突破所有C++辛辛苦苦建立起来的类封装,甚至是常数限制。

其实,最为尖锐的问题,就体现在了指针类型的强制转换中。

希望读者还记得C里面的malloc函数和void指针,在C里面,你可以畅通无阻地将一个void指针(当然,这甚至可以为任意类型的指针)转换成任何的指针,然后让指向并修改内存中的任意一段内存,在C里面,在动态内存分配处理上<malloc.h>上面就是这样处理的。

但是,C++意识到了他其实是一个重大的漏洞,因为编程者可能在不经意间使用了这些危险的C++类型转换,利用它不经意地访问并修改了内存中一些你并不想修改但非常重要的数据单元,从而导致严重的崩溃。因此,在C++中,采取了几个重要的手段来弥补这个漏洞:

首先,C++用 new 和 delete 关键字取缔了原来拗口并且容易出错的 malloc 方法,为动态分配内存提供了另一个方案来避免类型转换;

其次,C++引进了常量模式(const),使得对于定义为 const 的变量和指针不可能被(轻易地)修改,编译系统会截获他们并给出错误信息;

在者,类封装的访问权限也很好的改善了这个情况。

但是,我们的使用者可能会发问:“假如我非得要用这些C++类型转换,以完成一些特殊的工作的时候,应该怎么办?”

答案肯定是有的,这就是 ISO C++ 提供的几种显示转换,它可以突破类封装甚至是常量限制。

这样看来,C++ 对于它的封装性,其实并不是绝对规定性的,它可以破例,即使用以下的关键字。但是他要求使用它的程序员必须清楚地知道他自己在干什么,因此对于强制类型转换提供了四个模式。这样有另一个好处,因为当程序员遇上错误的时候,这类危险的操作绝对是首选的嫌疑犯,通过查找这类关键字可以迅速的定位你的错误。下面是这四类显示转换的关键字:

static_cast: 用于良性转换(一般的转换,包括自动转换),转换的时候甚至可以不用这个关键字;

const_cast: 用于const/volatile与非const/volatile之间的转换;

reinterpret_cast: 高度危险的重翻译转换,但可以实现最灵活的C++类型转换;

dynamic_cast: 用于类型安全的向下转换。

下面详细讨论前面三种类型转换:

上面的显示类型转换函数都是要求用模版格式的,例如下文中下划线的地方注意:

// static_cast: 良性及适度良性转换,安全级:高  



int num1=50; int a[20];  




long num2 = static_cast<long>(num1); 
// 宽化转换,没有信息丢失  




char num3 = static_cast<char>(num3); 
// 窄化转换,有信息丢失,具体转换规则请参考相关教程  




void* p = static_cast<void*>(a); 
// 使用void*的强制变换,允许  




float* q = static_cast<float*>(p); 
// 使用void*的强制变换,允许  




// float* r = static_cast<float*>(a); 
// 错误,没有经过void*的强制指针类型变换,不允许 

——上面即为static_cast的用法,只能用于赋值兼容的C++类型转换,否则不能使用,安全级最高,编译器会拦截所有超出安全级别的类型转换。

// const_cast:用于const/volatile与非const/volatile之间的转换,安全级:中  



const int a = 50; volatile int b = 100;  




int* p = const_cast<int*>(&a); *p = 51;  




cout << "a=" << a << endl; // 正确的输出a应该没有变动,a=50 




cout << "*p=" << *p <<endl; 
// 但是神奇的是 *p=51,内存已经变了,但是不会刷新到const a上面  




int* q = const_cast<int*>(&b); *q = 101;  




cout << "b=" << b << endl; 
// 用volatile的话,随时刷新,因此输出b=101,已经改变  




cout << "*q=" << *q << endl; // 这时候*q自然也是*q=101 

——上面可以看到,如果使用const_cast进行显式强制类型转换,可以突破C++的常数限制,修改const指向的内存,因此有一定的危险性,但是一个程序员如果这样做的话,基本上是会意识到这个问题的,因此也还有一定的安全性。

// reinterpret_cast:最最危险的,也是最最灵活,最最万能的转换方式,安全级:低  


char str[]="This is a string.";  



float* r = reinterpret_cast<float*>(str); 
// 使用reinterpret_cast的话,这也将是合法的 

——因此,为了安全的使用reinterpret_cast,必须在结束的时候把变量转换成它原本的类型,可以想象,用一个float指针来操作一个char数组是一件多么无稽,也是多么危险的事情,因此,这样的转换方式不再万不得已的时候不要使用,如果有需要使用的时候,先想想有没有别的办法。

但是毕竟,我们在某些场合,还是不得不使用这种方法,这种方法的灵活性甚至可以直接穿透一个继承类的封装,直接从内存访问其保护成员变量,甚至可以穿透到基类的私有级别变量,要知道,用一般的方法,这根本不可能实现。

相关推荐