Effective C++读书笔记——(一)Prefer consts, enums, and inlines to #defines

Clion+platformIO开发ESP32时找不到lib文件中引入第三方库的头文件(头文件下有波浪线),实际编译是成功的解决方法 Clion+platformIO开发ESP引入第三方库文件编译器无法找到.h文件但编译成功 阅读详情

const与#define的不同之处和const的优势早已耳熟能详,而Effective C++(后面简称EC)对这个话题的阐述使我有了几点新的认识:

1. it's important that the pointer be declaredconst, usually in addition to what the pointer points to. To define a constantchar*-based string in a header file, for example, you have to writeconsttwice:

一个指针,除了注意他指向的类型是否为常量外,还要注意指针本身是否需要为常量。要定义一个char *类型的字符串常量,需要写两个const才行:

const char * const authorName = "Scott Meyers";

等同于:

const  string authorName("Scott Meyers");

2. The enum hack is worth knowing about for several reasons. First, the enum hack behaves in some ways more like a#define than aconst does, and sometimes that's what you want. For example, it's legal to take the address of aconst, but it's not legal to take the address of an enum, and it's typically not legal to take the address of a#define, either.

...

Also, though good compilers won't set aside storage for const objects of integral types (unless you create a pointer or reference to the object), sloppy compilers may, and you may not be willing to set aside memory for such objects. Like#defines, enums never result in that kind of unnecessary memory allocation.

为什么enum类型是不能取址的?为什么#define和enum是不会分配空间的?

希望知道的人不吝赐教。谢谢。

3. 不管是面试还是笔试,经常会被问到下面的表达方式是否合理:

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

答案当然是不合理,原因是用宏来表达函数时,参数一定要加括号保护,以避免出现意想不到的运算结果。很多书上都讲要给函数的参数加括号。那是不是加了括号就一定没有问题了呢。以前我是这么认为的,没有考虑更多。EC告诉我,这样还是不够安全,比如把上面的表达式换成:

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

如果

int a = 5, b = 0;

那么

CALL_WITH_MAX(++a, b);          // a is incremented twice

CALL_WITH_MAX(++a, b+10);       // a is incremented once

由于入参的不同,导致a被运算了不同的次数。

所以最好是使用内联函数(inline)来替换用宏表达的函数:

template<typename T>                               // because we don't know what T is, we pass by reference-to-const 

inline void callWithMax(const T& a, const T& b)   

{                                                  

    f(a > b ? a : b);                               

}

这样就算不加括号也不用担心上面的问题,而且callWithMax是一个真实的函数,可以有自己的使用范围和访问权限,比如把他声明为一个类的private成员函数。而使用宏通常是不能做到这些的。

4. 今天还照着EC写了一段代码,收获很多。

class TextBlock {
private:
    string text;
public:
    TextBlock(string txt) {
        text = txt;
    }
     char& operator[] (size_t position)   {
        cout  <<  "const"  <<  endl;
        return text[position];
    }
    const char& operator[] (size_t position) const {
        cout  << "not const" << endl;
        return text[position];
    }
    void print() const {
        cout << text << endl;
    }
};
int main() {
	TextBlock tb("Hello");
    cout << tb[0] << endl;
    tb.print();

    const TextBlock ctb("World");
    cout << ctb[0] << endl;
    ctb.print();
    getch();
    return 0;
}


这里如果把void print() const或者const char& operator[] (size_t position) const中的const修饰符去掉,ctb的调用就会报错。而如果把第一个运算符重载函数char& operator[] (size_t position)去掉,tb的调用不会有问题。原因是:

在调用上,在未指明为const的类调用时,使用的是非const的函数。如果没有非const的函数,他才会调用const的函数。而如果const的类去调用非const的函数,会直接报错。所以在写代码时,可以考虑是不是可能会修改成员,如果希望是不修改的,那么请用const。

 5. 同样以上面的代码为基础,

class TextBlock {
private:
    string text;
public:
    TextBlock(string txt) {
        text = txt;
    }
      char& operator[] (size_t position)   {
        cout  <<  "const"  <<  endl;
        return text[position];
    }
     /*
    const char& operator[] (size_t position) const {
        cout  << "not const" << endl;
        return text[position];
    }
    */
    void print() const {
        cout << text << endl;
    }
};
int main() {
	TextBlock tb("Hello");
    cout << tb[0] << endl;
    tb.print();
/*
    const TextBlock ctb("World");
    cout << ctb[0] << endl;
    ctb.print();
    */
    getch();
    return 0;
}

这段代码运行没有问题。如果把char& operator[] (size_t position)改为char& operator[] (size_t position) const,就会编译报错:

error C2440: 'return' : cannot convert from 'const char' to 'char &'

这是因为,用const修饰的成员函数本身是限制不更改成员变量的,函数中确实也没有修改,但是返回的是一个引用,编译器发现有成员变量可能在该函数外部被修改,所以报错。所以这里要么改为
const char& operator[] (size_t position) const

要么改为

char operator[] (size_t position) const

后一种改法返回的是char的一个拷贝,就算被修改了也不会影响成员变量。

