Effective STL 条款39

Effective C++ 第三版 所有条款总结及对应代码描述(纯干货) Effective C++ 第三版 所有条款总结及对应代码描述(纯干货) 阅读详情

条款39:用纯函数做判断式

我讨厌为你做这些,但我们必须从一个简短的词汇课开始:

  • 判断式(predicate)是返回bool(或者其他可以隐式转化为bool的东西)。判断式在STL中广泛使用。标准关联容器的比较函数是判断式,判断式函数常常作为参数传递给算法,比如find_if和多种排序算法。(排序算法的概览可以在条款31找到。)
  • 纯函数是返回值只依赖于参数的函数。如果f是一个纯函数,x和y是对象,f(x, y)的返回值仅当x或y的值改变的时候才会改变。
    在C++中,由纯函数引用的所有数据不是作为参数传进的就是在函数生存期内是常量。(一般,这样的常量应该声明为const。)如果一个纯函数引用的数据在不同次调用中可能改变,在不同的时候用同样的参数调用这个函数可能导致不同的结果,那就与纯函数的定义相反。

现在已经很清楚用纯函数作判断式是什么意思了。我要做的所有事情就是使你相信我的建议是有根据的。要帮我完成这件事,我希望你能原谅我再增加一个术语所给你带来的负担。

  • 一个判断式类是一个仿函数类,它的operator()函数是一个判断式,也就是,它的operator()返回true或false。正如你可以预料到的,任何STL想要一个判断式的地方,它都会接受一个真的判断式或一个判断式类对象。

就这些了,我保证!现在我们已经准备好学习为什么这个条款提供了有遵循价值的指引。

条款38解释了函数对象是传值,所以你应该设计可以拷贝的函数对象。用于判断式的函数对象,有另一个理由设计当它们拷贝时行为良好。算法可能拷贝仿函数,在使用前暂时保存它们,而且有些算法实现利用了这个自由。这个论点的一个重要结果是判断式函数必须是纯函数。

想知道这是为什么,先让我们假设你想要违反这个约束。考虑下面(坏的实现)的判断式类。不管传递的是什么参数,它严格地只返回一次true:第三次被调用的时候。其他时候它返回假。

class BadPredicate:    // 关于这个基类的更多信息
 public unary_function<Widget, bool> { // 请参见条款40
public:
 BadPredicate(): timesCalled(0) {}  // 把timesCalled初始化为0
 bool operator()(const Widget&)
 {
  return ++timesCalled == 3;
 }
private:
 size_t timesCalled;
};

假设我们用这个类来从一个vector<Widget>中除去第三个Widget:

vector<Widget> vw;    // 建立vector,然后
     // 放一些Widgets进去
vw.erase(remove_if(vw.begin(),  // 去掉第三个Widget;
   vw.end(),  // 关于erase和remove_if的关系
   BadPredicate()), // 请参见条款32
   vw.end());

这段代码看起来很合理,但对于很多STL实现,它不仅会从vw中除去第三个元素,它也会除去第六个!

要知道这是怎么发生的,就该看看remove_if一般是怎么实现的。记住remove_if不是一定要这么实现:

template <typename FwdIterator, typename Predicate>
FwdIterator remove_if(FwdIterator begin, FwdIterator end, Predicate p)
{
 begin = find_if(begin, end, p);
 if (begin == end) return begin;
 else {
  FwdIterator next = begin;
  return remove_copy_if(++next, end. begin, p);
 }
}

这段代码的细节不重要,但注意判断式p先传给find_if,后传给remove_copy_if。当然,在两种情况中,p是传值——是拷贝——到那些算法中的。(技术上说,这不需要是真的,但实际上,是真的。详细资料请参考条款38。)

最初调用remove_if(用户代码中要从vw中除去第三个元素的那次调用)建立一个匿名BadPredicate对象,它把内部的timesCalled成员清零。这个对象(在remove_if内部叫做p)然后被拷贝到find_if,所以find_if也接收了一个timesCalled等于0的BadPredicate对象。find_if“调用”那个对象直到它返回true,所以调用了三次,find_if然后返回控制权到remove_if。remove_if继续运行后面的调用remove_copy_if,传p的另一个拷贝作为一个判断式。但p的timesCalled成员仍然是0!find_if没有调用p,它调用的只是p的拷贝。结果,第三次remove_copy_if调用它的判断式,它也将会返回true。这就是为什么remove_if最终会从vw中删除两个Widgets而不是一个。

