****************************  一、
Accustoming Yourself to C++ ****************************

条款02: Prefer consts,enums,and inlines to #defines

上一个条款,让我正确认识C++,并不是是一个一体语言,而是一个联邦,

而这一个条款。是在纠正我们的一些行为习惯。

尽可能的用const、enum、inline 而非 #define

这个就要扯到
编译器 和 预处理器 的方面了,

#define 所设置的内容会在预处理器的时候被移走,因此编译器会看不到它。

这时候。会出现这样的问题:

#define ASPECT_RATIO 1.653

而当 ASPECT_RATIO 出问题时。不会报错这个常量。而是报错  1.653 这个数字,

——Why?

所使用的名称可能并未进入到symbol table 内。

在编译器中,会将这些变量常量名称  归纳到 symbol table(记号表)内。

可是,这个常量被预处理器移走了。因此。没有进入 symbol table,所以假设编译出错,错误信息会提到1.653 而非ASPECT_RATIO

这就对我们追踪,查错造成非常多不必要的时间上的浪费。

假设用const取代:

const double AspectRatio = 1.653;  // 由于非#define 名称也要有对应规范的变化

这时,这个常量名就会进入 symbol table。因此假设编译出错,错误信息回是 AspectRatio

不只如此。对于浮点常量而言,使用常量const可能比#define导致较小量的码,由于预处理器“盲目”将宏名称 ASPECT_RATIO 替换成 1.653 可能导致 object code ( 目标码 )出现多份 1.653 ,若使用const 则会杜绝同样情况。

还有,对于const 替换 #define 还有两种特殊情况:

① 定义 constant pointers (常量指针)

在头文件定义一个常量指针须要用两个const,我的理解是,一个定义指针不能指向别的地址。一个是定义指针内容不可变。

这个详细还会在下一个条款详细讨论。

再有一点就是对于定义一个字符串。

用 const std::string authorName("Scott Meyers");

优于 const char* const authorName = "Scott Meyers";

② 关于class的专属常量

这是有关于 scope(域) 的一个概念,假设想将一个常量作用域限制于一个类内。你就必须让这个常量成为类内的一个 member(成员),这时为了让这个类内常量至多仅仅有一个实体,还必须让它成一个
static(静态) 成员。

当然,这个用#define做不到,由于#define 并不重视 作用域,一旦被定义,它在其后的编译过程都有效,除非在某处#undef。所以 const 是可被封装的。

另一点,在类内的const是声明式而非定义式,假设编译器非要求你出示一个定义式给它,能够这样实现:

class GamePlayer
{

private:

static const int NumTurns = 5;

int scores[NumTurns];

......

};

const int GamePlayer::NumTurns;
// 常量已在声明时获得初值。就无须再设初值

这就是 "in-class" 初值设定,

这个设定也有限制,要求class内的常量static并且integral type(整数类型){比如:ints,chars,bools}

但此处限制是对于整数类型的,假设是其它类型。能够类内声明不设初值。直接在类外定义时再设初值。

OK,关于const 说完,接下来就是 enum

对于上述情况,假设类在编译期间须要一个class常量值,而恰巧编译器不支持或不同意 static 整数型 class 常量 完毕 in-class 初值设定,那就能够使用
the enum hack 补偿方法。

它的理论基础是——
一个属于 enumerated type(枚举类型)的数值可权充int被使用

例:

class GamePlayer{
private:
enum { NumTurns = 5 };
int scores[NumTurns];
......
};

当然,enum作用不只这样,有非常多理由让我们。必须认识它


enum hack 行为比較像 #define 而非 const 。取const地址是合法的。而取enum和#define地址一般是不合法的,enum 和 #define 一样绝不会导致非必要内存的浪费

② enum hack  是 template metaprogramming (模板元编程)的基础技术

then 是 inline 方面

有一个常见的#define误用情况是用它来实现macros(宏)。

宏看起来像函数,但不会招致 function call (函数调用)带来额外开销。

以下这个样例,就是宏夹着宏实參,调用函数f:

// 以a和b的较大值调用f
#define CALL_WITH_MAX(a,b) f( (a) > (b) ? (a) : (b) )

说实话。宏长这样真的会让人痛苦啊。。。

这还仅仅是简单的取较大的调用f,假设复杂点的函数,那画面不敢想!

PS:要注意。不管什么时候写带实參的宏,要为宏中全部的实參带上小括号。

调用它的样例:

int a = 5 , b = 0 ;
CALL_WITH_MAX(++a,b); // a被累加两次
CALL_WITH_MAX(++a,b+10); // a被累加一次

这里,调用f之前,a的累加次数。居然取决于“它被拿来和谁比較”!

这时,你就须要一个 template inline 函数 解决这个问题:

template<typename T>
inline void callWithMax( const T& a, const T& b){
f( a > b ? a : b );
}

此外,因为这是一个真正的函数,它遵守scope(作用域)的概念和訪问规则,因此。全然能够封装在类内,当然这是宏无法做到的。

结束语:

尽管 有了const、enum、inline以后,我们对预处理器,尤其是 #define 的需求减少了,但并不是全然消除。#include 仍然是必需品,#ifdef/#ifndef也继续扮演控制编译的重要角色。眼下还没有到预处理器隐退的时候,可是我们能够明白的给它更长的假期。

