Effective-C++阅读解析条款(条款二:尽量以const,enum,inline替换#define)

个人主页:Lei宝啊 

愿所有美好如期而遇


书中说这个条款或许改为“宁可以编译器替换预处理器”比较好,这句话在我看来原因是这样的:

如果我们有这样一个宏(假设写这个宏的人比较粗心):#define Add(x, y)  x + y

我们本意是想得到x+y的值,但是如果我们在这样一个表达式中:int ret = 3 * Add(4 + 5);  我们预期的结果应该是27,但是实际上我们得到的结果是17,这就是由于在预处理阶段进行了宏替换,表达式就成了这样:ret  =  3 * 4 + 5; 

又或者是这样:#define NUM 1.653,如果我们使用这个常量但是获得一个编译错误信息时,错误信息可能会提到1.653而不是NUM,因为在预处理阶段就已经进行了宏替换,所以在编译阶段已经没有NUM了。如果这个宏甚至不是我们写的,那么如果报错1.653,那我们一定对这个值没有概念,不知道他是哪里来的,所以我们不推荐使用宏,而是使用const,enum,inline等,因为他们都是在编译阶段的,在编译后会进入符号表。所以上面的宏我们可以替换成这样:const double NUM = 1.653; 

当我们使用常量替换宏,有两种特殊情况需要我们注意:

第一就是定义常量指针,由于常量定义通常被放在头文件内,因此有必要将指针和他所指的值都声明为const,假设我们定义一个常量的字符串,我们使用指针指向,我们通常这样写:

const char* const authorname = "Scot Meyers";

并且书中提到,string对象通常比char*更加合适,因为我们是可以使用char*来构造string对象,并且string对象使用起来更加方便,所以他推荐这样定义上面的authorname变量:

const std::string authorname("Scot Meyers");这样authorname这个变量也就同样不能对这个字符串进行修改了。

第二个需要注意的就是class的专属常量。为了将常量的作用域限制在class内,所以必须让其成为class内的一个成员:而常量我们没有必要让每个对象都拥有一份,所以最好是让所有对象都能够共享他,所以我们让他成为一个static成员,就像这样:

class GamePlayer
{
private:
    static const int Num = 5;
    int scores[Num];
}

这里博主要提醒一下,静态成员变量只能在类外进行初始化,而上面这个初始化是一个特殊的例子,仅仅只有const int这样的静态成员变量可以在类内这样进行声明。

并且我们书中提到,通常C++要求我们对所使用的任何一个东西提供定义式,但如果他是个class专属常量并且是static,并且还是整型,那么只要不取他们的地址,我们就可以使用他们并且无需提供他们的定义式,就像这样:

class Gameplayer
{
public:
    static const int Num = 5;
    int scores[Num];
};

int main()
{
    cout << Gameplayer::Num << endl;

    return 0;
}

但是如果我们需要取他的地址,就需要提供定义式(书中这样写的,但是博主经过测试,发现即使不提供定义式,似乎也是可以的,但是我们仍然还是按照书中的去写代码,不要依赖于编译器的各种骚操作):

并且我们这里要说,如果这样的一个常量在声明时获得初值,那么定义时不可以再给初值!

顺带一提,我们无法利用#define创建一个class专属常量,因为#define并不注重作用域,也就是说,一但宏被定义,那么他在其后的编译过程中都是有效的,除非在某处被#undef。

也就是说,没有private : #define这样的东西,他不能够用来定义class专属常量,也就没有任何封装性。

书中提到,如果你的编译器不支持静态整型常量在类内给初值,那么就将初值放在定义式。

唯一例外的是,如果有成员变量,也就是我们上面的scores数组需要这个常量值,那么在编译期间,这个常量值就必须让编译器知道,也就是说,这个常量需要在类内给一个初值,在定义式给初值是不可以的:

这也是唯一例外。

如果你的编译器不允许在类内给初值,那么可以使用enum这种补偿做法。这种做法的理论基础是:“一个枚举类型的数值可以被充作ints使用”,所以Gameplayer可以定义成这样:

书中说到enum值得我们去认识,那么我们就去认识一下,第一点,enum的行为的某方面比较像#define而不是const,他举了这样一个例子:如果我们想要取一个const的地址是合法的,而取enum的地址就不合法,同样的,取#define的地址通常也不合法。

如果我们不想让别人获得一个指针或引用指向我们的某个整型常量,enum就可以帮助我们实现这个约束,因为取他的地址是不合法的。

同时书中提到空间上的问题,说到优秀的编译器不会为整数型const对象设定另外的空间(除非我们创建一个指针或引用指向这个对象),但不够优秀的编译器可能不会这么做,而这可能不是我们想要的结果。enum就和#define一样绝不会导致非必要的内存分配,这里其实博主理解的也不是很清楚,所以也就不多解释。

书中说到认识enum的第二个理由纯粹是为了使用主义。许多代码用了他,所以我们见到他时必须认识他,事实上,enum是模板编程的基础(事实上,博主不清楚这点,解释不清)

现在我们继续谈预处理器的问题。也就是说,我们使用宏的时候,需要加小括号,否则就会像我们开始Add那样得出我们不想要的结果,而这些小括号往往令人抓狂。

首先有一个这样的函数:

int f(int a)
{
	return a;
}

 我们使用宏,有这样一个例子:

#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))

书中使用这样的函数来进行替代:

template<class T>
inline int callWIthMax(const T& a, const T& b)
{
	return f(a > b ? a : b);
}

我们对他们进行举例,看结果: 

我们可以看见宏中的++a,a是否要++竟然取决于比较的先后顺序!所幸我们不需要为这种无聊的方式浪费时间,所以就有了上面替代的函数。(其实只要使用函数,这些值都将是确定的,而不是像宏那样,烦且存在许多不确定性)。并且这样的函数可以成为在类内的private inline函数,一般而言宏无法完成此时。

有了const, enum, inline,我们对于预处理器的需求(特别是#define)降低了,但是#include仍然是必需品,并且#ifdef/#ifndef对于控制编译也是很重要的,博主这里对于#if 和 #endif也是常用,博主常常这样使用:

#include <iostream>
using namespace std;

#if 1
int main()
{
    //using...
    
    return 0;
}
#endif


#if 0
int main()
{
    //...

    return 0;
}
#endif

其实相当于一个变相的注释了,而且想释放使用时将0改成1即可。

本篇重点,请记住:

  • 对于单纯常量,最好使用const对象或enum替换#define
  • 对于形似函数的宏,我们最好改用inline函数替换#define

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

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

相关文章

“创新强基·应用强链”2024第104届上海电子展会

2024第104届上海电子展会 2024 Shanghai electron Expo 时间:2024年11月18--20日 地点:上海新国际博览中心 主题:创新强基应用强链 上海与长三角各地产业协同发展&#xff0c;到2025年&#xff0c;初步建成具有全球影响力和竞争力的世界级电子信息产业集群。产业链稳定性和韧…

黄金票据制作

1、黄金票据 黄金票据在内网渗透中主要用于权限维持&#xff0c;即留下后门。使用黄金票据前提是你已经控制了整个域的域控制器。其核心原理与服务器中的krbtgt用户相关。从以下可以了解krbtgt原理&#xff1a; 前言 | windows protocolhttps://daiker.gitbook.io/windows-pro…

DNDC模型对所有处理的土壤温度和湿度模拟效果良好,但有时土壤湿度模拟存在偏差

使用 DNDC 模型评估加拿大多样化作物轮作系统对产量和 N2O 排放的影响 原名&#xff1a;Assessing the impacts of diversified crop rotation systems on yields and nitrous oxide emissions in Canada using the DNDC model 译名&#xff1a;使用 DNDC 模型评估加拿大多样…

使用vite创建一个react18项目

一、vite是什么&#xff1f; vite 是一种新型前端构建工具&#xff0c;能够显著提升前端开发体验。它主要由两部分组成&#xff1a; 一个开发服务器&#xff0c;它基于原生 ES 模块提供了丰富的内建功能&#xff0c;如速度快到惊人的模块热更新&#xff08;HMR&#xff09;。 …

站群CMS系统

站群CMS系统是一种用于批量建立和管理网站的内容管理系统&#xff0c;它能够帮助用户快速创建大量的网站&#xff0c;并实现对这些网站的集中管理。以下是三个在使用广泛的站群CMS系统&#xff0c;它们各具特色&#xff0c;可以满足不同用户的需求。 1. Z-BlogPHP Z-BlogPHP是…

PSA制氧设备装置的使用注意事项解析

PSA制氧设备&#xff0c;即变压吸附制氧设备&#xff0c;是一种利用物理吸附原理&#xff0c;通过特定的吸附剂&#xff0c;在压力变化的情况下&#xff0c;从空气中分离出氧气的设备。由于其高效、节能、环保等特点&#xff0c;PSA制氧设备在工业、能源等领域得到了广泛应用。…

QCustomPlot一、QCustomPlot基础及画图显示

1、QCustomPlot下载 QCustomPlot源码demo 根据需要选择需要的文件&#xff1a; 完整版。QCustomPlot.tar.gz 源代码例子帮助文档&#xff1b; 共享库。QCustomPlot-sharedlib.tar.gz 库编译和使用&#xff1b; 源代码。QCustomPlot-source.tar.gz 源代码 里面包含了很多QCusto…

JavaEE 初阶篇-深入了解单例模式(经典单例模式:饿汉模式、懒汉模式)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 单例模式的概述 2.0 单例模式 - 饿汉式单例 2.1 关于饿汉式单例的线程安全问题 3.0 单例模式 - 懒汉式单例 3.1 关于懒汉式单例的线程安全问题 3.1.1 加锁 synchr…

设计模式——行为型——责任链模式Chain Of Responsibility

请求类 public class ApproverRequest {private int type;//请求批准的类型private float price;//请求的金额private int id;//请求的编号 } 审批人抽象类 public abstract class ApproverPerson {protected ApproverPerson next;protected String name;//审批过程public a…

基于SSM+Jsp+Mysql的班级同学录网站

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…

深入解析实时数仓Doris:Rollup上卷表与查询

码到三十五 &#xff1a; 个人主页 心中有诗画&#xff0c;指尖舞代码&#xff0c;目光览世界&#xff0c;步履越千山&#xff0c;人间尽值得 ! 目录 一、基本概念二、Aggregate 和 Unique 模型中的 ROLLUP三、Duplicate 模型中的 ROLLUP四、ROLLUP 调整前缀索引五、ROLLUP使…

【Mars3d绘制完成后设置离地面的实体高度】graphicLayer.startDraw绘制带高度的实体

实现效果&#xff1a; 相关需求场景&#xff1a; 绘制之后可以在success中通过graphic可以拿到所点击的点的位置&#xff0c;然后重新生成一个graphic添加到地图上&#xff0c;重新生成的面在初始化的时候可以指定想要的高度 相关实现代码&#xff1a; // 开始绘制多边形 exp…

如何对抓取的文本进行分词、词频统计、词云可视化和情感分析

目录 一、引言 二、文本分词 三、词频统计 四、词云可视化 五、情感分析 六、总结 一、引言 在大数据时代&#xff0c;文本数据的处理和分析显得尤为重要。对于爬虫抓取的大量文本数据&#xff0c;如何进行高效、准确的处理和分析&#xff0c;是每一个数据分析师和开发…

QML 用文件的方式自定义组件

自定义组件 序言方法首先新建一个QML文件QML文件名称创建完后编写自己的组件 效果注意 序言 说到QML&#xff0c;就肯定离不了自定义一说&#xff0c;毕竟你总不至于用一个qml文件写完全部吧&#xff1f;我来写点基础的东西丰富下QML资料贫乏 方法 首先新建一个QML文件 QML文…

jupyter加目录contrib nbextension 使用

jupyter在工作中使用很方便&#xff0c;可是一旦单个文件的内容多了&#xff0c;即使有markdown文本提醒&#xff0c;上下翻找依旧很费精力。这时&#xff0c;有个目录就显得至关重要了。 1 安装模块 打开anaconda的PowerShell&#xff08;带有Prompt的&#xff09;&#xff0…

nut-ui中的menu 菜单组件的二次封装

这个菜单组件 一般可以直接用到项目里 如果复用性不强的话 直接使用 但是有一个问题 如果很多地方都需要用到这个组件 我们可以把这个组件二次封装一下 <template><div class"cinema-search-filter-component"><nut-menu><template #icon>&…

大学html作业-原神官网静态页面(免费)

大学html作业-原神官网静态页面&#xff08;免费&#xff09; 源码来自githab&#xff0c;有些简单的问题我都修复了。大家可以直接去找原作者的源码&#xff0c;如果githab打不开就从我下载的网盘里下载吧。 效果 在这里插入图片描述 源码

C++11模版元编程

1.概述 模版元编程&#xff08;template metaprogram&#xff09;是C中最复杂也是威力最强大的编程范式&#xff0c;它是一种可以创建和操纵程序的程序。模版元编程完全不同于普通的运行期程序&#xff0c;它很独特&#xff0c;因为模版元程序的执行完全是在编译期&#xff0c;…

找嵌入式软件工作,freertos要掌握到什么程度?

作为一个在嵌入式软件行业工作的6年的工程师告诉你&#xff0c;这根本不是重点&#xff0c;我没有实际学过RTOS&#xff0c;都是赶鸭子上架&#xff0c;只要你会单片机&#xff0c;这种东西太简单了。在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「fre…

Rust 机器学习图形库 petgraph

一、介绍 Petgraph 是一个开源的图数据结构库&#xff0c;提供了非常丰富的图形类型和算法&#xff0c;并且支持将图形以 Graphviz 格式输出&#xff0c;还允许你为图的节点和边赋予任意类型的数据&#xff0c;从而能够灵活地处理和表示复杂的数据关系。 Petgraph 支持边的方…