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语言中的一种基本数据类型,并不是构 ...
随机推荐
- linux下安装mysql(rpm安装)
Mysql 5.7.29安装步骤 1.首先卸载自带的Mysql-libs(如果之前安装过mysql,要全都卸载掉) rpm -qa | grep -i -E mysql\|mariadb | xarg ...
- SonarQube 的安装、配置及 Maven 项目的使用
转载自:https://cloud.tencent.com/developer/article/1010612 1.SonarQube 介绍 SonarQube 是一个用于管理源代码质量开放平台,它可 ...
- C++自学笔记 初始化列表 Initializer list
初始化p A(){ p = 0;cout<<"A::A()"<<endl;} 初始化列表 Initializer list A():p(0){ cout&l ...
- Tubian0.42
0.43已发布:https://www.cnblogs.com/tubentubentu/p/16746748.html Sourceforge.net下载:https://sourceforge.n ...
- PHP全栈开发(八):CSS Ⅵ 列表 style
列表分为有序列表和无序列表 我们知道有序列表的标签是<ol>意思是order list 无序列表的标签是<ul> 列表里面每项的标签用<li>来进行包裹. 使用CS ...
- Flutter Cocoon 已达到 SLSA 2 级标准的要求
文/ Jesse Seales, Dart 和 Flutter 安全工作组工程师 今年年初,我们发布了 Flutter 2022 产品路线图,其中「基础设施建设」这部分提到:2022 年 Flutte ...
- day45-JDBC和连接池01
JDBC和连接池01 1.JDBC概述 基本介绍 JDBC为访问不同的数据库提供了同一的接口,为使用者屏蔽了细节问题 Java程序员使用JDBC,可以连接任何提供了jdbc驱动程序的数据库系统,从而完 ...
- Spring的自动装配和注解
Bean的自动装配 自动装配说明 自动装配是使用spring满足bean依赖的一种方法 spring会在应用上下文中为某个bean寻找其依赖的bean. Spring的自动装配需要从两个角度来实现,或 ...
- 7.MongoDB系列之聚合框架
1. 管道阶段和可调参数 聚合框架基于管道的概念.他由多个阶段组成,每个阶段都会提供一组按钮或可调参数.每个阶段对其输入执行不同的数据处理任务,并生成文档已作为输出传递到下一阶段. 2. 阶段常见操作 ...
- Linux基础_7_文本显示
注:实质是针对标准输出文本的各种骚操作! 简单查看 注:初略加工后进行显示. cat -n 文件名 #查看文件内容并显示行号 tac 文件名 #逆序查看 more 文件名 less 文件名 #按?搜索 ...