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

宁可以编译器替换预处理器。因为或许#define不被视为语言的一部分,当你:

#define ASPECT_RATIO 1.653

名字ASPECT_RATIO从未被编译器看到,可能在编译器处理源码前它就被预处理器移走了,于是ASPECT_RATIO可能没进入记号表(symbol table)内,当运用此常量获得一个编译错误时,会带来困惑,因为错误信息可能会提到1.653而不是ASPECT_RATIO。当这个名字被定义在并非你写的头文件内时,会因为追踪它而浪费很多时间。

解决方法:

const double AspectRatio = 1.653;    // 使用常量替换宏,大写名常用于宏,因此改变写法

此时名称会进入记号表。此外,对于浮点常量,常量可能比宏更省空间,因为预处理器盲目地将宏名称替换为值,可能导致目标码出现多份值,而常量只会出现一份值。

常量替换宏时,如果常量定义的是一个指针,应把指针定义为const的,以便使该指针永远指向某对象。

书中写道:

class GamePlayer {  
private:  
    static const int NUM_TURNS = 5;    // 常量声明式,而非定义
    int scores[NUM_TURNS];    // use of constant  
}; 

const int GamePlayer::NUM_TURNS;    // 定义式,放在实现文件中,由于类内已赋初值,此处不用赋初值
                                    // 书上说此处定义作用为,要取地址值时,必须有定义式

我测试时,就算没有类外定义static常量,也能取到该常量的地址。可能是编译器优化或新版本C++特性,最好加上。

我们无法使用#define创建一个class专属常量,#define不重视作用域,只要被定义,就在编译过程中有效(除非#undef),它不能提供封装性,即没有private #define。

比较旧的编译器可能不支持static成员在类内获得初值。in-class初值设定也只能对整型常量进行。此时我们可以在类外定义static值。

但当class编译器需要一个类内常量,如创建一个数组时,可用enum,enum类型的值可当做整型使用:

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

enum中的枚举项不能取地址,#define的宏也不能取地址。但enum类型的值可以取地址:

    enum test { a = 5 };
    test *p = &a;    // 错误
    
    test t = a;
    test* pEnum = &t;
    cout << *pEnum << endl;    // 输出5

如果你不想让别人获得一个pointer或reference指向你的某个整数常量,enum可以实现这个约束。

#define可以实现宏函数,但它不会招致函数调用带来的开销,如:

#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))    // f是函数,选较大的一个数传入函数

要为宏中每个实参加上小括号,否则由于运算符的优先级,会导致意想不到的结果。我们直接将以上宏函数改写为inline模板函数,就不会出现由于运算符优先级产生的未知结果了。但仍然会出现其他未知结果,如:

CALL_WITH_MAX(a++, b);    // 当a>b时,a自增两次,否则自增一次

因此以上宏函数最好使用templete inline函数代替。

预处理器除#define外还有更多作用,如#ifdef可以控制编译、#include仍是必需品,但应该尽量少用它预处理器。

posted @   epiphanyy  阅读(9)  评论(0编辑  收藏  举报  
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示