最简单的使你自己不摔跟头而进入语言陷阱的方法是在判断式类中把你的operator()函数声明为const。如果你这么做了,你的编译器不会让你改变任何类数据成员。

class BadPredicate:
 public unary_function<Widget, bool> {
public:
 bool operator()(const Widget&) const
 {
  return ++timesCalled == 3;  // 错误!在const成员函数中
 }     // 不能改变局部数据
};

因为这是避免我们刚测试过的问题的一个直截了当的方法,我几乎可以把本条款的题目改为“在判断式类中使operator()成为const”。但那走得不够远。甚至const成员函数可以访问multable数据成员、非const局部静态对象、非const类静态对象、名字空间域的非const对象和非const全局对象。一个设计良好的判断式类也保证它的operator()函数独立于任何那类对象。在判断式类中把operator()声明为const对于正确的行为来说是必要的,但不够充分。一个行为良好的operator()当然是const,但不只如此。它也得是一个纯函数。

本条款的前面,我强调了任何STL想要一个判断式的地方,它都会接受一个真的判断式或一个判断式类对象。它在两个方向上都是对的。在STL任何可以接受一个判断式类对象的地方,一个判断式函数(可能由ptr_fun改变——参见条款41)也是受欢迎的。你现在明白判断式类中的operator()函数应该是纯函数,所以这个限制也扩展到判断式函数。作为一个判断式,这个函数和从BadPredicate类产生的对象一样糟:

bool anotherBadPredicate(const Widget&, const Widget&)
{
 static int timesCalled = 0;   // 不!不!不!不!不!不!不!
 return ++timesCalled == 3;   // 判断式应该是纯函数,
}      // 纯函数没有状态

不管你怎么写你的判断式,它们都应该是纯函数。

【绝版C++书籍】《Effective STL》读书笔记 《Effective STL》读书笔记写在前面《Effective STL》中可能过时的内容1 容器第1条:慎重选择容器类型。第2条:不要试图编写独立于容器类型的代码。第3条:确保容器中的对象拷贝正确而高效。第4条:调用empty而不是检查size()是否为0。第5条:区间成员函数优先于与之对应的单元素成员函数。第6条:当心C++编译器最烦人的分析机制。第7条:如果入容器中包含了通过new操作创建的指针,切记在容器对象析构前将delete掉。第8条:切勿创建包含auto_ptr的容器对象。第9条:慎重选择删 阅读详情

相关推荐

网工实验笔记:SNMP的配置

简单网络管理协议SNMP(Simple Network Management Protocol)是广泛应用于TCP/IP网络的网络管理标准协议。SNMP提供了一种通过运行网络管理软件的中心计算机(即网络管理工作站)来管理设备的方法。

2301_76170756的博客 2114

条款39、明智审慎地使用private继承

TK32论证了public继承是is-a关系。类student继承自类Person。于是在编译必要时student暗自转变为Person。现在考虑private继承: class Person{….}; class Student:private Person{….}; void eat(const Person& p); //任何人都会吃 void study(const Stude

lsfreeing的专栏 828

小程序选项卡以及swiper套用(跨页面)

选项卡tab和swpier之间的套用 其实我之前写过一篇选项卡的切换demo,大家阔以参考一下 小程序多个选项卡切换 那今天写这个demo呢,是因为项目需求,所以仅供参考。 我是拿到了home.wxml的数组下标, 通过url传参的方式去将这个id传到下一个页面, 下一个页面接收以后再将id赋值给对应的tabI或者currentId(swiper 的下标) 实现上一个页面进来以后直接进入对应的页...

Rechal_Mei的博客 3597

条款 39:明智而审慎地使用private继承

priority_queue

江南又一春 356

YOLOv8模型INT8量化实战:解决sigmoid输出归零的坑(附代码修改)

本文详细介绍了YOLOv8模型INT8量化过程中遇到的sigmoid输出归零问题及其解决方案。通过修改模型导出代码、优化ONNX量化流程,并提供完整的部署优化技巧,帮助开发者在边缘计算设备上实现高效的模型推理。文章还包含量化效果验证和常见问题排查方法,助力开发者快速解决实际部署中的难题。

vvv99的博客 252

Kindle电子书DRM破解全攻略:从下载到转换的完整流程(含最新DeDRM插件配置)

