C++ 的书上常说,尽量不要用 #define 来定义常量。这究竟是为什么呢?

其实 C++ 并不仅仅不提倡用宏来定义常量,而且还不提倡用宏来定义“函数”。事实上 C++ 并不是很喜欢预处理宏,在很多很多方面,如果不是必需,尽量不要使用预处理宏。

为什么 C++ 不喜欢预处理宏?

首先,预处理宏是“全局”的。所以,在 C++ 这样如此强调命名空间、类这样的东西的语言中,全局的东西真是越少越好。但是其实预处理宏的全局并不是语义上的全局,之所以叫预处理宏,是因为预处理宏会在编译器编译代码之前被简单地替换成代码。

然后,正因为预处理宏会被简单替换,所以替换的结果是不可预料的。这些说起来还是比较模糊,所以下面将举一些实际的例子。

#define 会把其后的注释也简单地替换

#define PI 3.14159 // This is a constant./* Some harmonious code */double radius = 2.0;
double area = PI * radius * radius
;cout << area << endl;

某些编译器的结果会输出 3.14159 . 但是使用了 g++ 的测试结果却是正确的。在输出错误结果的编译器下,是因为上面使用了 PI 的那行被替换成了:

double area = 3.14159 // This is a constant. * radius * radius

其实这种情况可以用下面的方法解决:

#define PI 3.14159 /* This is a constant */

尽管可以如此解决,我们还是不提倡使用 #define 定义常量。我们还是有一万个理由,尽量不用 #define .

宏并不能正确地指定类型

我们知道 #define 定义的常量并不需要指定类型,而用 const 修饰的常量是必须指定类型的。这个方面其实没有太多的例子可以举,而且我也没有想出一些明显的错误的例子,所以就提一下吧。

C 里必须用宏定义常数而 C++ 并不一定

考虑以下这段代码:

const int n = 256;
char a[n] = {0};

这段代码如果以 .c 作为后缀保存,会提示定义数组时需要一个常量作下标,因为在 C 语言中,const 只是“不可修改的变量”之意。所以在 C 里只能用 #define 定义常量。但是在 C++ 中却可以用 const.

这些代码在 VC6 和 gcc 中测试过,都是如上所述。

用 #define 定义字面常量可能会浪费很多空间

比如在代码中使用 #define 定义了一个比较长的常量字符串,如果这个宏被使用了很多次,那么这个字面常量将会遍地开花,如果编译器没有那么聪明的话,可能会耗费很多不需要耗费的空间。

用 #define 定义常量对象可能会执行多次构造函数而降低时间效率

参见如下代码:

#define WELCOME_MESSAGE string("Welcome!")

如果多次使用 WELCOME_MESSAGE 宏的话,将有可能每次遇到它们的时候都调用 class string 的 string(const char *) 构造函数。这样的话,不仅空间会被浪费,而且也会影响执行效率。

#define 定义的“函数”“参数”也只是简单替换

考虑如下代码:

#define max(a,b) (a > b ? a : b)/* Some harmonious code */int x = 5, y = 6;
int n = max(++x, ++y);

这样之后,n 不会是 7. 因为那个调用 max 宏的那行被替换成了:

int n = (++x > ++y ? ++x : ++y);

所以 n 的值会是 8. 其实这种小错误还算不错了,还有更错的。

#define 定义的函数不“认识” C++ 里的 template

当 #define 出生的时候,还没有 template, 似乎也没有 // 开头的注释。考虑如下代码:

#define max(a,b) (a > b ? a : b)template
class example {
/* ... */
public:
bool operator>(const example &foo) const {
/* ... */
}
};/* ... */template x, y;
/* ... */
template n = max(example(x), example(y));

这里的最后一行的 max 宏会把 example<float 视为 a, double>(x) 视为 b. 后面的东西就会报错了。在 C 语言里只考虑到了括号内的逗号,而没考虑大小于号里的括号。

但是为什么 C++ 又没有放弃支持 #define

C 语言里有一个 assert.h , 是必须要用宏来实现的。除此之外,C++ 似乎有一种在运行时获取“类”的名字的思路,就是使用宏。还有,宏可以避免头文件被多次包含。而且有的时候需要一些预定义的宏,来控制代码在不同的环境下的编译。

有的时候,对于一些要重复多次,并且比较长的代码,可以在局部启用宏。但是在用完的地方一定要使用 #undef 将其取消。但是大多时候,尽量不要用宏来定义常数和“函数”。

那在 C++ 中应该怎么办才能替代 #define 的一些功能

C++ 不但能替代一些 #define 没有完善的功能,而且能做得更好。对于常数定义,在 C++ 中使用 const 就可以了。对于一些简单的“函数”,可以使用 C++ 的 inline 修饰符,使用这个修饰符,效果上会和一般的函数一样,但是实际上编译器会把函数中的代码根据语义替换到调用函数的地方,所以运行效率不会受到太大影响。而且在使用 max(++i, ++j) 之类的函数的时候,不会是简单替换,所以 ++i 和 ++j 分别只被计算一次。

