`
廖世勇
  • 浏览: 90570 次
  • 性别: Icon_minigender_1
  • 来自: 湖南郴州
社区版块
存档分类
最新评论

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

    博客分类:
  • C++
c++ 
阅读更多
• C++类型转换基本概念浅谈
• http://developer.51cto.com  2010-02-03 11:11  佚名  博客园  我要评论(0)
C++类型转换总共可以分为两种,一个是显式的类型转换,另一种则是隐式的类型转换。今天我们将会针对这些内容进行一个详细介绍。
C++编程语言的应用,可以帮助开发人员大大提高程序开发效率。我们今天为大家介绍的有关C++类型转换的相关内容,就是其中一个基础应用技巧。希望大家可以充分掌握这方面的知识,以方便将来的引用。
o C++动态数组应用方式探讨
o 各种C++继承方式范例分析
o C++操作符重载实际操作方法解析
o C++多态实现方法探讨
o 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: 用于类型安全的向下转换。
下面详细讨论前面三种类型转换:
上面的显示类型转换函数都是要求用模版格式的,例如下文中下划线的地方注意:
6. // static_cast: 良性及适度良性转换,安全级:高 
7. int num1=50; int a[20]; 
8. long num2 = static_cast<long>(num1);
// 宽化转换,没有信息丢失 
9. char num3 = static_cast<char>(num3);
// 窄化转换,有信息丢失,具体转换规则请参考相关教程 
10. void* p = static_cast<void*>(a);
// 使用void*的强制变换,允许 
11. float* q = static_cast<float*>(p);
// 使用void*的强制变换,允许 
12. // float* r = static_cast<float*>(a);
// 错误,没有经过void*的强制指针类型变换,不允许
——上面即为static_cast的用法,只能用于赋值兼容的C++类型转换,否则不能使用,安全级最高,编译器会拦截所有超出安全级别的类型转换。
13. // const_cast:用于const/volatile与非const/volatile之间的转换,安全级:中 
14. const int a = 50; volatile int b = 100; 
15. int* p = const_cast<int*>(&a); *p = 51; 
16. cout << "a=" << a << endl; // 正确的输出a应该没有变动,a=50
17. cout << "*p=" << *p <<endl;
// 但是神奇的是 *p=51,内存已经变了,但是不会刷新到const a上面 
18. int* q = const_cast<int*>(&b); *q = 101; 
19. cout << "b=" << b << endl;
// 用volatile的话,随时刷新,因此输出b=101,已经改变 
20. cout << "*q=" << *q << endl; // 这时候*q自然也是*q=101
——上面可以看到,如果使用const_cast进行显式强制类型转换,可以突破C++的常数限制,修改const指向的内存,因此有一定的危险性,但是一个程序员如果这样做的话,基本上是会意识到这个问题的,因此也还有一定的安全性。
21. // reinterpret_cast:最最危险的,也是最最灵活,最最万能的转换方式,安全级:低 
22. char str[]="This is a string."; 
23. float* r = reinterpret_cast<float*>(str);
// 使用reinterpret_cast的话,这也将是合法的
——因此,为了安全的使用reinterpret_cast,必须在结束的时候把变量转换成它原本的类型,可以想象,用一个float指针来操作一个char数组是一件多么无稽,也是多么危险的事情,因此,这样的转换方式不再万不得已的时候不要使用,如果有需要使用的时候,先想想有没有别的办法。
但是毕竟,我们在某些场合,还是不得不使用这种方法,这种方法的灵活性甚至可以直接穿透一个继承类的封装,直接从内存访问其保护成员变量,甚至可以穿透到基类的私有级别变量,要知道,用一般的方法,这根本不可能实现。
实际上,上面的显示C++类型转换只是一个习惯,其实大可以不作这样的显示类型转换声明,只不过后果可能比较严重,尤其是对经验不丰富的程序员来说。可以想象当你的程序遇到奇怪的运行错误甚至是崩溃的时候,最大危险的地方很有可能就出现在这些莫名其妙的类型转换中,假如在编码的时候没有让自己充分地意识到这个问题,也许你的程序崩溃会传染给你本人(你自己也一起崩溃(~_~)),但如果意识到了这类问题,并且在编码中用上述提倡的显式类型转换标识你的代码,这样也许你会很快的查找到错误的所在。
C的这种灵活性也许正成为了它最为人所诟病的地方,灵活性是一面双刃剑,也许体会到这种用法的优点,才算是从C上升到了C++
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics