关闭

More Effective C++议题【二】:尽量使用C++风格的类型转换

标签: static_castconst_castdynamic_castreinterpret_castC++类型转换
617人阅读 评论(0) 收藏 举报
分类:

<<More Effective C++>> 学习笔记【二】

C风格的类型转换的缺点:

  1. 过于粗鲁: 允许在任何类型间进行转换。
  2. 代码阅读时在程序语句中难以识别。

其中第一点在转换不同行为类型中体现出了巨大的差异性,建议中的例子包括 :

  • const 转换为非const:去除const的类型.
  • 基类指针转换成子类指针:完全改变对象类型,实际上是对于内存空间的变化。

而C风格转换并不会区分。

---------------------------------------

C++通过引进四个新的类型转换操作符克服了C风格类型转换的缺点,这四个操作符是, static_cast, const_cast, dynamic_cast, 和reinterpret_cast。

static_cast:

假设你想把一个int转换成double,以便让包含int类型变量的表达式产生出浮点数值的结果:

    int firstNumber, secondNumber;
    //...
    double result = ( (double)firstNumber)/secondNumber;		//C风格
    double result = static_cast<double>(firstNumber)/secondNumber;	//C++风格
static_cast在功能上基本上与C风格的类型转换一样强大,含义也一样。
它有如下功能上限制:

  • 你不能用static_cast象用C风格的类型转换一样把struct转换成int类型或者把double类型转换成指针类型,
  • 不能从表达式中去除const属性,因为另一个新的类型转换操作符const_cast有这样的功能。

const_cast:

const_cast用于类型转换掉表达式的const或volatileness属性。通过使用const_cast,你向人们和编译器强调你通过类型转换想做的只是改变一些东西的constness或者 volatileness属性。

class Widget { ... };
class SpecialWidget: public Widget { ... };

void update(SpecialWidget *psw);

    SpecialWidget sw; // sw 是一个非const 对象。
    const SpecialWidget& csw = sw; // csw 是sw的一个引用, 它是一个const 对象
    update(&csw); // 错误!不能传递一个const量给一个处理non-const类型变量的函数
    update(const_cast<SpecialWidget*>(&csw));//正确!csw的const被C++风格显示地转换掉,变量能够得到更新.
    update((SpecialWidget*)&csw);// 正确!与上面作用相同的C风格的类型转换

    Widget *pw = new SpecialWidget;
    update(pw); // 错误!指向子类的父类指针传递给只接受子类指针的函数.
    update(const_cast<SpecialWidget*>(pw));// 错误!const_cast仅能被用在影响constness or volatileness的地方上,不能用在向继承子类进行类型转换。

对于父类和子类指针间的转换,关系到了动态类型转换,所以这个类型转换符叫做Dynamic_cast

dynamic_cast:

dynamic_cast被用于安全地沿着类的继承关系向下进行类型转换。
这就是说用dynamic_cast把指向基类的指针或引用转换成指向其派生类或其兄弟类的指针或引用,
而且你能知道转换是否成功:失败的转换将返回空指针(当对指针进行类型转换时)或者抛出异常(当对引用进行类型转换时):

Widget *pw;
    //...
    update(dynamic_cast<SpecialWidget*>(pw));
    // 正确,传递给update函数一个子类类型的指针
    // 如果pw没有指向一个对象传递过去的将使空指针。
dynamic_casts在帮助你浏览继承层次上是有限制的。它不能被用于缺乏虚函数的类型上(参见条款M24),也不能用它来转换掉constness:

     int firstNumber, secondNumber;
     //...
     double result = dynamic_cast<double>(firstNumber)/secondNumber;// 错误!没有继承关系
     const SpecialWidget sw;
     //...
     update(dynamic_cast<SpecialWidget*>(&sw));// 错误! dynamic_cast不能转换掉const。
如你想在没有继承关系的类型中进行转换,你可能想到static_cast。如果是为了去除const,你总得用const_cast

reinterpret_cast:

reinterpret_cast操作符的类型转换,往往是根据实现定义(implementation-defined)。这里侯捷的翻译似乎有问题.
在《The C++ Programming Language》中,对这个转换符作用的定义如下:
"reinterpret_cast handles conversions between unrelated types such as an integer to a pointer
or a pointer to an unrelated pointer type (§iso.5.2.10)."

而他的作用则是直接转换类似于如下的情况:
IO_device∗ d1 = reinterpret_cast<IO_device∗>(0Xff00); // device at 0Xff00
/*There is no way a compiler can know whether the integer 0Xff00 is a valid address (of an I/O device
register). Consequently, the correctness of the conversions is completely in the hands of the programmer.*/

所以这个转换的局限性很强,应用领域较窄,并且基本无法移植.
reinterpret_casts的最普通的用途就是在函数指针类型之间进行转换。例如,假设你有一个函数指针数组:
     typedef void (*FuncPtr)(); // FuncPtr is 一个指向函数的指针,该函数没有参数,返回值类型为void
     FuncPtr funcPtrArray[10]; // funcPtrArray 是一个能容纳10个FuncPtrs指针的数组
     //假设你希望(因为某些莫名其妙的原因)把一个指向下面函数的指针存入funcPtrArray数组:
     int doSomething();
     funcPtrArray[0] = &doSomething; // 错误!数组里的函数返回值是void类型,而doSomething返回值int类型。
     //使用reinterpret_cast可以让你迫使编译器以你的方法去看待它们:
     funcPtrArray[0] = reinterpret_cast<FuncPtr>(&doSomething);//可以骗过编译器!

如上代码中,转换函数指针的代码是不可移植的(C++实现中不保证所有的函数指针都被用一样的方法表示),在一些情况下这样的转换会产生不正确的结果(参见条款M31),所以你应该避免转换函数指针类型,除非你处于着背水一战和尖刀架喉的危急时刻。一把锋利的刀。一把非常锋利的刀。尴尬

从《The C++ Programming Language》中的补充:


For logical and historical reasons, C++ offers explicit type conversion operations of varying convenience and safety:
  • Construction, using the {} notation, providing type-safe construction of new values (§11.5.1)
  • Named conversions, providing conversions of various degrees of nastiness:
    •  const_cast for getting write access to something declared const (§7.5)
    •  static_cast for reversing a well-defined implicit conversion (§11.5.2)
    •  reinterpret_cast for changing the meaning of bit patterns (§11.5.2)
    •  dynamic_cast for dynamically checked class hierarchy navigation (§22.2.1)
  • C-style casts, providing any of the named conversions and some combinations of those(§11.5.3)
  • Functional notation, providing a different notation for C-style casts (§11.5.4)
又及:《The C++ Programming Language》是本圣经,如果他看全了,不需要什么effective..more effective了就.




0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场