Effective C++ 条款2:尽量以const,enum,inline替换#define

#define用来定义常量。

首先我们需要知道,#define会在预编译的时候,以字符串替换的形式被替换掉。假设我们#define AspectRatio= 1.635。如果AspectRatio在使用的过程中报错,编译器会直接提示1.635。如果这个宏是其他文件定义的,那么对于使用者而言,他甚至都不知道1.635是个什么东西。

下面还有两种特殊情况

定义常量指针

常量指针通常需要用两个const。即既限定指针指向对象无法改变,也限定无法通过指针改变对象值。因为如果用#define定义指针的话,通常定义的是一个常量地址。这个地址代表的一定是一个固定的东西,因此必须是指针常量。并且,我们当然也不希望通过地址值直接修改任何东西。所以也必须是常量指针。

下面是个例子。

const char* const author_name = "Scott Meyers";

这里面定义的一个指针,就是指针常量。但通常我们用string而非char*

const std::string author_name("Scott Meyers");

class专属常量

类中的常量,我们希望他只有一版,因此需要定义为静态的。

class GamePlayer{
private:
    static const int number_turns = 5;
    int scores[number_turns];
}

但由于上面的这个常量是写在类中的,因此只是一个声明,而非定义。在取地址的时候,如果这个常量是内置的整数型,int,char,bool,就没什么问题。但是如果是别的类,或者有的其他编译器不允许这样,那就要在外面定义一下。

const int GamePlayer::number_turns = 5;

因为C++要求任何东西都要被定义才能使用。

enum hack

上述问题还有一个解决方法,就是enum hack。枚举型的变量可以当成一个int使用。而且枚举型可能比const更像#define。因为const变量可以取地址,但是enum和#define都无法取地址。

class GamePlayer {
private:
    enum {num_turns = 5};
    int scores[num_turn];
}

滥用宏

这一块我们讨论一个小问题,就是把宏定义成非常复杂的函数。这样可能导致一些意外的结果。下面是个例子

#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))
int a = 5, b = 0;
CALL_WITH_MAX(++a, b)

由于宏会直接替换,所以最后就成了

f((++a) > (b) ? (++a) : (b))

很显然,我们只希望a自加一次,但是这里却加了两次。

解决这个问题的方法是使用模板内联函数

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

#define 设计的通常都是些简单的函数,因此完全可以将他们设计成内联函数。最大的好处是,模板内联函数是个函数,他可以放在class当中。这样就可以为它设计访问性,比如public和private。但是宏定义是没有类中权限的。

结论

  • 对于单纯常量,用const替换
  • 对于形似函数的宏,最好用inline替换
上一篇:枚举、结构体、共用体【Java】


下一篇:Java 枚举类型