为什么 C++ 中提倡尽量避免使用宏 #define(转)相关推荐

  1. VC中的宏 (#define) 与预处理 (#if/#ifdef/#pragma) 的使用方法总结。

    C/C++ 预定义宏 例子:C/C++ 预定义宏的取值 C/C++ 预定义宏用途:诊断与调试输出 CRT 的诊断与调试输出:assert, _ASSERT/_ASSERTE, _RPTn/_RPTFn ...

  2. C语言中被常用到的宏

    不管用什么语言编写程序,都会用到一些灵活的.简约的风格来实现简单的应用.C语言(当然也包括其他语种),设计良好的宏定义,不但可以使代码易懂耐用,而且也能大幅度提升自己对语言的掌控和应用能力,使得&qu ...

  3. C标准中一些预定义的宏,如__FILE__,__func__等

    C标准中一些预定义的宏 C标准中指定了一些预定义的宏,对于编程经常会用到.下面这个表中就是一些常常用到的预定义宏. 宏 意义 __DATE__ 进行预处理的日期("Mmm dd yyyy&q ...

  4. ANSI C and Microsoft C++中常用的预定义宏以及 宏定义中 # 和 ## 的区别

    ANSI C and Microsoft C++中常用的预定义宏以及 宏定义中 # 和 ## 的区别 第一部分,常见的预定义宏 第二部分,# 和 ## 再宏定义中的使用说明 第三部分,类似 #prag ...

  5. 关于word2016中mathtype无法使用以及“由于宏安全设置,无法找到宏或宏已被禁用”的解决方案

    转载于:https://www.cnblogs.com/Blue-Keroro/p/8583262.html 关于word2016中mathtype无法使用以及"由于宏安全设置,无法找到宏或 ...

  6. Word处理控件Aspose.Words功能演示:使用 C# 在 Word 文档中创建和修改 VBA 宏

    Aspose.Words 是一种高级Word文档处理API,用于执行各种文档管理和操作任务.API支持生成,修改,转换,呈现和打印文档,而无需在跨平台应用程序中直接使用Microsoft Word.此 ...

  7. 在Python中运行Excel的VBA宏

    转载自https://www.cnblogs.com/russellluo/archive/2011/10/16/2214347.html 并加以细化 在EXCEL的VBA中提供了一个ExecuteE ...

  8. Linux内核中的READ_ONCE和WRITE_ONCE宏

    在Linux内核代码中,经常可以看到读取一个变量时,不是直接读取的,而是需要借助一个叫做READ_ONCE的宏:同样,在写入一个变量的时候,也不是直接赋值的,而是需要借助一个叫做WRITE_ONCE的 ...

  9. iOS 宏(define)与常量(const)的正确使用

    2019独角兽企业重金招聘Python工程师标准>>> 在iOS开发中,经常用到宏定义,或用const修饰一些数据类型,经常有开发者不知怎么正确使用,导致项目中乱用宏与const修饰 ...

最新文章

  1. phalcon系列(1) hello phalcon
  2. 封装案例-02-创建(qiang)类
  3. 响应式系统reactive system初探
  4. Effective Java之对于实例控制,枚举类型优于readResolve(七十七)
  5. 【手势交互】9. PS Move
  6. System V IPC之信号灯
  7. PHP JSON文件解析并获取key、value,判断key是否存在
  8. java.lang.IllegalStateException: No modifications are allowed to a locked ParameterMap问题的解决
  9. webpack5学习与实战-(九)-区分开发和生产环境的配置
  10. c# mysql varbinary_数据库中用varbinary存储二进制数据
  11. Serverless 实战:3 分钟实现文本敏感词过滤
  12. 面向创新设计的汽车外形曲面三维逆向建模
  13. xml建模包括以下_我们的服务|无人机倾斜摄影三维建模
  14. linux服务器硬盘检测,在Linux服务器中检查硬盘坏道的命令
  15. 服务器部署dble全流程
  16. 计算机开机会跳过自检嘛,开机怎么样跳过主板自检 电脑开机不自检
  17. 电商战决胜在物流 聚美优品破瓶颈发展
  18. Android自定义键盘
  19. Linux 下 Login 和 Logout 详解
  20. 哈工大计算机系统lab7——微壳

热门文章

  1. 10 迭代器与生成器
  2. apt的通讯信道是如何发现的?
  3. opencv3.10加入OPENCV_contrib模块
  4. 一步一步asp.net_三层构架的学习
  5. asp 与 database (3)
  6. python list转矩阵
  7. MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)的理解(即c++参数初始)
  8. C++中的private protected public区别
  9. copyof java_死磕 java集合之CopyOnWriteArrayList源码分析
  10. [云炬python3玩转机器学习笔记] 2-3监督学习、非监督学习、半监督学习和增强学习