C++ 之 宏定义
宏在 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
.hfile.
对于 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
#definemacros right before you use them, and#undefthem right after.
参考资料
谷歌 C++ 编码规范 - Preprocessor Macros
《The Art of Readable Code》 chapter 8
后记
写完博文,当我还沉浸在搞清一个 C++ 知识点的兴奋中时,突然想到鲁迅笔下的《孔乙己》,这篇博文,不就是教茴字四种写法的现代版么?
孔乙己的悲剧,更多是因时代巨变所致,是旧社会一代读书人的命运缩影,如果时代没有变,兴许茴字的写法,也是科举考试中的一个知识点。
然而,孔乙己还是有一技之长的,"幸而写得一笔好字,便替人家抄抄书,换一碗饭吃"。在如今经济停滞甚至衰退的浪潮下,我又有什么一技之长 "换一碗饭吃" 呢?
写到此,我也没有答案,孔乙己 = 恐怕以为是自己,只能以《孔乙己》的结尾警示自己:我到现在终于没有见——大约孔乙己的确失业了...
C++ 之 宏定义的更多相关文章
- c++宏定义命令
在程序开始以#开头的命令,他们是预编译命令.有三类预编译命令:宏定义命令.文件包含命令.条件编译命令:今天聊聊宏定义: 宏定义命令将一个标识符定义为一个字符串,源程序中的该标识符均以指定的字符串来代替 ...
- dll导入导出宏定义,出现“不允许 dllimport 函数 的定义”的问题分析
建立dll项目后,在头文件中,定义API宏 #ifndef API_S_H #define API_S_H ...... #ifndef DLL_S_20160424 #define API _dec ...
- iOS之常用宏定义
下面我为大家提供一些常用的宏定义! 将这些宏定义 加入到.pch使用 再也不用 用一次写一次这么长的程序了 //-------------------获取设备大小------------------- ...
- linux中offsetof与container_of宏定义
linux内核中offsetof与container_of的宏定义 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->M ...
- Linux Kernel代码艺术——系统调用宏定义
我们习惯在SI(Source Insight)中阅读Linux内核,SI会建立符号表数据库,能非常方便地跳转到变量.宏.函数等的定义处.但在处理系统调用的函数时,却会遇到一些麻烦:我们知道系统调用函数 ...
- 面试问题5:const 与 define 宏定义之间的区别
问题描述:const 与 define 宏定义之间的区别 (1) 编译器处理方式不同 define宏是在预处理阶段展开: const常量是编译运行阶段使用: (2) 类型和安全检查不同 ...
- 关于Xcode8.1 / iOS10+ 真机测试系统打印或者宏定义打印不显示问题
前言: 最近做项目时遇到了很多莫名其妙的问题,其中就有这个打印(NSLog).也不多废话了,我们先来回顾一下Xcode8发布以来,我们遇到的一些关于打印的问题,当然也有解决方法: 1.Xcode8打印 ...
- JDStatusBarNotification和一些宏定义
// // AddTopicViewController.m // vMeet2 // // Created by 张源海 on 16/6/30. // Copyright © 2016年 h ...
- #define宏定义形式的"函数"导致的bug
定义了一个宏定义形式的"函数": #define SUM8(YY)\ {\ int Y = YY>>2;\ ...\ } 然后使用的时候,传入了一个同名的变量Y: i ...
- 黑马程序员——C语言基础 枚举 宏定义 自定义 static exterm
Java培训.Android培训.iOS培训..Net培训.期待与您交流! (以下内容是对黑马苹果入学视频的个人知识点总结) (一)枚举 1)枚举类型的定义 枚举是C语言中的一种基本数据类型,并不是构 ...
随机推荐
- HCNP Routing&Switching之ARP安全
前文我们了解了IP安全相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/16652367.html:今天我们来聊一聊ARP安全相关话题: 什么是ARP? ...
- CentOS7添加永久路由
CentOS7永久静态路由需要写到 /etc/sysconfig/network-scripts/route-****** 文件中,故在/etc/sysconfig/network-scripts/下 ...
- fastapi快速入门
fastapi是高性能的web框架.他的主要特点是: 快速编码 减少人为bug 直观 简易 具有交互式文档 基于API的开放标准(并与之完全兼容):OpenAPI(以前称为Swagger)和JSON ...
- 使用ko安装的nexus想作为maven仓库使用
默认安装的nexus并不能作为maven仓库使用,只能添加maven仓库来使用,具体可以查看该文档:使用nexus3配置maven私有仓库 https://www.cnblogs.com/sanduz ...
- nginx+gunicorn部署Django项目
实际采用的nginx.conf文件内容: server { charset utf-8; listen 80; server_name ip; access_log /webapps/project/ ...
- OpenJudge 1.5.35:求出e的值
35:求出e的值 总时间限制:1000ms 内存限制:65536kB 描述 利用公式e = 1 + 1/1! + 1/2! + 1/3! + ... + 1/n! 求e . 输入 输入只有一行,该行包 ...
- CSP-J2020 洛谷P7072 直播获奖(Splay/桶排序)
题目描述 NOI2130 即将举行.为了增加观赏性,CCF 决定逐一评出每个选手的成绩,并直播即时的获奖分数线.本次竞赛的获奖率为 w%,即当前排名前 w% 的选手的最低成绩就是即时的分数线. 更具体 ...
- [题解] Codeforces Dytechlab Cup 2022 1737 A B C D E 题解
傻*Dytechlab还我rating!(不过目前rating还没加上去,据说E是偷的说不定要unrated) 实在没预料到会打成这样... 求点赞 点我看题 A. Ela Sorting Books ...
- python中的多线程与多进程
线程概念: 线程也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位. 线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其 ...
- Annocation(注解)的使用示例
示例一:生成文档相关的注解示例二:在编译时进行格式检查(JDK内置的三个基本注解)@Override: 限定重写父类方法, 该注解只能用于方法@Deprecated: 用于表示所修饰的元素(类, 方法 ...