首页 > 编程语言 >C++ 之 宏定义

C++ 之 宏定义

时间:2022-11-28 20:12:03浏览次数:72  
标签:__ 定义 C++ macros add memory define

    宏在 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++,macros,add,memory,define
From: https://www.cnblogs.com/xinxue/p/16893770.html

相关文章

  • Android自定义搜索框(EditText)的搜索功能实现,过滤ListView
    实现自定义搜索框(实际上就是一个EditText):<RelativeLayoutandroid:id="@+id/music_search_rl"android:layout_width="match_parent"android:layout_......
  • JSP中的自定义标签
    目录​​目录​​​​简介​​​​入门案例​​​​自定义标签功能扩展​​​​传统自定义标签的运行原理​​​传统自定义标签的使用​​​控制JSP页面部分内容执行​​​......
  • MySQL自定义函数
     ⚠不推荐将业务逻辑存储在数据库中.MySQL不仅提供了很多很方便的内置函数,用户还可以自定义函数。不同于MongoDB对Js函数的良好支持,MySQL的自定义函数用起来感觉处处掣......
  • spdlog库和fmt库是否使用dll宏定义
    定义FMT_HEADER_ONLY定义SPDLOG_COMPILED_LIB使用fmt.dll使用spdlog.dllNONOYESNOYESNONONONOYESYESYESYESYESYESYES......
  • iOS开发之自定义ActionSheet视图
    有时我们需要用到actionSheet来展示,但是但是往往系统的界面显示很丑或者并不符合UI的要求,所以在这里自定义一个,方便以后使用,后续有时间写一下Swift的开发。自定义ActionShee......
  • 自定义UICollectionViewController之后如何设置布局方式
    今天使用了自定义UICollectionViewController,发现了布局问题,所以给初学者讲解一下,当我们自定义了UICollectionViewController就无法设置UICollectionView的布局样式的问题......
  • C++11:lambda匿名函数
    lambda源自希腊字母表中第11位的λ,在计算机科学领域,它则被用来表示一种匿名函数。所谓匿名函数,简单地理解就是没有名称的函数,又常被称为lambda函数或者lambda表达......
  • pinia源码解读二(定义模块)
    定义模块store.ts文件的defineStore方法判断是option写法还是setup写法isSetupStore=typeofsetup==='function'内部创建useStore函数,并给函数绑定$id属性为用......
  • C++中的几种构造函数和析构函数
    本篇文章,我们来了解一下C++中的几种构造函数,以及析构函数#include<format>#include<iostream>#include<string>usingstd::format;usingstd::cout;usingstd::......
  • 自定义中文全文索引
    自定义中文全文索引​​一、中文分词插件​​​​1、分词组件的调整​​​​2、分词测试​​​​二、样例数据准备​​​​三、通过中文全文分词组件创建节点索引​​​​四......