条款2:不要滥用宏

文章目录

  • 优先选择编译器而不是预编译器
  • 两种特殊情况
  • 使用宏替代函数调用
  • 总结

优先选择编译器而不是预编译器

 假设我们预定义了一个宏#define ASPECT_RATIO 1.653,当我们的程序在这个地方出现错误的时候。可能会出现以下的问题:

  • 符号名称ASPECT_RATIO可能永远不会被编译器看到。它可能在源代码进入编译器之前被预处理器删除。(如果发生错误,编译器的给出的错误信息可能是1.653,而不是ASPECT_RATIO。设想一下,如果这部分不是你自己写的,而来自某个头文件呢?)
  • 目标代码中,可能出现多个1.653的副本。
     也可能由于这个宏是你提供给别人的库导致如果在这个地方出现问题的时候,别人可能找不到这个问题导致程序错误。
     这个时候我们有更好的替代方案,定义一个常量:const double AspectRatio = 1.653;

两种特殊情况

1、定义常量指针
 由于常量定义式通常被放在头文件内(以便被不同的源码包含),因此有必要将声明为const。例如若要在头文件内定义一个常量的字符串,你必须写const两次:const char *authorName = “Scott Meyers”;
2、类的常量
 要把常量的作用域限制为类,就必须把它设为成员;要确保最多有一个常量的副本,就必须把它设为静态成员:

class GamePlayer {
private:
//#define NUMTURNS 5 //宏无法将作用域现在在类的内部
    static const int NumTurns = 5;  // constant 声明
    int scores[NumTurns];           // 使用constant
    //...
public:
    int numTurns() { 
	return NumTurns; 
}
};
const int GamePlayer::NumTurns; // NumTurns的定义;

1、通常,C++要求为所使用的任何东西提供定义,但类专用的静态整数类型(例如integer、char、bool)常量是个例外。只要不获取它们的地址,就可以在不提供定义的情况下声明并使用它们。
2、因为类常量的初始值是在声明时指定的,所以在定义时不允许赋初始值。
关于类内定义常量,我们还可以使用枚举量:

class GamePlayer {
private:
    enum { NumTurns = 5 }; 
    int scores[NumTurns]; // fine,而且枚举更像#define,不可以取地址
    //...
};

使用宏替代函数调用

 #define指令的另一个常见(错误)用法是使用它来实现看起来像函数的宏,但不会引起函数调用的开销。下面是一个宏,它使用较大的参数调用某个函数f:

int f(int num) { return num; }
// 使用较大的参数,调用函数f
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))

像这样的宏有很多缺点。必须记住将宏主体中的所有参数都用括号括起来。否则,当有人用表达式调用宏时,可能会遇到麻烦。但即使你很小心,还是可能会发生奇怪的事情:

int a = 5, b = 0;
std::cout << CALL_WITH_MAX(++a, b) << std::endl; 		// a 递增了2次
std::cout << CALL_WITH_MAX(++a, b + 10) << std::endl; 	// a 递增了1次

在这里插入图片描述

 运行结果:我们可以发现由于宏是直接替代的方式,所以实际上a被++了两次,在返回的时候也被++一次,所以第一次结果为7,第二次返回的是b+10,所以第二次的结果为8,两次返回的结果不一致。
 替代方案:通过使用内联函数模板,可以获得宏的所有效率,以及普通函数的所有可预测行为和类型安全

template<typename T> 
inline T callWithMax(const T& a, const T& b)
{
    return f(a > b ? a : b);
}

