(Effective C++学习笔记)条款 1:尽量用 const 和 inline 而不用#define

条款 1:尽量用 const 和 inline 而不用#define

  • 此条款最好称为:“尽量用编译器而不用预处理
    • 因为#define 经常被认为好像不是语言本身的一部分。这是问题之一。再看下面的语句:
#define ASPECT_RATIO 1.653
  • 编译器会永远也看不到 ASPECT_RATIO 这个符号名
    • 在源码进入编译器之前,它会被预处理程序去掉,于是 ASPECT_RATIO 不会加入到符号列表中
    • 如果涉及到这个常量的代码在编译时报错,就会很令人费解,因为报错信息指的是 1.653,而不是 ASPECT_RATIO。
    • 如果 ASPECT_RATIO 不是在你自己写的头文件中定义的,就会奇怪 1.653 是从哪里来的,甚至会花时间跟踪下去。这个问题也会出现在符号调试器中,因为同样地,你所写的符号名不会出现在符号列表中。
  • 解决这个问题的方案很简单:不用预处理宏,定义一个常量
const double ASPECT_RATIO = 1.653;
  • 有两个特殊情况要注意
    • 定义指针常量时会有点不同
    • 因为常量定义一般是放在头文件中(许多源文件会包含它),除了指针所指的类型要定义成 const 外,重要的是指针也经常要定义成 const
      • 例如,要在头文件中定义一个基于 char*的字符串常量,你要写两次 const
const char * const authorName = "Scott Meyers";
  • 定义某个类(class)的常量
    • 要把常量限制在类中,首先要使它成为类的成员;为了保证常量最多只有一份拷贝还要把它定义为静态成员
class GamePlayer {
     private:
     static const int NUM_TURNS = 5; // constant declaration
     int scores[NUM_TURNS]; // use of constant
     ...
};
  • 上面的语句是 NUM_TURNS 的声明,而不是定义,所以还必须在类的实现代码文件中定义类的静态成员
const int GamePlayer::NUM_TURNS; // mandatory definition;
 // goes in class impl. file
  • 如果忘了定义,链接器会提醒。
  • 旧一点的编译器会不接受这种语法,因为它认为类的静态成员在声明时定义初始值是非法的;而且,类内只允许初始化整数类(如:int, bool, char )还只能是常量
  • 在上面的语法不能使用的情况下,可以在定义时赋初值:
class EngineeringConstants { // this goes in the class
     private: // header file
     static const double FUDGE_FACTOR;
     ...
 };
// this goes in the class implementation file
 const double EngineeringConstants::FUDGE_FACTOR = 1.35;
  • 特殊场景:类在编译时需要用到这个类的常量的情况
    • 例如上面 GamePlayer::scores 数组的声明(编译过程中编译器一定要知道数组的大小)
    • 为了弥补那些(不正确地)禁止类内进行整型类常量初始化的编译器的不足,可以采用称之为“借用 enum的方法来解决。
      • :除非你正在用老的编译器(即写于 1995 年之前),你不必借用 enum
      • 这种技术很好地利用了当需要 int 类型时可以使用枚举类型的原则,所以GamePlayer 也可以象这样来定义:
class GamePlayer {
 private:
     enum { NUM_TURNS = 5 }; // "the enum hack" — makes
                             // NUM_TURNS a symbolic name
                             // for 5
     int scores[NUM_TURNS]; // fine
     ...
 };
  • #define 指令用它来实现那些看起来象函数而又不会导致函数调用的宏
    • 典型的例子是计算两个对象的最大值:
#define max(a,b) ((a) > (b) ? (a) : (b))
  • 必须记住在写宏体时对每个参数都要加上括号;否则,别人调用你的宏时如果用了表达式就会造成很大的麻烦。即使这样做了,还会有象下面这样奇怪的事发生:
int a = 5, b = 0;
max(++a, b); // a 的值增加了 2 次
max(++a, b+10); // a 的值只增加了 1 次
  • 用普通函数实现宏的效率,再加上可预计的行为和类型安全,这就是内联函数(见条款 33):
inline int max(int a, int b) { return a > b ? a : b; }
  • 不过这和上面的宏不大一样,因为这个版本的 max 只能处理 int 类型。但模板可以很轻巧地解决这个问题:
template<class T>
inline const T& max(const T& a, const T& b)
{ return a > b ? a : b; }
  • 这个模板产生了一整套函数,每个函数拿两个可以转换成同种类型的对象进行比较然后返回较大的(常量)对象的引用。
    • 因为不知道 T 的类型,返回时传递引用可以提高效率(见条款 22)。
  • 有了 const inline,对预处理的需要减少了,但也不能完全没有它。如,#ifdef/#ifndef 在控制编译的过程中还扮演重要角色
  • 【注】参考《Effective C++》
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值