宏在 C 语言中非常重要,但在 C++ 中却无甚大用,普遍的共识:尽量避免使用宏

C++ 之父 Bjarne 在《C++ Programming Language》中写到

  • Avoid macros

《Effective C++》 条款 2

  • Prefer const, enum, and inline to #define

谷歌 C++ 编码规范,关于宏的描述

  • Avoid defining macros, especially in headers
  • Do not use macros to define pieces of a C++ API

1  禁用宏

谷歌 C++ 规范中,禁用宏的情况有三种:头文件、API 接口、程序文本

头文件中禁用宏,规范里写的很明确:

  • Don't define macros in a .h file.

对于 C++ API 接口,则是:

  • Do not use macros to define pieces of a C++ API

因此,如下形式的宏,是禁止的

class PANDA_TYPE(Foo) {
// ...
public:
EXPAND_PUBLIC_PANDA_API(Foo) EXPAND_PANDA_COMPARISONS(Foo, ==, <)
};

程序文本中禁用宏,尤其是用 ## 来替换变量名

  • Don't use macros for program text manipulation
  • Prefer not using ## to generate function/class/variable names. 

例如,下面代码是要避免的

#define CAT(a, b) a ## b
#define STRINGIFY(a) #a void f(int x, int y)
{
string CAT(x, y) = "asdf"; // BAD: hard for tools to handle (and ugly)
string sx2 = STRINGIFY(x);
// ...
}  

2  替代宏

《Effective C++》 条款 2:用 const, enum 或 inline 来替代宏

用宏来表示常量和函数,是不推荐的

#define PI 3.14

#define SQUARE(a, b) (a * b)

可用 constexpr 和 模板函数来替代,这样的好处:constexpr 定义的常量 kPI 会进入符号表,能被编译器识别到,编译报错时会提示 kPI 错误

而定义在 .h 中的宏,如果编译出错,只会提示 3.14 这个数值的错误,对于不是自己写的头文件,且常数含义未知时,很难查到错误来源

constexpr double kPI = 3.14;

template<typename T> 
T square(T a, T b)
{
return a * b;
}  

同样,如下代码也是需要避免的

// webcolors.h (third party header)
#define RED 0xFF0000
#define BLUE 0x0000FF // productinfo.h, the following define product subtypes based on color
#define RED 1
#define BLUE 2 int web = BLUE; // web == 2; probably not what was desired

  可用 enum class 来代替,在 C++11 之 enum class 中也有提及

enum class Web_color { red = 0xFF0000, green = 0x00FF00, blue = 0x0000FF };
enum class Product_info { red = 0, purple = 1, blue = 2 }; int webby = blue; // error: be specific
Web_color web = Web_color::blue;  

3  使用宏

虽然宏在 C++ 中如此被嫌弃,但为了兼容 C 语言,也不能直接将其删掉,这也是阻碍 C++ 发展的历史包袱

在某些方面,宏还是有点价值的,比如:头文件的保护宏

#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_ ... #endif // FOO_BAR_BAZ_H_

还有一些预定义好的宏

__cpluplus
__DATE__
__FILE__
__LINE__

在代码可读性上,宏往往会有意想不到的效果,如《The Art of Readable Code》中的例子

void AddStats(const Stats& add_from, Stats* add_to)
{
add_to->set_total_memory(add_from.total_memory() + add_to->total_memory());
add_to->set_free_memory(add_from.free_memory() + add_to->free_memory());
add_to->set_swap_memory(add_from.swap_memory() + add_to->swap_memory());
add_to->set_status_string(add_from.status_string() + add_to->status_string());
add_to->set_num_processes(add_from.num_processes() + add_to->num_processes());
...
}

为了增强可读性,使用宏定义,可改为如下形式

void AddStats(const Stats& add_from, Stats* add_to)
{
#define ADD_FIELD(field) add_to->set_##field(add_from.field() + add_to->field())
ADD_FIELD(total_memory);
ADD_FIELD(free_memory);
ADD_FIELD(swap_memory);
ADD_FIELD(status_string);
ADD_FIELD(num_processes);
...
#undef ADD_FIELD
}

 当必须使用宏时,注意如下几点:

  • If you must use macros, use names with capital letters
  • Name macros with a project-specific prefix
  • #define macros right before you use them, and #undef them right after.

参考资料

    C++ Core GuideLines

谷歌 C++ 编码规范 - Preprocessor Macros

《The Art of Readable Code》 chapter 8

后记

    写完博文,当我还沉浸在搞清一个 C++ 知识点的兴奋中时,突然想到鲁迅笔下的《孔乙己》,这篇博文,不就是教茴字四种写法的现代版么?

孔乙己的悲剧,更多是因时代巨变所致,是旧社会一代读书人的命运缩影,如果时代没有变,兴许茴字的写法,也是科举考试中的一个知识点。

然而,孔乙己还是有一技之长的,"幸而写得一笔好字,便替人家抄抄书,换一碗饭吃"。在如今经济停滞甚至衰退的浪潮下,我又有什么一技之长 "换一碗饭吃" 呢?

写到此,我也没有答案,孔乙己 = 恐怕以为是自己,只能以《孔乙己》的结尾警示自己:我到现在终于没有见——大约孔乙己的确失业了...