总结

 考虑到const、枚举和内联的可用性,对预处理器(特别是#define)的需求减少了,但并没有完全消除。#include仍然是必不可少的,#ifdef/#ifndef在控制编译方面继续发挥重要作用。

  • 对于简单常量,首选const对象或枚举,而不是#define。
  • 对于类似函数的宏,优先选择内联函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/213062.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MQTT客户端、代理(broker)和连接建立

在前篇文章&#xff08;http://t.csdnimg.cn/IamPz&#xff09;中&#xff0c;介绍了发布/订阅架构和MQTT如何据此交换信息&#xff0c;其中的关键概念是&#xff1a; 发布/订阅架构触耦了负责发布信息的客户端&#xff08;发布者&#xff09;和负责接收信息的客户端&#xff…

C语言-联合和枚举

------------------------------------ --------------- ------ 最慢的步伐不是跬步&#xff0c;而是徘徊&#xff1b; 最快的脚步不是冲刺&#xff0c;而是坚持。 今天来到我们的联合和枚举类型的讲解&#xff1a; 目录 联合体类型 联合体类型的声明 联合体类型的特点 …

Wireshark抓包分析RTMP协议时,出现Unknown问题

进行rtmp推流时&#xff0c;使用wireshark抓包&#xff0c;发现部分包显示Unknown 解决方法&#xff1a; 编辑 -> 首选项 -> Protocols -> RTMPT&#xff0c;这里Maximum packet size默认是32768 将该值调大&#xff0c;比如调成1048576&#xff0c;即可解决该问题。…

ChatGPT 的 18 种玩法,你还不会用吗?

你确定&#xff0c;你会使用 ChatGPT 了吗&#xff1f; 今天给大家整理了 18 种 ChatGPT 的用法&#xff0c;看看有哪些方法是你能得上的。 用之前我们可以打开R5Ai平台&#xff0c;可以免费使用目前所有的大模型 地址&#xff1a;R5Ai.com 语法更正 用途&#xff1a;文章…

改进LiteOS中物理内存分配算法(详细实验步骤+相关源码解读)

一、实验要求 优化TLSF算法&#xff0c;将Best-fit策略优化为Good-fit策略&#xff0c;进一步降低时间复杂度至O(1)。 优化思路&#xff1a; 1.初始化时预先为每个索引中的内存块挂上若干空闲块&#xff0c;在实际分配时避免分割&#xff08;split&#xff09;操作&#xff…

[原创]C++98升级到C++20的复习旅途-从汇编及逆向角度去分析“constexpr“关键字

[简介] 常用网名: 猪头三 出生日期: 1981.XX.XXQQ: 643439947 个人网站: 80x86汇编小站 https://www.x86asm.org 编程生涯: 2001年~至今[共22年] 职业生涯: 20年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、Delphi…

AtCoder Beginner Contest 331 题解 A-E

目录 A - TomorrowB - Buy One Carton of MilkC - Sum of Numbers Greater Than MeD - Tile PatternE - Set Meal A - Tomorrow 原题链接 题目描述 已知一年有M个月D天&#xff0c;求出第y年m月d天的后一天是哪一天。 思路&#xff1a;分类讨论 分别讨论m和d的是否是最后一个月…

基于SpringBoot的旅游信息网【源码好优多】

简介 旅游信息网是一款介绍旅游相关内容的网站&#xff0c;分为前台和后台部分&#xff0c;其中前台用户注册以后可以浏览景点、景点详情、预订景点、酒店、车票、保险、以及浏览旅游攻略、个人信息修改、在线留言等&#xff0c;管理员在后台对景点、攻略、订单信息、酒店信息、…

oj赛氪练习题

数组调整 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner new Scanner(System.in);int n scanner.nextInt();int k scanner.nextInt();int[] arr new int[n];for (int i 0; i < n; i) {arr[i] scanner.nextIn…

java源码-类与对象

1、面向对象与面向过程 在了解类和对象之前我们先了解一下什么是面向过程和面向对象。 1&#xff09;面向过程编程&#xff1a; C语言就是面向过程编程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题。 2&#xff09;面向对…

Redis 发布订阅机制深入探索

Redis 的发布订阅&#xff08;pub/sub&#xff09;机制是一种消息传递模式&#xff0c;允许消息的发送者&#xff08;发布者&#xff09;和消息的接收者&#xff08;订阅者&#xff09;通过一个中介层&#xff08;频道&#xff09;进行通信&#xff0c;而无需彼此直接交互。以下…

半导体工艺发展概述

集成电路发展到今天&#xff0c;经历从1940年的PN结发现&#xff0c;到1950年BJT三极管发明&#xff0c;再到1963年CMOS电路发明。从单纯基于Si的半导体电路&#xff0c;再到GaAs, GaN&#xff0c;SiGe, InP等化合物半导体集成电路。不断的通过化学材料配比&#xff0c;基本单元…

TinyVue 组件库助力赛意信息获得工业软件种子奖

首先恭喜广州赛意信息科技股份有限公司荣获工业软件种子奖&#xff01;在本次大赛中&#xff0c;凭借“数据驱动智造&#xff0c;基于 iDME 的赛意新一代 SMOM 赋能电子行业制造运营管理解决方案”这一作品脱颖而出~ 大赛简介 10月30日至10月31日&#xff0c;由广东省工业和信…

圆通速递查询入口,以表格的形式导出单号的每一条物流信息

批量查询圆通速递单号的物流信息&#xff0c;以表格的形式导出单号的每一条物流信息。 所需工具&#xff1a; 一个【快递批量查询高手】软件 圆通速递单号若干 操作步骤&#xff1a; 步骤1&#xff1a;运行【快递批量查询高手】软件&#xff0c;并登录 步骤2&#xff1a;点击…

Hadoop——分布式计算MapReduce和资源调度Yarn

分布式计算 MapReduce YARN架构 YARN集群部署 一、Hadoop安装目录下/etc/hadoop修改mapred-env配置文件&#xff0c;mapred-site.xml文件 二、etc/hadoop文件内&#xff0c;修改yarn-env.sh&#xff0c;yarn-site.xml 三、将配置好的文件分发到其他服务节点 start-dfs.…

SLAM ORB-SLAM2(10)轨迹跟踪过程

SLAM ORB-SLAM2(10)轨迹跟踪过程 1. 总体过程2. ORB 特征点提取2.1. 相机数据处理2.1.1. 单目相机图像处理2.1.2. 双目相机图像处理2.1.3. RGBD相机图像处理2.2. ORB 特征点3. 地图初始化3.1. 坐标形式3.2. 坐标原点3.3. 地图尺度4. 相机位姿初始估计4.1. 关键帧4.2. 运动模型…

文件搜索神器—Everything,结合内网穿透秒变在线搜索神器!

Everythingcpolar搭建在线资料库&#xff0c;实现随时随地访问 文章目录 Everythingcpolar搭建在线资料库&#xff0c;实现随时随地访问前言1.软件安装完成后&#xff0c;打开Everything2.登录cpolar官网 设置空白数据隧道3.将空白数据隧道与本地Everything软件结合起来总结 前…

【每日一题】1423. 可获得的最大点数-2023.12.3

题目&#xff1a; 1423. 可获得的最大点数 几张卡牌 排成一行&#xff0c;每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。 每次行动&#xff0c;你可以从行的开头或者末尾拿一张卡牌&#xff0c;最终你必须正好拿 k 张卡牌。 你的点数就是你拿到手中的所有…

【Android】解决安卓中并不存在ActivityMainBinding

安卓中并不存在ActivityMainBinding这个类&#xff0c;这个类是在XML布局的最外层加入就会自动生成。但是你在最后绑定主布局时会报错获取不到根节点getRoot(). 最好的办法就是&#xff0c;删除原来的最外层节点&#xff0c;再重新添加&#xff0c;感觉是因为复制时并没有让系…

[蓝桥杯 2020 省 AB1] 解码

做题前思路&#xff1a; 1.因为是多组输入&#xff0c;又包含字符于是我们可以先定义一个char类型数组arr 2.定义数组的长度&#xff1a;题目说简写&#xff08;字母加数字&#xff09;长度不超过100&#xff0c;但原来的长度可能超过100&#xff0c;加上小明不会将连续超过9…