考虑到函数本身是没有修改成员变量的需求的,所以源代码中完全不加const的做法欠妥当。

置顶必读(1) |《YOLOv11实战:从入门到深度优化》专栏导读,带你一文了解本专栏! 🏆 本文收录于 《YOLOv8实战:从入门到深度优化》 专栏,该专栏专注于分享我在YOLOv8目标检测模型中的实战经验,涵盖从入门基础到深度优化的全方位解决方案。无论你是计算机视觉的新手,还是具有多年深度学习经验的专家,本专栏都将为你提供高效、可复现的技术方案,助你掌握YOLOv8的核心技术,提升目标检测性能,突破瓶颈,优化模型,并加速项目开发🚀! 阅读详情

相关推荐

开发板的使用

一、开发板使用学习大纲。 1、开发板组成、核心板资源,底板资源。 2、开发板连接、调试工具、串口参数配置。 3、开发板启动过程。 4、文件传输 1)使用串口传输。 上传/下载 2)使用网口传输。 上传/下载 5、临时/永久配置开发板IP地址。 6、开发板启动脚本。 二、开发板的组成。 核心板: CPU处理器: 芯片:S5P6818(三星) 内核: ARM cortex-A53 -> 八核处理器 运行内存:512MB*2=1G nandflash:4G 底板: 电源线:5V 串口...

qq_45633736的博客 2659

Item 2: Prefer consts, enums, and inlines to #defines(Effective C++)

  When you do something like this,1#defineASPECT_RATIO1.653   the symbolic name ASPECT_RATIO may never be seen by compilers; it may be removed by the preprocessor before the source code ever gets ...

weixin_30256901的博客 225

告别‘Duang’的一声:手把手解决RK3588开发板PCIe初始化卡死问题(DTS修改实战)

本文详细介绍了解决RK3588开发板PCIe初始化卡死问题的完整过程,包括问题诊断、DTS文件修改及编译烧写步骤。通过禁用未使用的PCIe硬件资源,有效解决了启动卡死问题,为嵌入式开发者提供了实用的调试方法和经验总结。

weixin_30753873的博客 216

Effective C++读书笔记(3)-Item 2: Prefer consts, enums, and inlines to #defines

Item 2: Prefer consts, enums, and inlines to #defines   This Item might better be called "prefer the compiler to the preprocessor“ #define ASPECT_RATIO 1.653 The solution is to replace

Geek4IT的专栏 552

Android开发者必看:F2FS文件系统如何优化你的手机存储性能(附实测数据对比)

本文深入解析了F2FS文件系统如何针对闪存特性优化Android设备存储性能。通过对比ext4,F2FS采用追加写和冷热数据分离策略,将随机写转化为顺序写,显著提升写入速度并减少GC卡顿。实测数据显示,其在随机写入和SQLite事务等场景下性能提升显著,为开发者提供了优化应用IO性能的关键思路。

weixin_29248313的博客 325

DANN实战:用Python手把手教你实现对抗迁移学习(附完整代码)

本文通过Python实战教程,手把手教你实现DANN对抗迁移学习模型,以解决领域偏移问题。文章详细解析了领域对抗神经网络的核心机制,包括梯度反转层和领域判别器的构建,并提供了从数据准备、模型搭建到训练评估的完整代码,帮助开发者快速掌握这一强大的无监督领域自适应技术。

net55的博客 318

Effective C++ (3rd Ed) 读书笔记(一)Item 2: Prefer constS, enumS, and inlineS to #defineS

#includecxvxv

persimmon2010的专栏 516

电子取证之服务器取证思路

服务器取证是一个技术性很强的领域,涉及到多个环节,包括网站重构、网站代码分析、容器分析等。数据库密码的获取通常通过查看配置文件、日志文件、备份等途径,同时可以通过一些绕过技术如禁用身份验证来获取权限。对于不同的数据库,需要根据其特性采取相应的应对方法和命令。

2401_84830066的博客 2490

Effective C++ Item 2:Prefer constS, enumS, and inlineS to #defineS

这个条款叫“喜欢编译器胜过预处理器”更贴切。因为#define不被视为语言的一部分。当你这样写时, #define ASPECT_RATIO 1.653 符号名ASPECT_RATIO对于编译器来讲是不可见的;在源程序到达编译器之前,ASPECT_RATIO很可能被预处理器删除了。所以,ASPECT_RATIO符号名也许压根就没有进入符号表。这就会使你很困惑了,特别编译过程中涉

hfutqianwei的专栏 494

Halcon异常检测训练避坑指南:如何避免GPU内存溢出和样本不足问题

本文针对Halcon深度学习异常检测训练中常见的GPU内存溢出和样本不足两大难题,提供了从原理分析到实战解决方案的深度指南。通过调整批次大小、图像尺寸、使用梯度累积等技巧有效管理显存,并利用针对性数据增强与人工合成策略破解小样本困境,帮助开发者构建稳定高效的训练流程。

river的博客 216

SpringCloud详细教程(下)

