现在的位置: 首页 > 综合 > 正文

条款2:最好使用C++转型操作符(1)

2014年02月03日 ⁄ 综合 ⁄ 共 3160字 ⁄ 字号 评论关闭

想想低阶转型动作。它几乎像goto一样被视为程序设计上的"贱民"。尽管如此,它却仍能够苟延残喘,因为当某种情况愈来愈糟,转型可能是必要的。是的,当某种情况愈来愈糟,转型是必要的!

不过,旧式的C转型方式并非是唯一选择。它几乎允许你将任何类型转换为任何其他类型,这是十分拙劣的。如果每次转型都能够更精确地指明意图,则更好。举个例子,将一个 pointer-to-const-object 转型为一个 pointer-to-non-const-object(也就是说只改变对象的常量性),和将一个 pointer-to-base-class-object 转型为一个pointer-to-derived-class-object(也就是完全改变了一个对象的类型),其间有很大的差异。传统的 C
转型动作对此并无区分(这应该不会造成你的惊讶,因为 C 式转型是为 C 设计的,不是为了 C++)。

旧式转型的第二个问题是它们难以辨识。旧式转型的语法结构是由一对小括号加上一个对象名称(标识符)组成,而小括号和对象名称在 C++ 的任何地方都有可能被使用。因此,我们简直无法回答最基本的转型相关问题"这个程序中有使用任何转型动作吗?"。因为人们很可能对转型动作视而不见,而诸如 grep 之类的工具又无法区分语法上极类似的一些非转型写法。

为解决C 旧式转型的缺点,C++ 导入4个新的转型操作符(cast operators):static_cast,const_cast,dynamic_cast 和reinterpret_cast。对大部分使用目的而言,面对这些操作符你唯一需要知道的便是,过去习惯的写码形式:

  1. (type) expression 

现在应该改为这样:

  1. static_cast<type>(expression) 

举个例子,假设你想要将一个 int 转型为一个double,以强迫一个整数表达式导出一个浮点数值来。采用 C 旧式转型,可以这么做:

  1. int firstNumber, secondNumber;  
  2. ...  
  3. double result = ((double)firstNumber)/secondNumber; 

如果采用新的 C++ 转型法,应该这么写:

  1. double result = static_cast<double>(firstNumber)/secondNumber; 

这种形式十分容易被辨识出来,不论是对人类或是对工具程序而言。

static_cast 基本上拥有与 C 旧式转型相同的威力与意义,以及相同的限制。例如,你不能够利用 static_cast 将一个 struct 转型为int,或将一个 double 转型为 pointer;这些都是 C 旧式转型动作原本就不可以完成的任务。static_cast 甚至不能够移除表达式的常量性(constness),因为有一个新式转型操作符 const_cast 专司此职。

其他新式 C++ 转型操作符适用于更集中(范围更狭窄)的目的。const_cast 用来改变表达式中的常量性(constness)或变易性(volatileness)。使用 const_cast, 便是对人类(以及编译器)强调 ,通过这个转型操作符,你唯一打算改变的是某物的常量性或变易性。这项意愿将由编译器贯彻执行。如果你将 const_cast 应用于上述以外的用途,那么转型动作会被拒绝。下面是个例子:

  1. class Widget { ... };  
  2. class SpecialWidget: public Widget { ... };  
  3. void update(SpecialWidget *psw);  
  4. SpecialWidget sw;                 // sw 是个 non-const 对象,  
  5. const SpecialWidget& csw = sw;   // csw 却是一个代表 sw 的 reference,  
  6.                                      // 并视之为一个 const 对象。  
  7.  
  8. update(&csw);           // 错误!不能将 const SpecialWidget*  
  9.                             // 传给一个需要 SpecialWidget* 的函数。  
  10.  
  11. update(const_cast<SpecialWidget*>(&csw));  
  12.                          // 可!&csw 的常量性被去除了。也因此,  
  13.                          // csw(亦即 sw)在此函数中可被更改。  
  14.  
  15. update((SpecialWidget*)&csw);  
  16.                          // 情况同上,但使用的是较难辨识  
  17.                          // 的 C 旧式转型语法。  
  18.  
  19. Widget *pw = new SpecialWidget;  
  20. update(pw);            // 错误!pw 的类型是 Widget*,但  
  21.                          // update() 需要的却是 SpecialWidget*。  
  22.  
  23. update(const_cast<SpecialWidget*>(pw));  
  24.                          // 错误!const_cast 只能用来影响  
  25.                          // 常量性或变易性,无法进行继承体系  
  26.                          // 的向下转型(cast down)动作。 

显然,const_cast 最常见的用途就是将某个对象的常量性去除掉。

第二个特殊化的转型操作符是dynamic_cast,用来执行继承体系中"安全的向下转型或跨系转型动作"。也就是说你可以利用 dynamic_cast,将"指向 base class objects的pointers或references"转型为"指向derived(或sibling base)class objects 的 pointers 或 references",并得知转型是否成功 。如果转型失败,会以一个 null 指针(当转型对象是指针)或一个 exception(当转型对象是 reference)表现出来:

  1. Widget *pw;  
  2. ...  
  3. update(dynamic_cast<SpecialWidget*>(pw));  
  4.                          // 很好,传给 update() 一个指针,指向  
  5.                          // pw 所指的 SpecialWidget--如果 pw  
  6.                          // 真的指向这样的东西;否则传过去的  
  7.                          // 将是一个 null 指针。  
  8.  
  9. void updateViaRef(SpecialWidget& rsw);  
  10.  
  11. updateViaRef(dynamic_cast<SpecialWidget&>(*pw));  
  12.                          // 很好,传给 updateViaRef() 的是  
  13.                          // pw 所指的 SpecialWidget--如果  
  14.                          // pw 真的指向这样的东西;否则  
  15.                          // 抛出一个 exception。

抱歉!评论已关闭.