本文详细介绍了如何合法移除个人购买的Kindle电子书的DRM保护,实现跨设备阅读与备份。核心内容包括使用最新DeDRM插件(如noDRM分支)配合Calibre进行配置,并针对亚马逊最新的KFX加密格式提供了密钥提取与实战操作指南。文章强调该方法仅适用于个人合理使用,旨在帮助读者真正掌控自己的数字资产。

delta的博客 575

条款39:明智而审慎地使用private继承

考虑如下代码: class Person {}; class Student : private Person {}; void eat(const Person& p); // 任何人都会吃 void study(const Student& s); // 只有学生在校学习 Person p; // p是人 Student s; // s是学生 eat(p); // 没问题 eat(s); // 错误!难道学生不是人? 上述eat(s)错误的原因有两点: 如果classes之

L_H_L的博客 319

锐捷-GZ073 网络系统管理赛项赛题第1套A模块-S5配置

锐捷-GZ073 网络系统管理赛项赛题第1套A模块-S5配置

weixin_46183247的博客 767

Effective STL学习笔记-条款8-9

条款8 永不建立auto_ptr的容器 条款9 在删除选项中仔细选择永不建立auto_ptr的容器为什么不能建立auto_ptr的容器呢,是因为这个智能指针复制会转移“使用者”具体详细内容就不介绍了,现在它已经被shared_ptr、weak_ptr、unique_ptr代替了。我们试一下使用shared_ptr可不可放在容器: vector<shared_ptr<int>> v;

柠檬柠檬C的博客 601

基于本机知识库 + 豆包(火山引擎)+ MCP的落地方案

最小 MVP:本地知识库 + Doubao Embedding + 简单检索。工具化:把检索/写入/系统功能通过 MCP 封装。Agent 编排:RAG 流程 + 自检 + 引用输出。这样你就可以快速落地一个基于豆包 + 本机知识库的 MCP Agent。

lpfasd123的博客 1095

深入YOLOv8内部核心:用RepNCSPELAN4深度重构主干网络,即插即用的高效涨点方案

本文提出了一种改进YOLOv8目标检测模型的方法,通过在YOLOv8中引入RepNCSPELAN模块来提高检测性能。该模块融合了RepVGG重参数化思想与CSP-ELAN结构,在保持推理速度的同时增强特征表达能力。实验结果表明,改进后的RepNCSPELAN-YOLOv8相比原模型在多项指标上均有提升:准确率从0.84提高到0.86,召回率从0.72提升至0.81,mAP50和mAP50-95也分别提高了0.06和0.04。文章详细介绍了模块设计原理和移植步骤,包括配置文件创建、代码修改等关键环节,为YOL

FJN110的博客 185

笔记: 条款39: 明智而审慎地使用private继承

这是我在阅读Effective c++中认为比较重要的部分,下面给出了我对这一节的理解,并写出对应的比较容易理解的代码。 如果类之间的继承关系是private,编译器不会自动将一个继承类对象转换为一个基类对象。 private继承意味着根据某物实现出,和复合的意义一样。 通过使用复合+public继承使得继承的类无法继承它并重新定义它的虚函数,模拟java中的final或c#中的seal...

lkq_primer的博客 364

微信小程序 - 引入并使用 Fly.js 请求库(超级详细的教程及运行示例)提供 Fly.js 源码源文件下载,贴心的配置示例及注释,优雅快速的发起 http 网络请求

fly.js请求库官方教程文档,微信小程序封装fly.js请求,在小程序/mpvue中使用flyio发起网络请求的方法,微信小程序中如何使用flyio封装网络请求,微信小程序中如何使用flyio封装网络请求,最好的教程,fly最好的文档教程,微信小程序使用fly接口请求框架,引入fly.js报错,微信小程序中如何使用flyio封装网络请求,微信小程序引入Fly.js请求库,如何在fly中配置请求/响应拦截器,如何在微信小程序中优雅地发送异步请求,请求/响应拦截器,微信小程序-引入并使用F

🏆 前端领域优质创作者 5608

Effective STL学习笔记-条款39

用纯函数做判断式 判断式是返回bool(或者其他可以隐式转化为bool的东西)。判断式在STL中广泛使用。标准关联容器的比较函数是判断式,判断式函数常常作为参数传递给算法,比如find_if和多种排序算法。 纯函数是返回值只依赖于参数的函数。如果f是一个纯函数,x和y是对象,f(x, y)的返回值仅当x或y的值改变的时候才会改变。 一个判断式类是一个仿函数类,它的operator()函数是一个判断式