Please remember:

① 对于单纯常量。最好以const 对象或者enum 替换 #define

② 对于形似函数的宏,最好改用 inline函数 替换 #define

End....

版权声明:本文博主原创文章,博客,未经同意不得转载。

《Effective C++ 》学习笔记——条款02的更多相关文章

  1. Effective C++学习笔记 条款02:尽量以const,enum,inline替换 #define

    尽量使用const替换 #define定义常量的原因: #define 不被视为语言的一部分 宏定义的常量,预处理器只是盲目的将宏名称替换为其的常量值,导致目标码中出现多分对应的常量,而const定义 ...

  2. Effective C++学习笔记 条款07:为多态基类声明virtual析构函数

    一.C++明确指出:当derived class对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,其结果未定义——实际执行时通常发生的是对象的 ...

  3. Effective C++学习笔记 条款06:如不想使用编译器自动生成的函数,就该明确拒绝

    一.为驳回编译器自动提供的机能,可将相应成员函数声明为private并且不予实现.(如果你仅仅是自己不实现的话,编译器会帮你实现) 如: class A { public: A(const strin ...

  4. Effective C++学习笔记 条款05:了解C++默默编写并调用的哪些函数

    一.如果用户没有提供构造函数.copy构造函数.copy assignment操作符和析构函数,当且仅当这些函数被需要的时候,编译器才会帮你创建出来.编译器生成的这些函数都是public且inline ...

  5. Effective C++学习笔记 条款04:确定对象被使用前已先被初始化

    一.为内置类型对象进行手工初始化,因为C++不保证初始化它们. 二.对象初始化数据成员是在进入构造函数用户编写代码前完成,要想对数据成员指定初始化值,那就必须使用初始化列表. class A { pu ...

  6. Effective STL 学习笔记 39 ~ 41

    Effective STL 学习笔记 39 ~ 41 */--> div.org-src-container { font-size: 85%; font-family: monospace; ...

  7. Effective STL 学习笔记 Item 38 : Design functor classes for pass-by-value

    Effective STL 学习笔记 Item 38 : Design functor classes for pass-by-value */--> div.org-src-container ...

  8. Effective STL 学习笔记 Item 34: 了解哪些算法希望输入有序数据

    Effective STL 学习笔记 Item 34: 了解哪些算法希望输入有序数据 */--> div.org-src-container { font-size: 85%; font-fam ...

  9. Effective STL 学习笔记 32 ~ 33

    Effective STL 学习笔记 32 ~ 33 */--> div.org-src-container { font-size: 85%; font-family: monospace; ...

随机推荐

  1. 技术回归01-Windows内存分配工具

    很久没有写技术方面的东西了,这半年主要是在学习别人的东西,对自己提高比较大,算是一次技术回笼吧,这次学习之旅目的是结束技术方面的专注,开始向应用方面找突破口,也就是完成技术积累或者为技术的积累做坚实的 ...

  2. 认识axure组件区域

    组件区域也叫做部件区域,英文为widgets,还有人称之为控件区域,组件是axure事先准备好的网站项目常用的零件,比如一些基本的页面元素 Axure默认存在2个组件库,分别为线框图和流程图.同时我们 ...

  3. cocos2d-x在win32和iOS、android下获取当前系统时间的方法

    最近在游戏里要显示当前系统时间的功能,网上一搜很多写着获取的方法,大都是如下 struct cc_timeval now; CCTime::gettimeofdayCocos2d(&now, ...

  4. JavaScript编程:java事件模型

    3.java事件模型: 传统事件处理程序指派方法:          1.var odiv=document.getElementById("div1");             ...

  5. Cocos2dx引擎10-事件派发

    本文介绍Cocos2dx事件(以下简称Event)处理机制中的事件分发模块,在Event发生后,进过一系列处理,最后将会分发Event: 1.dispatchEvent& dispatchTo ...

  6. 利用Android属性动画实现Banner的原理与实践

    事实上在Android刚推出属性动画的时候.就想利用它来设计一个Banner控件,一直没什么时间尝试. 在当时看我们应用中的Banner,使用计时器来控制自己主动播放,设置一个非常大的数,利用余数原理 ...

  7. 使用BigDecimal来进行精确计算

    在一些以金融等行业中的计算是需要十分精确的,即使我们使用像double这样的类型,由于浮点数的原因,会使得数据计算变得不精确,例如下面的例子: double a = 0.1; double b = 0 ...

  8. Html.Partial("")与Html.RenderPartial("")区别

    文章有点长,但大多是代码,看看很快的,不要压力太大.网上有很多关于这两个方法的区别,都说出了它本质的区别(不看代码,只看这个结论,就已经足够了,如果觉得有必要从代码中得出这个结论,那就继续往下看),这 ...

  9. delphi中一切皆指针

    unit Unit1; interface uses  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Form ...

  10. Windows server 2008 R2实现多用户远程连接

    原文 Windows server 2008 R2实现多用户远程连接 经常使用远程桌面的朋友可能会注意到,Windows server 2008 R2中,远程桌面最多只允许两个人远程连接,第三个人就无 ...