SpringCloud详细教程,springcloud完全教程。Springcloud微服务教程(上)的姊妹篇:介绍了其它SpringCloud组件。

鸟哥哥的专栏 4万+

《Effective C++》rule 02: Prefer consts, enums, and inlines to #defines

宏定义的问题 C++ 会在预处理阶段对宏定义进行字符串替换. 因此,如果在一个头文件进行了类似如 #define ASPECT_RATIO 1.653 的宏定义,那么此常量相关的编译错误信息显示的会是 1.653, 而这个宏定义如果不在你写的程序内 (而是它包含的一个头文件内),那么定位问题就会很麻烦. 原因很简单: 宏定义在预处理阶段就被处理,因此使用的名称并没有进入符号表 (symbol table) 中. 如何解决 对于常量,可以使用 const 来代替 #define: const double A

RJ_theMag的博客 265

VS2019项目使用CGAL6.1配置教程详细步骤

在VS2019项目中使用CGAL详细步骤

插件开发 331

Effective C++ 2. Prefer consts, enums, and inlines to #defines

consts & enums & inlines

ruihuank的博客 266

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

effective C++ Item 2: Prefer consts, enums, and inlines to #defines

前面一款基本上就是介绍性的内容,这一节进入主题。   条款二:尽量以const,enum,inline代替#define   1:使用const代替#define定义常量。     如果你定义圆周率如下: #define PI 3.1415926    那么意味着预处理

manyouzhelilixi的专栏 538

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

[翻译] Effective C++, 3rd Edition, Item 2: 用 consts, enums 和 inlines 取代 #defines

Item 2: 用 consts, enums 和 inlines 取代 #defines作者:Scott Meyers译者:fatalerror99 (iTePubs Nirvana)发布:http://blog.youkuaiyun.com/fatalerror99/这个 Item 改名为“用 compiler(编译器)取代 preprocessor(预处理器)”也许更好一些,因为 #d

艺术编程 6553

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Effective C++记录(2):Prefer consts, enums, and inlines to #defines

Effective C++记录

WCAO_ERROR的博客 235

Effective C++:条款02:尽量以const, enum, inline替换#define (Prefer consts, enums, and inline to #defines.)...

对于单纯常量,最好以const对象或enums替换#defines。 对于形似函数的宏(macros),最好改用inline函数替换#defines。 转载于:https://www.cnblogs.com/elite/archive/2011/03/19/1988657.html...

dibi3773的博客 121

Live555 Streaming Media学习(一)——openRTSP分析

live555是一个开源的流媒体库,支持RTP/RTCP,RTSP,SIP。它提供了一套可供开发调用的库,同时还提供了很多例子,在testProgs文件夹下。开发人员只需要依葫芦画瓢,就可以写出自己的代码。要想学习live555的代码,最好也以这些例子为入口,去分析live555的代码结构。 本文就分析下openRTSP这个例子。在切入主题之前,有一些基础知识需要准备。 select() s

zhhx310的专栏 1351

http请求

HTTP请求类型包括GET, POST, PUT和PUT。HTTP请求的报文格式为: GET和POST的区别为:GET请求中,数据是放在中。而POST请求中,数据是放在中。也就是说,如果是GET请求,用户发送的数据会显示在浏览器的网址栏里面,POST请求则不会。所以POST请求比GET请求更安全一些。   HTTP响应报文的格式为: [] 所谓的状态码(200

zhhx310的专栏 591

SSL

SSL secure socket layer 一种介于应用层和传输层之间的安全的传输协议。 SSL的主要特征是: 1. 数据加密 2. 身份验证 3. 消息完整性验证 SSL中加密包括对称和非对称加密算法,相比较而言,对称算法运算速度更快一些。所以,SSL通信中一些比较小而且重要的数据都是通过非对称算法做的,比如密钥交换。而普通的大数据是通过非对称算法加密的。在消息完整性验证中的M

zhhx310的专栏 540

virtual关键字与多态

今天在网上看到这么一段博文(以下蓝色为引用): 一, 什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你就应该从这里开始) 简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略。下面来看一段简单的

zhhx310的专栏 473

邮件收发协议相关

SMTP: http://www.5dmail.net/html/2004-2-3/200423200710.htm POP和IMAP: http://www.5dmail.net/html/2010-10-14/20101014221009.htm 一句话:大学的网络技术基础课白学了!要好好补补。

zhhx310的专栏 399

职责链模式

今天学习了职责链模式,挺有意思。这个模式的要点主要有两点(摘录书中原话): 1. 需要给每个具体管理者设置他的上司是哪个类,也就是设置后继者。 2. 需要在每个具体管理者处理请求时,做出判断,是可以处理这个请求,还是需要“推销责任”,转移给后继者去处理。

zhhx310的专栏 323

java中的反射

今天学习了下反射,记录下给自己启发的链接: http://www.blogjava.net/amigoxie/archive/2007/04/21/112421.html http://hejianjie.iteye.com/blog/136205

zhhx310的专栏 292
上一篇: Live555 Streaming Media学习(一)——openRTSP分析
zhhx310
博客等级 码龄18年 2粉丝 12原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值