柠檬柠檬C的博客 292

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Effective STL 中文版(大全)

候捷说,对于STL,程序员有三个境界,开始是使用STL,然后是理解STL,最后是补充STL。EffectiveSTL是一本非常好的书,帮助你更好的理解STL,其作者就是《EffectiveC++》一书的作者。如果你已经初步了解了STL的容器、迭代器、算法和函数,而又想更好的了解STL,那么《EffectiveSTL》是你的最佳选择。 还有一部分没有找到链接,如果再找不到我会自己试着翻译

幸福村的人 1139

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

2 Studying《Effective STL》

《Effective STL》摘要 STL是C++标准库的重要组成部分,涵盖容器、迭代器、算法和函数对象等。本文总结了STL使用中的关键要点: 容器选择: 根据插入删除位置选择序列容器(vector、list、deque) 考虑查找效率选择关联容器(set、map)或哈希容器 指针容器需注意资源管理,建议使用智能指针 容器操作优化: 优先使用empty()而非size()==0 区间操作比单元素操作更高效 使用reserve避免频繁重新分配内存 注意string实现的多样性 算法使用: 确保目标区间足够大

u012906122的专栏 753

Camera2Demo

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

Effective STL 目录

由于没有看内容,有些翻译可能不准确,请见谅:)容器条款1: 仔细选择你要的容器 条款2: 小心对“容器无关代码”的幻想 条款3: 使容器里对象的拷贝操作轻量而正确 条款4: 用empty来代替检查size是否为0 条款5: 尽量使用范围成员函数代替他们的单元素兄弟 条款6: 警惕C++的及其令人恼怒的分析 条款7: 当使用new得指针的容器时,切记在容器销毁前d

AMin2001的专栏 1483

Effective STL目录

    从去年12月开始,我花了40天看了《Effective STL》这本书,并写了相关的读书笔记,我在考虑是否将这部分笔记在BLOG上贴出来。在此之前,先贴上Effective STL目录,并且强烈推荐这本书。如果想加深对STL的了解,这本书必不可少。同时还需要参考的书是《STL源代码剖析》。这篇目录上带有了相关链接。前言致谢导读容器条款1:仔细选择你的容器条款2:小心对“容器无关代码”的幻想

李永亮的专栏 1861

Effective C++ 条款总结

Effective C++ 条款总结一、让自己习惯C++1 视C++为一个语言联邦2 尽量使用const等替换#define3 尽可能使用const4 确定对象使用前被初始化 自己在看这本书的时候,回去翻看目录的时候,有些规则会被遗忘,因此做个简单的小总结供自己和其他人参考,没读过的还是要先去读一遍的 一、让自己习惯C++ 1 视C++为一个语言联邦 C++是一种包含许多特性的语言,因而不要把它视为一个单一语言。理解C++至少需要学习一下4个部分: C语言。C++仍以C为基础 objected-ori

catkin_ws的博客 1177

【C++】万字详解Effective C++

本文为前段时间读《Effective C++》(侯捷译)时做的个人笔记。

Friday 4289

《Effective C++》:条款38-条款39

条款38通过复合塑模树has-a 或根据某物实现出 条款39明智而审慎的使用private继承

KangRoger的专栏 2586

【一文读懂】《Effective STL》50条有效使用STL的经验

本文按 Scott Meyers《Effective STL》(2001)原书 8 章顺序,系统梳理全部 50 条条款,覆盖容器选型、vector/string、关联容器、迭代器、算法、函数子、STL 实战与其他议题。对初学者易混淆处配代码示例,并在关键处标注 C++11/20 的现代等价写法(如 unordered_map、std::erase_if、lambda 取代 bind1st)。

码工许师傅 366

Effective STL笔记

#estl 第50条:熟悉与STL相关的web站点。三个:www.sgi.com/tech/stl、www.stlport.org 和 www.boost.org。 #estl 第49条:学会分析与STL相关的编译器诊断信息。嗯,第一招是替换大法,然后介绍了一下与容器、插入迭代器、绑定器、输出迭代器或算法相关的错误大概有什么套路看。 #estl 第48条:总是包含(#include)正确 的头文件...

weixin_34015566的博客 164
上一篇: Effective STL 条款38
下一篇: Effective STL 条款36
AMin2001
博客等级 码龄26年 7粉丝 35原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值