C++ 之 宏定义的更多相关文章

  1. c++宏定义命令

    在程序开始以#开头的命令,他们是预编译命令.有三类预编译命令:宏定义命令.文件包含命令.条件编译命令:今天聊聊宏定义: 宏定义命令将一个标识符定义为一个字符串,源程序中的该标识符均以指定的字符串来代替 ...

  2. dll导入导出宏定义,出现“不允许 dllimport 函数 的定义”的问题分析

    建立dll项目后,在头文件中,定义API宏 #ifndef API_S_H #define API_S_H ...... #ifndef DLL_S_20160424 #define API _dec ...

  3. iOS之常用宏定义

    下面我为大家提供一些常用的宏定义! 将这些宏定义 加入到.pch使用 再也不用 用一次写一次这么长的程序了 //-------------------获取设备大小------------------- ...

  4. linux中offsetof与container_of宏定义

    linux内核中offsetof与container_of的宏定义 #define offsetof(TYPE, MEMBER)    ((size_t) &((TYPE *)0)->M ...

  5. Linux Kernel代码艺术——系统调用宏定义

    我们习惯在SI(Source Insight)中阅读Linux内核,SI会建立符号表数据库,能非常方便地跳转到变量.宏.函数等的定义处.但在处理系统调用的函数时,却会遇到一些麻烦:我们知道系统调用函数 ...

  6. 面试问题5:const 与 define 宏定义之间的区别

    问题描述:const 与 define 宏定义之间的区别 (1) 编译器处理方式不同     define宏是在预处理阶段展开:     const常量是编译运行阶段使用: (2) 类型和安全检查不同 ...

  7. 关于Xcode8.1 / iOS10+ 真机测试系统打印或者宏定义打印不显示问题

    前言: 最近做项目时遇到了很多莫名其妙的问题,其中就有这个打印(NSLog).也不多废话了,我们先来回顾一下Xcode8发布以来,我们遇到的一些关于打印的问题,当然也有解决方法: 1.Xcode8打印 ...

  8. JDStatusBarNotification和一些宏定义

    // //  AddTopicViewController.m //  vMeet2 // //  Created by 张源海 on 16/6/30. //  Copyright © 2016年 h ...

  9. #define宏定义形式的"函数"导致的bug

    定义了一个宏定义形式的"函数": #define  SUM8(YY)\ {\ int Y = YY>>2;\ ...\ } 然后使用的时候,传入了一个同名的变量Y: i ...

  10. 黑马程序员——C语言基础 枚举 宏定义 自定义 static exterm

    Java培训.Android培训.iOS培训..Net培训.期待与您交流! (以下内容是对黑马苹果入学视频的个人知识点总结) (一)枚举 1)枚举类型的定义 枚举是C语言中的一种基本数据类型,并不是构 ...

随机推荐

  1. liunx标准输入与输出

    一.Linux提供了三种输入/输出通道给程序在linux中,每个进程都会有三个文件,并且这三个文件会进行重定向处理:1. 标准输入(STDIN) - 缺省为键盘2. 标准输出(STDOUT) - 默认 ...

  2. Python数据科学手册-Pandas:累计与分组

    简单累计功能 Series sum() 返回一个 统计值 DataFrame sum.默认对每列进行统计 设置axis参数,对每一行 进行统计 describe()可以计算每一列的若干常用统计值. 获 ...

  3. CVE-2022-39197(CobaltStrike XSS <=4.7)漏洞复现

    最新文章更新见个人博客 漏洞说明 根据9.20日CobaltStrike官方发布的最新4.7.1版本的更新日志中介绍,<=4.7的teamserver版本存在XSS漏洞,从而可以造成RCE远程代 ...

  4. 【微服务】- 配置中心 - Nacos

    微服务 - 配置中心 - Nacos 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 今天的学习任务就是学习使用Nacos作为配置中心. 努力克制自己,拒绝摆烂! 什么是配 ...

  5. 使用调度器apscheduler实现py文件不停连接MySQL数据库

    背景说明: 使用内网负载均衡添加后端主机,该主机安装有nginx,其配置文件代理内网的MySQL数据库地址 (数据库是主备形式的,重启的话会都重启) 需要测试两个目标: (采用不停往MySQL里写数据 ...

  6. sql语句的组成

    数据定义语言(DDL,Data Definition Language):包括CREATE(创建)ALTER(修改)DROP(删除)等. 数据操纵语言(DML,Data Manipulation La ...

  7. P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并 (树上差分+线段树合并)

    显然的树上差分问题,最后要我们求每个点数量最多的物品,考虑对每个点建议线段树,查询子树时将线段树合并可以得到答案. 用动态开点的方式建立线段树,注意离散化. 1 #include<bits/st ...

  8. P1084 [NOIP2012 提高组] 疫情控制 (二分答案、贪心)

    因为若一个时间限制满足题意,则所有比它大的时间限制一定都满足题意,因此本题答案具有单调性,可以想到二分答案求解. 本题思路不是很难,但细节和代码实现比较复杂. 见牛人博客:https://www.lu ...

  9. 会话跟踪技术 - Cookie 和 Session 快速上手 + 登陆注册案例

    目录 1. 会话跟踪技术概述 2. Cookie 2.1 Cookie的概念和工作流程 2.2 Cookie的基本使用 2.3 Cookie的原理分析 2.4 Cookie的使用细节 2.4.1 Co ...

  10. 我的Vue之旅 07 Axios + Golang + Sqlite3 实现简单评论机制

    第三期 · 使用 Vue 3.1 + TailWind.CSS + Axios + Golang + Sqlite3 实现简单评论机制 效果图 CommentArea.vue 我们需要借助js的Dat ...