欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

《Effective C++》Item2:尽量以const、enum、inline代替#define

程序员文章站 2024-03-23 14:28:46
...

宏的问题在于:处理它的是预处理器,而不是编译器。考虑如下的定义:

#define PI 3.1415926

在预处理器工作时,会将实际代码中的所有PI都直接替换为3.1415926,所以编译器在工作的时候根本看不到PI这个记号。由此产生的问题是:PI没有进入编译器的符号表。于是当你使用PI但是不幸收到一个编译错误时,其错误信息可能包含3.1415926,而不是PI。如果这个宏不是由你定义的,那么这个错误很可能给你造成很大的困扰。

解决这个问题的一个方法是使用常量替换掉这个宏:

const double PI = 3.1415926;

这种做法有两种情况值得探讨:

  • 第一是定义常量指针。由于常量定义式通常被定义在头文件中,因此有必要给指针添加顶层const:
const char *AuthorName = "zhangkeyang";
  • 第二是定义类中的常量。为了将常量的作用于限制在某个类中,必须将这个常量定义为类的静态成员:
class Article
{
public:
    static const std::string AuthorName;
    //...
};

然而要注意,这里第四行的写法是声明而非定义(也就是说编译器实际上并没有为这个字符串常量分配内存空间)。我们必须在某个源文件中手动做这件事:

const std::string Article::AuthorName("zhangkeyang");

我们无法使用#define来定义类专属常量,因为宏是不区分作用域的。

以上讨论了const代替宏的有效做法。但是这种做法存在一种问题,就是对于在类内定义数组时,需要指定固定的长度;然而有些编译器不允许在声明类中的整型常量的同时初始化。此时,可以采取enum来代替宏:

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

枚举的实现方法很像宏:它们都没有内存分配的动作,并且都不可被取地址,但是枚举却确确实实进入了编译器的符号表。因此,对于整数型的常量,可以考虑使用枚举来代替宏。

在传统的C语言程序中,宏同时被用来定义方便而简短的“函数”——看上去非常像函数,但是却没有函数调用的运行时开销:

#define min(x, y)  ((x) > (y) ? (y) : (x))

这般长相的宏有着太多的缺点。首先,你不得不为所有的参数都添加括号,这很影响阅读;即便如此,这种宏仍然会产生意料不到的错误。考虑如下写法:

int a = 5, b = 0;
min(a++, b);

我们的初衷是在调用结束后a只递增一次,但实际情况是它被递增了两次。

解决以上问题的良药是内联。使用inline关键字,C++代码可以实现得更为优雅和高效:

template<typename T>
const T &min(const T &x, const T &y) 
{
    return x > y ? y : x;
}

这个函数模板解决了上述所有的问题。

目前位置,我们已经介绍了足够多的替换原始宏的做法。但是,宏在有些方面仍然是不可替代的,比如使用#ifndef/#define控制编译等等。总而言之,如果非必要,应该尽可能地避免使用宏。

**【注意】**对于常量,应该使用constenum代替宏;同样,可以使用模板和内联函数来代替宏函数。

相关标签: 读书笔记 c++