Item 02:尽量为const,enum,inline 替换 #define

天门

Item 02: Prefer const,enums,and inlines to #define


准章或改也“宁愿选择编译器而无是先行处理器”。

宏与常量

当你写有如此的代码的时光:

#define ASPECT_RATIO 1.653

也许会见带动有烦劳。
解决之道是以一个常量替换上述的宏(#define):

const double AspectRatio = 1.653;

用作一个言语常量,AspectRatio肯定会吃编译器看到,当然就会进入记号表内。此外针对浮点常量而言,使用常量可能较下#define导致比较小量的码,因为预处理器“盲目地用宏名称ASPECT_RATIO替换为1.653”可能造成目标码出现多瓜分1.653,若改用常量AspectRatio绝不见面并发相同的场面。

当我们盖常量替换#define,有点儿种植特有情况值得说一下。

常量指针

由于常量定义式通常为放在头文件内(以便让不同的源码含入),因此发生必不可少将指针(包括指针所据的物)声明也const。例如若一旦以峰文件内定义一个常量的(不变换的)char*-based字符串,你得写const两糟:

const char* const authorName = “Hello”;

此间string对象通常比较其前辈char*-based副,所以上述的authorName定义成这样又好把:

const std::string authorName("Hello");

class专属常量

以用常量的意域限制于class内,你必须叫它们化class的一个分子;而也确保这个常量至多只有来同份实体,逆序让她变成一个static成员:

class GamePlayer{
private:
    static const int NumTurns = 5;    //常量声明式
    int scores[NumTurns];            //使用该常量
}

可你所盼的凡NumTurns的声明式而非定义式。通常C++要求你针对而所利用的其余事物提供一个定义式,但只要它们是单class专属常量又是static且也整数类型(例如ints,chars,bools),则要特别处理。只要不取它们的地点,你可以声明并采用它而毫无提供定义式。但若是您拿走有class专属常量的地点,或者即使你无沾该地址而若的编译器却坚持要见到一个定义式,你不怕必另外提供定义式如下:

const int GamePlayer::NumTurns;        //NumTurns的定义

央把这个姿势放上一个兑现公文要非头文件。由于class常量已当宣称时获得初值,因此定义式不得以再使初值。

来拘禁一下我们的测试例子:

#include <iostream>
using namespace std;

class Shape {
public:
    static const int NumTurns = 5;    //常量声明式
    int scores[NumTurns];            //使用该常量
};

//const int Shape::NumTurns;

int main()
{
    cout << Shape::NumTurns << endl;    //输出为5,无编译警告错误
    return 0;
}

但我们反吗

int main()
{
    cout << &Shape::NumTurns << endl;    //error: undefined reference to `Shape::NumTurns'
    return 0;
}

下一场,我们再次转移:

const int Shape::NumTurns;

int main()
{
    cout << &Shape::NumTurns << endl;    //输出0x404064,无编译警告错误
}

然后,我们重新变更:

const int Shape::NumTurns = 10;

int main()
{
    cout << Shape::NumTurns << endl;    //error: duplicate initialization of 'Shape::NumTurns'
}

俺们无能为力使#define创建一个class专属常量,因为#define并无推崇作用域。一旦宏被定义,它就以气候的编译过程被有效(除非在某处被#undef)。这就算表示#defines不仅不能够用来定义class专属常量,也不可知提供任何封装性,也就是说没有所谓private#define这样的东西。

旧式编译器也许不支持上述语法,它们不允static成员以那个声明式上获初值。此外所谓的“in-class初值设定”也不过同意对整数常量进行。所以我们绝好之做法是管初值放在定义式:

class CostEstimate{
private:
    static const double FudgeFactor;    //static class常量声明
    ...                                    //位于头文件内
};
const double CostEstimate::FudgeFactor = 1.35; //static class常量定义位于实现文件内

enum hack

上述做法看起都十分全面了。但唯一不同是当你以class编译期间需要一个class常量值,例如当上述的GamePlayer::scores的数组声明式中(编译器坚持要于编译期间掌握数组的轻重缓急)。这时候万如出一辙您的编译器不允许“static整数型class常量”完成“in
class初值设定”,可改用所谓的“the enum
hack”补偿做法。其辩解功底是:“一个属于枚举类型的数值可权充ints被利用”,于是GamePlayer可定义如下:

class CostEstimate{
private:
    enum { NumTurns = 5};    //"the enum hack"————令NumTurns成为5的一个记号名称
    int scores[NumTurns];
    ...
};

enum
hack的一言一行某方面来说比像#define而非像const。例如取一个const的地址是官的,但取一个enum的地方便不合法,而取一个#define的地方便为未合法。如果您免思吃别人沾一个pointer或reference指向您的之一正数常量,enum可以助你实现这约束。Enums和#defines一样绝不会促成不必不可少的内存分配。“enum
hack”是模板元编程的根底技术。

所谓的“宏函数”

其它一个大规模的#define误用状态是坐她实现所谓的“宏函数”,宏看起来如函数,但不会见导致函数调用带来额外的支付。

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

这种宏有太多缺点,无论何时当你写来这种庞大,你要记住也宏中的兼具实参加上小括声泪俱下,否则某些人当表达式中调用这个宏时可能会见遭到麻烦。但不怕经常你吗你有着实参加上小括哀号,看看下面不可思议的事情:

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

当此间,调用f之前,a的递增次数甚至在“它于将来跟哪位比”。
生什么好的更迭方法呢?

模板内联函数

透过沙盘内联函数而得落宏带来的频率及一般函数的兼具预料行为以及花色安全性。

template <typename T>    //由于我们不知道T是什么,所以采用pass by reference-to const
inline void callWithMax(const T& a, const T& b)
{
    f(a > b ? a : b);
}

斯模板产出一整群函数,每个函数都接受两单同型对象,并盖中间于生啊调用f。这里不欲在函数本体中也每个参数加上括号,也不需要担心参数为求值多次……。此外,由于callWithMax是只实在的函数,它遵守作用域和做客规则。例如你绝对好形容来一个“class内的private
inline函数”,一般而言宏无法成功此事。

有了consts、enums和inlines,我们针对先行处理器的求回落了,但绝不全盘铲除。#include仍然是必需品,而#ifdef/#ifndef也连续去主宰编译的重点角色。

NOTE:

  • 于只有常量,最好以const对象要enums替换#defines。
  • 对于“宏函数”,最好改用inline函数替换#defines.

Effective C++