[深入理解C++(一)]类型转换(Type Casting)

罗朝辉 ( http://www.cnblogs.com/kesalin/)
CC许可,转载请注明出处

类型转换就是将给定类型的表达式转换为另一种类型。C++中的转型可分为两种:隐式类型转换和显式类型转换。下面将详细介绍这两种转型操作,以及各自的适用场景,潜在问题,最终将总结使用类型转换操作应牢记的原则。

一,隐式类型转换

隐式类型转换是C中的遗留物,在C++中并不推荐使用(C++有专门的转型操作符,见下文的显式转型)。将某种类型的对象拷贝到另一种不同类型的对象中时就会发生隐式转型。比如异型赋值,返回值(函数声明的返回值与代码块实际返回值不同的情况下),按值传递异型参数等情况均会发生隐式类型转换。

short a = 128;
int b;
b = a;

如上所示,short 类型的对象被赋值给 int 型的对象,这是C++语言内建支持的标准转换。

情形一:标准转换支持数值类型,bool以及某些指针之间相互转换。注意:某些转换可能会导致精度丢失,比如从 long 转换到 int。

情形二:可被单参调用(只有一个参数或多个参数但至少从第二个参数起均带有缺省值)的构造函数或隐式类型转换操作符也会引起隐式类型转换。比如:

class A {};
class B
{public: B (A a) {}public: B (int c, int d = 0);public: operator double() const;
};A a;
B b1 = a;
B b2 = 10;
B b3;
double d;
d = 10 + b3;

上面的代码里就存在只带有一个参数的构造函数,多个参数但至少从第二个参数起均带有缺省值以及用户自定义类型转换操作符这三种情况。

隐式类型转换是件麻烦事,它们很可能导致错误或非预期的函数被调用(参看ME 条款5);此外 C++ 也不能在一个转换过程中连续进行多余一次的用户自定义转换操作(即情形二中的转换),如下所示:接上面的代码,A (类型的对象,后略)可被隐式转换为 B,B 可被隐式转换为 C,但 A 却非常不合逻辑地不可被隐式转换为 C。

class C
{public: C(B b) {};
}A a;
C c;
c = a; // 错误!

因此应该尽量避免隐式类型转换,为此 C++ 提供了关键字 explicit 来规避可被单参调用的构造函数引起的隐式类型转换。但标准转换以及隐式类型转换操作符引起的转换只能交由那些追求至善至美的程序员来小心处理了。当然 C++ 语言还是提供了必要的工具来辅助那些追求至善至美的程序员,这些工具就是下面要讲的显式类型转换关键字:static_cast, const_cast, dynamic_cast 以及 reinterpret_cast。

二,显式类型转换

C++ 是一门强类型转换,因此不同自定义类型之间的转换必须进行显式转换,当然基础数据类型也可以进行显式转换。

 short a = 10;int b;b = (int) a;    // c-like cast notationb = int (a);    // functional notation

以上是基础数据类型之间进行传统的强制类型转换。这种强制类型转换可以在两种指向不同类型对象的指针之间进行,这很可能是相当危险的事情。所以 C++ 提供四种转换操作符来细分显式类型转换:

static_cast <new_type> (expression)
const_cast <new_type> (expression)
dynamic_cast <new_type> (expression)
reinterpret_cast <new_type> (expression)

static_cast

static_cast 很像 C 语言中的旧式类型转换。它能进行基础类型之间的转换,也能将带有可被单参调用的构造函数或用户自定义类型转换操作符的类型转换,还能在存有继承关系的类之间进行转换(即可将基类转换为子类,也可将子类转换为基类),还能将 non-const对象转换为 const对象(注意:反之则不行,那是const_cast的职责。)。

 double d = 3.14159265;int i = static_cast<int>(d);class A {};class B{public:B (A a) {};}; A a;B b = static_cast<B>(a);class CBase {};class CDerived: public CBase {};CBase * a = new CBase;CDerived * b = static_cast<CDerived *>(a);

注意:static_cast 转换时并不进行运行时安全检查,所以是非安全的,很容易出问题。因此 C++ 引入 dynamic_cast 来处理安全转型。

dynamic_cast 

dynamic_cast 主要用来在继承体系中的安全向下转型。它能安全地将指向基类的指针转型为指向子类的指针或引用,并获知转型动作成功是否。如果转型失败会返回null(转型对象为指针时)或抛出异常(转型对象为引用时)。dynamic_cast 会动用运行时信息(RTTI)来进行类型安全检查,因此 dynamic_cast 存在一定的效率损失。(我曾见过属于优化代码80/20法则中的20那一部分的一段游戏代码,起初使用的是 dynamic_cast,后来被换成 static_cast 以提升效率,当然这仅是权宜之策,并非好的设计。)

 class CBase { };class CDerived: public CBase { };CBase b;CBase* pb;CDerived d;CDerived* pd;pb = dynamic_cast<CBase*>(&d);     // ok: derived-to-basepd = dynamic_cast<CDerived*>(&b);  // error: base-to-derived

上面的代码中最后一行 VS2010 会报如下错误:

error C2683: 'dynamic_cast' : 'CBase' is not a polymorphic type
IntelliSense: the operand of a runtime dynamic_cast must have a polymorphic class type

这是因为 dynamic_cast 只有在基类带有虚函数的情况下才允许将基类转换为子类。

 class CBase{virtual void dummy() {}};class CDerived: public CBase{int a;};int main (){CBase * pba = new CDerived;CBase * pbb = new CBase;CDerived * pd1, * pd2;pd1 = dynamic_cast<CDerived*>(pba);pd2 = dynamic_cast<CDerived*>(pbb);return 0;}

结果是:上面代码中的 pd1 不为 null,而 pd2 为 null。

dynamic_cast 也可在 null 指针和指向其他类型的指针之间进行转换,也可以将指向类型的指针转换为 void 指针(基于此,我们可以获取一个对象的内存起始地址 const void * rawAddress = dynamic_cast<const void *> (this);)。

const_cast

前面提到 const_cast 可去除对象的常量性(const),它还可以去除对象的易变性(volatile)。const_cast 的唯一职责就在于此,若将 const_cast 用于其他转型将会报错。

 void print (char * str){cout << str << endl;}int main (){const char * c = " http://www.cppblog.com/kesalin/";print ( const_cast<char *> (c) );return 0;}

reinterpret_cast

reinterpret_cast 用来执行低级转型,如将执行一个 int 的指针强转为 int。其转换结果与编译平台息息相关,不具有可移植性,因此在一般的代码中不常见到它。reinterpret_cast 常用的一个用途是转换函数指针类型,即可以将一种类型的函数指针转换为另一种类型的函数指针,但这种转换可能会导致不正确的结果。总之,reinterpret_cast 只用于底层代码,一般我们都用不到它,如果你的代码中使用到这种转型,务必明白自己在干什么。

三,typeid:获取表达式的类型

typeid 定义在标准头文件<typeinfo>中,用于获取表达式的类型,它返回一个数据类型或类名字的字符串。当 typeid 用于自定义类型时,它使用 RTTI 信息来获取对象的动态类型。基于 typeid,我们可以构建出比较对象(动态)类型的操作。

四,使用原则:尽量避免类型转换操作;优先使用 C++ 风格的转型

1,鉴于类型转换的隐蔽,不安全,易引起非预期的函数调用,对象切割等等诸多问题,应该尽量避免类型转换操作。如使用 explicit 声明可被单参调用的构造函数,按引用传递参数或返回值,使用虚函数机制等等可避免类型转换;

2,若类型转换不可避免,优先使用 C++ 风格的新式类型转换。C++ 风格的类型转换一则易于辨识,二则有着其特有惯用手法,遵循这些惯用手法好处多多。

五,测验:

class A {
public :
virtual ~A () {}
}class B : private virtual A { }
class C : public A {}
class D : public B, public C {}

对于上述类,定义如下对象与引用:

A a1, B b1, C c1; D d1;
const A a2;
const A& ca1 = a1;
const A& ca2 = a2;

下面四个赋值表达式都正确么?如果正确又分别对应哪一种 C++ 风格的转换?
         A * pa; B * pb; C * pc;
(一)pa = (A*) & ca1;
(二)pa = (A*) & ca2;
(三)pb = (B*) & c1;
(四)pc = (C*) & d1;

引用:
1,<Effective C++> 条款 27
2,<More Effective C++> 条款 02
3,http://www.cplusplus.com/doc/tutorial/typecasting/

转载于:https://www.cnblogs.com/kesalin/archive/2012/10/28/type_cast.html

[深入理解C++(一)]类型转换(Type Casting)相关推荐

  1. Python编程基础:第四节 类型转换Type Cast

    第四节 类型转换Type Cast 前言 整型(int) 浮点型(float) 字符串型(str) 前言 在数据处理过程中,我们有时需要将数据类型进行强制转换.例如做整数加法时,我们需要将所有变量类型 ...

  2. Type Casting

    Type Casting   C++ : Documents : C++ Language Tutorial : Type Casting   Search: user pass [register] ...

  3. java的type转化class_第七章 (类型转换)Type Convertion

    第七章 Type Convertion 为什么会有类型转换? HTTP协 议中传递的任何内容都是String类型的,所以一旦我们在服务器上需要一个非String类型的对象,例如:int或者Date,那 ...

  4. 深入理解C语言类型转换

    类型转换简要介绍 C语言类型转换分为两种: 显式强制类型转换 隐式自动类型转换 也许你曾经遇到过一个负数经过类型转换后可能变成一个很大的整数之类的现象,却不知道实质上是因为什么,只是浅显地了解到类型转 ...

  5. [翻译]JavaScript秘密花园 - Type Casting,undefined,eval,setTimeout,Auto Semicolon Insertion - 全部完成PDF打包下载

    JavaScript Garden - 原文 JavaScript Garden - 中文翻译 PDF打包下载 类型转换 JavaScript 是弱类型语言,所以会在任何可能的情况下应用强制类型转换. ...

  6. C语言中conversion用法,C语言类型转换(type conversions in C)

    java/C++这类语言对类型的要求非常严格,但是C语言对类型几乎没什么限制. 它可以非常灵活的处理不同类型之间的转换. 正因为它的这种灵活性,也导致了程序员在使用各种混合的数据类型时,并不关心这些类 ...

  7. 转换php script类型,ECMAScript 类型转换

    ECMAScript 类型转换 所有程序设计语言最重要的特征之一是具有进行类型转换的能力. ECMAScript 给开发者提供了大量简单的类型转换方法. 大部分类型具有进行简单转换的方法,还有几个全局 ...

  8. Airbnb 规范大全

    想要Xmind版的请关注私聊我,写的很全了. 前端的基本都概括了. 命名编码规范 驼峰式命名法介绍 ·      Pascal Case 大驼峰式命名法:首字母大写.eg:StudentInfo.Us ...

  9. mach空串 php preg_PHP 优化详解

    笔者收集的这些技巧来源较广,完整性不能保证. 由于数量较多,这些优化技巧没有经过测试.请各位看官在使用之前自行测试,毕竟这些技巧是否能派上用场,还是需要由PHP所在的独特环境所决定的. 目录索引 找到 ...

  10. php怎样解析数组,PHP 数组 Array 解析

    官方介绍 PHP 数组实际上是一个有序的映射(map),映射是一种把多个值(values)关联到对应的多个键(keys)的类型.这种映射类型在很多方面做了优化,因此可以把它当成真正的数组(就是上面说的 ...

最新文章

  1. c# 元组Tuple
  2. python爬取单个网页照片!
  3. Android备份和添加短信
  4. 新手坐高铁怎么找车厢_一女子坐高铁回桂平坐过站,到了平南南站,怎么办?...
  5. php memcached 扩展下载,编译安装 PHP 的 Memcached 扩展
  6. 从阿里中台战略看企业IT架构转型之道(下)
  7. restful和rest_HATEOAS的RESTful服务:JVM上的REST API和超媒体
  8. linux强行卸载qt,Linux下卸载QT SDK
  9. C++或C 实现AES ECB模式加密解密,支持官方验证
  10. 【Linux使用】Centos 7安装图形界面/切换文本界面与图形界面
  11. 信息论与编码_庆祝中山大学计算机科学系成立40周年系列活动 | 第四届“信息论与编码中大论坛”...
  12. 图像变换——图像反转
  13. html制作排班表模板,你会制作排班表吗?手把手教你用Word制作!分分钟搞定!...
  14. latex 插入bibtex 的文献。在参考文献上方多了一行序号,解决办法参考
  15. 基于Node.js的ORM框架 Prisma的上手使用
  16. NN中的学习技巧之(一)参数的最优化之 Momentum
  17. linux下使用python截图_linux多线程网页截图-python
  18. MySQL Workbench建表时 PK NN UQ BIN UN ZF AI 的含义
  19. PEEK薄膜特性与各型号性能特征分析
  20. 【STM32】NVIC中断优先级管理(中断向量表)

热门文章

  1. 每日一题(开开森森学前端之Object系列)
  2. ExtJS Grid中文字链接点击事件的更合理的处理办法
  3. 借Java EE守护者联盟之力拯救Java EE
  4. 从MySQL复制功能中得到一举三得实惠
  5. Salt: 2015.8.8.2 L@匹配BUG
  6. [C# 基础知识系列]专题十一:匿名方法解析
  7. 软件应用:HexorBase Tool 实战测试!
  8. 使用一个插件将Zend Framework应用程序的内容转换为xml
  9. Linux 重启命令
  10. 3月4日 第1人称相机世界的坐标系,焦距、焦点、调焦、超焦距、视场角、滑动变焦Dolly zooming,相机内参