当前位置: 首页 > article >正文

条款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。
  • 对于类似函数的宏,优先选择内联函数。

http://www.kler.cn/news/155792.html

相关文章:

  • 【Linux服务器Java环境搭建】05 Node JS安装及环境变量配置
  • 【数据库】基于封锁的数据库调度器,以及等待锁处理的优先级策略
  • 电磁兼容EMC理论基础汇总
  • ubuntu 下载编译 opencv4.2.0并检验
  • 详细学习Pyqt5的10种容器(Containers)
  • STM32 SCF文件
  • 有什么值得推荐的node. js练手项目吗?
  • Redis 数据结构详解
  • 共享智能指针shared_ptr
  • windows 映射 webdav 为本地磁盘
  • ChatGPT 的 18 种玩法,你还不会用吗?
  • 31-WEB漏洞-文件操作之文件包含漏洞全解
  • 前端下拉框select标签的插件——select2.js
  • Python与GPU编程快速入门(二)
  • springboot centos集成 OpenOffice 实现 在线预览 doc excel docx 成pdf的 并且包含中文包和英文包和安装
  • WPF MVVM模式下如何将UI窗口变量传参到Viewmodel层
  • 【Web安全】拿到phpMyAdmin如何获取权限
  • mysql面试题——日志与MVCC
  • solidity实现ERC721代币标准发布NFT
  • 28.线段树与树状数组基础
  • 谈谈MYSQL索引
  • 数据库-MySQL之数据库必知必会22-26章
  • 工具网站:随机生成图片的网站
  • Fiddler抓包工具之fiddler的composer可以简单发送http协议的请求
  • 【数据库】数据库元素的层次,树形结构的下的多粒度加锁,以及幻象的正确处理
  • FIORI /N/UI2/FLP 始终在IE浏览器中打开 无法在缺省浏览器中打开
  • Facebook做外贸推广如何?
  • vue3高雅的使用useDialog
  • 设计模式-结构型模式之代理设计模式
  • 前端分片上传