Implementations-27,30

27.Minimize casting.(尽量少做转型动作)
C++规则的设计目标之一是,保证“类型错误”绝不可能发生。不幸的是,转型(casts)破坏了类型系统。

(1)回顾一下转型语法,通常有三种不同的形式:

1)C风格的转型动作:
(T)expression    //将expression转型为T

2)函数风格的转型动作:
T(expression)    //将expression转型为T

以上两种形式并无差别,纯粹只是小括号的摆放位置不同而已。我们称此两种形式为“旧式转型”。

3)C++还提供四种新式转型:

  • const_cast<T>(expression)
  • dynamic_cast<T>(expression)
  • reinterpret_cast<T>(expression)
  • static_cast<T>(expression)

其中,

a)const_cast通常被用来将对象的常量性转除(cast away the constness)。

b)dynamic_cast主要用来执行“安全向下转型”(safe downcasting),也就是用来决定某个对象是否归属继承体系中的某个类型。

c)reinterpret_cast意图执行低级转型,实际动作(及结果)可能取决于编译器,这也就表示它不可移植。

d)static_cast用来强迫隐式转换(implict conversions),例如将non-const对象转换为const对象,或将int转为double等等。

4)旧式转型仍然合法,但新式转型较受欢迎。鼓励使用新式转型,但我唯一使用旧式转型的情况是:当我要调用一个explicit构造函数将一个对象传递给一个函数时,如下

(2)优良的C++代码很少使用转型,但若说要完全摆脱它们又太过不切实际。

请记住:
(1)如果可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_cast。
(2)如果转型是必要的,试着将它隐藏于某个函数背后。客户随后可以调用该函数,而不需将转型放进他们自己的代码内。
(3)宁可使用C++ style转型,不要使用旧式转型。

30.Understand the ins and outs of inlining.(透彻了解inlining的里里外外)

(1)Inline函数,看起来像函数,动作像函数,比宏好得多,可以调用它们又不需要蒙受函数调用所招致的额外开销。

(2)不仅如此,编译器最优化机制通常被设计用来浓缩那些“不含函数调用”的代码,所以当你inline某个函数,或许编译器就因此有能力对它执行语境相关最优化。

(3)inline函数背后的观念是:将“对此函数的每一个调用”都以函数本体替换之。这样做可能增加你的目标码(object code)大小。过度热衷inlining会造成程序体积太大;换个角度说,如果inline函数的本体很小,编译器针对“函数本体”所产出的码可能比针对“函数调用”所产出的码更小。

(4)记住,inline只是对编译器的一个申请,不是强制命令。这项申请可以隐喻提出也可以明确提出。

1)隐喻方式是将函数定义于class定义式内

这样的函数通常是成员函数,但条款46说friend函数也可被定义于class内,如果真的那样,它们也是被隐喻声明为inline。

2)明确声明inline函数的做法是在其定义式前加上关键字inline。

inline函数通常一定被置于头文件内,因为大多数build environments在编译过程中进行inlining,而为了将一个“函数调用”替换为“被调用函数的本体”,编译器必须知道那个函数长什么样子。

3)大部分编译器拒绝将太过复杂的函数inlining,而对所有virtual函数的调用也都会使inlining落空。因为virtual直到运行期才确定调用哪个函数,而inline意味“执行前,先将调用动作替换为被调用函数的本体”。

(5)这些叙述整合起来的意思是:一个表面上看似inline的函数是否真是inline,取决于你的创建环境build environment,主要取决于编译器。辛运的是,大多数编译器提供了一个诊断级别:如果它们无法将你要求的函数inline化,会给你一个警告信息。

请记住:
(1)将大多数inlining限制在小型、被频繁调用的函数身上。这可使日后的调试过程和二进制升级(binary upgradability)更容易,也可使潜在的代码膨胀问题最小化,是程序的速度提升机会最大化。
(2)不要只因为function template出现在头文件,就将它们声明为inline。

发表评论

电子邮件地址不会被公开。 必填项已用*标注