尽量以const、inline替换 #define

以const代替#define定义常量

Effective C++中给出的遵循这个条款大概理由如下:

#define的宏在预处理阶段就已经被替换掉,所以编译器可能看不到宏的存在,只看到被替换的内容,从而导致因这个宏的错误使用而导致的编译错误没有被正确得提示。

#define ASPECT_RATIO 1.653

例如和上面这段宏有关的编译错误,可能会提及1.653而不是ASPECT_RATIO

但是随着编译器的发展,一个健壮的编译器已经能发现并定位到#define导致的错误。

例如这段代码:

#define ASPECT_RATIO 1.653

int main() {
    ASPECT_RATIO = 1;
}

这段代码存在试图给宏ASPECT_RATIO赋值,因为ASPECT_RATIO是一个左值自然会出现编译错误。

replace_define.cpp: In function ‘int main():
replace_define.cpp:4:20: error: lvalue required as left operand of assignment
    4 |     ASPECT_RATIO = 1;
      | 

这段error的含义是:赋值操作的左侧需要一个lvalue,可以看到编译器正确检查出错误类型并提示发生错误的位置和具体内容。

虽然编译器能正确检查出错误并正确提示,但是还是建议使用const提到#define。#define给我们很高的自由度并且它没有类型检查,这会导致我们在定义了错误类型的宏但不能及时发现,const提供类型检查,可以配合LSP在编写代码阶段就发现问题。

例如我们想定义一个字符串,但是手误写成了字符:

#define SOME_STRING '1'

这并不会出现错误提示,因为LSP也不知道你想定义的是什么类型,但是使用const就会存在提示:

const std::string someString = '1';
no suitable constructor exists to convert from "char" to "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>"C/C++(415)

使用inline代替#define定义类似函数的宏

有时候我们会定义一个类似函数的宏,这种宏的替代可以减少函数调用的开销,但是这种宏在使用的过程中,也是误用的重灾区。

考虑如下情况:

定义一个计算平方的宏:

#define SQUARE(x) x * x

使用该宏:

int y = SQUARE(3);

你得到了你期望的值9,你又测试了很多组数据,从负数到零再到更多正数,都没有问题,于是你认为这段代码没有任何问题,殊不知你已经埋下了一颗随时会爆炸的地雷

因为只有当你以下面这种方式使用时,它才会显出原形:

int y = SQUARE(1 + 2)

输出:

5

之所以会出现这么诡异的情况,是因为#define只是简单的文本替换,所以int y = SQUARE(1 + 2)实际上被替换成了int y = 1 + 2 * 2 + 1y自然就得到了5

要解决这个问题,需要给x(变量)裹上()

#define SQUARE(x) ((x) * (x))

SQUARE加上()确实是解决的,但是()却没能解决所有问题。

#include <iostream>
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int main() {
    int a = 5, b = 0; // a = 5, b = 0
    MAX(++a, ++b); // a = 7, b = 1, ++a执行了两次,++b执行一次
    MAX(++a, ++b + 10); // a = 8, b = 3, ++a执行一次,++b执行两次
    // ...
}

这里的ab分别被错误得进行两次++操作,真得让人头大。

与其花心思琢磨它会有什么样的魔术效果,不如考虑使用更稳定的inlineinline会将函数调用替换为函数本身,从而减少函数调用的开销。最重要的是它可以享受函数的可预见性和类型安全,不会像宏一样产生魔术效果。

考虑将上面那段代码替换为:

#include <iostream>
template <typename T>
inline T MAX(const T& a, const T& b) {
    return a > b ? a : b;
}

int main () {
    int a = 5, b = 0; // a = 5, b = 0
    MAX(++a, ++b); // a = 6, b = 1, 都正确地只执行1次++。
    MAX(++a, ++b + 10); // a = 7, b = 2, 都正确地只执行1次++。
    // ...
}

这段代码就不会出现错误得进行两次++操作的行为。

总而言之,我们应该减少对#define的依赖,使用const替代#define定义常量,使用inline替代#define定义类似函数的宏。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值