6、仿函数、仿函数类、函数等

函数和类似函数的对象——仿函数——遍布STL。关联容器使用它们来使元素保持有 序;find_if使用它们来控制它们的行为;如果缺少它们,那么比如for_each和transform这样的组件就没有意义了;此外,not1和 bind2nd这样的适配器会积极地产生它们。

条款38:把仿函数类设计为用于值传递

STL函数对象在函数指针之后成型,所以STL中的习惯是当传给函数和从函数返回时函数对象也是值传递的(也就是拷贝)。最好的证据是标准的for_each声明,这个算法通过值传递获取和返回函数对象:

1
2
3
4
5
6
7
8
9
template
Function // 注意值返回
for_each(InputIterator first,
InputIterator last,
Function f); // 注意值传递

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

先介绍两个概念–“纯函数”与“判别式”:

纯函数是返回值只依赖于参数的函数。如果f是一个纯函数,x和y是对象,f(x, y)的返回值仅当x或y的值改变的时候才会改变。

判断式是返回bool(或者其他可以隐式转化为bool的东西)。判断式在STL中广泛使用。标准关联容器的比较函数是判断式,判断式函数常常作为参数传递给算法,比如find_if和多种排序算法。

判断式类是仿函数类,它的operator()函数是一个判断式,也就是,它的operator()返回true或false(或其他可以隐式转换到true或false的东西)。

重要结论:判断式函数必须是纯函数

条款40:使仿函数类可适配

假设有一个Widget*指针的list和一个函数来决定这样的指针是否确定一个有趣的Widget:

1
2
3
list widgetPtrs;
bool isInteresting(const Widget *pw);

如果要在list中找第一个指向有趣的Widget的指针,可以这样做:

1
2
3
4
5
6
7
8
9
list::iterator i = find_if(widgetPtrs.begin(), widgetPtrs.end(),
isInteresting);
if (i != widgetPtrs.end()) {
... // 处理第一个有趣的指向Widget的指针
}

但如果要找第一个指向不有趣的Widget的指针,显而易见的方法却编译失败:

1
2
3
4
5
list::iterator i =
find_if(widgetPtrs.begin(), widgetPtrs.end(),
not1(isInteresting)); // 错误!不能编译

取而代之的是,必须对isInteresting应用ptr_fun在应用not1之前:

1
2
3
4
5
6
7
8
9
10
11
list::iterator i =
find_if(widgetPtrs.begin(), widgetPtrs.end(),
not1(ptr_func(isInteresting))); // 没问题
if (i != widgetPtrs.end()) {
... // 处理第一个指向Widget的指针
}

解 释:ptr_fun做的唯一的事是使一些typedef有效,而四个标准函数适配器(not1、not2、bind1st和bind2nd)都需要这些 typedef,一些其他非标准STL兼容的适配器(比如来自SGI和Boost)也需要。提供这些必要的typedef的函数对象称为可适配的,而缺乏 那些typedef的函数对象不可适配。可适配的比不可适配的函数对象可以用于更多的场景,所以只要能做到你就应该使你的函数对象可适配。

条款41:了解使用ptr_fun、mem_fun和mem_fun_ref的原因

假设有一个可以测试Widget的函数,

1
void test(Widget& w); // 测试w,如果没通就标记为“failed”

此外,有一个Widget的容器:

1
vector vw; // vw容纳Widget

要测试vw中的每个Widget,显然可以这么使用for_each:

1
for_each(vw.begin(), vw.end(), test); // 调用#1(可以编译)

但如果test是Widget的成员函数而不是非成员函数,也就是说,Widget支持自我测试:

1
2
3
4
5
6
7
class Widget {
public:
void test(); // 进行自我测试;如果没通过就把*this标记为“failed”
};

如果使用for_each对vw中的每个对象调用Widget::test:

1
for_each(vw.begin(), vw.end(),&Widget::test); // 调用#2(不能编译)

解释:STL里的一个普遍习惯:函数和函数对象总使用非成员函数的语法形式调用,而不直接支持成员函数的调用

为了解决这个问题,mem_fun和mem_fun_ref便提出来了:

1
2
3
4
5
list lpw; // 同上
...
for_each(lpw.begin(), lpw.end(),mem_fun(&Widget::test)); // 这个现在可以编译了

条款42:确定less<T>表示operator<

7、 使用STL编程

条款43:尽量用算法调用代替手写循环

本条款将证明调用算法通常比手写的循环更优越,有三个理由:

● 效率:算法通常比程序员产生的循环更高效。

● 正确性:写循环时比调用算法更容易产生错误。

● 可维护性:算法通常使代码比相应的显式循环更干净、更直观。

如:有一个支持重画的Widget类:

1
2
3
4
5
6
7
8
9
10
11
class Widget {
public:
...
void redraw() const;
...
};

要重画一个list中的所有Widget对象,可以使用这样一个循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
list lw;
...
for (list::iterator i =
lw.begin();
i != lw.end(); ++i) {
i->redraw();
}

也可以用for_each算法来完成:

1
for_each(lw.begin(), lw.end(),mem_fun_ref(&Widget::redraw));

而第二种更优,提倡使用。

条款44:尽量用成员函数代替同名的算法

有 些容器拥有和STL算法同名的成员函数。关联容器提供了count、find、lower_bound、upper_bound和 equal_range,而list提供了remove、remove_if、unique、sort、merge和reverse。大多数情况下,应该 用成员函数代替算法。这样做有两个理由:首先,成员函数更快;其次,比起算法来,它们与容器结合得更好(尤其是关联容器)。

条款45:注意count、find、binary_search、lower_bound、upper_bound和equal_range的区别

你有一个容器或者你有一个由迭代器划分出来的区间——你要找的东西就在里面。你要

怎么完成搜索呢?可用的工具有:count、count_if、find、find_if、binary_search、lower_bound、

upper_bound和equal_range。面对着它们,怎么做出选择?下面是一个总结:

条款46:考虑使用函数对象代替函数作算法的参数

把STL函数对象——化装成函数的对象传递给算法所产生的代码一般比传递真的函数高效,同时,代码会更容易移植。

条款47:避免产生只写代码

“只写代码”指的是容易写,但很难读和理解的代码。举例:

假设有一个vector<int>,要去掉vector中值小于x而出现在至少和y一样大的最后一个元素之后的所有元素。下面代码立刻出现在你脑中吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
vector v;
int x, y;
...
v.erase(
remove_if(find_if(v.rbegin(), v.rend(),
bind2nd(greater_equal(), y)).base(),
v.end(),
bind2nd(less(), x)),
v.end());

这段代码是正确的,但极难理解和维护,应避免这样的代码。

条款48:总是#include适当的头文件

● 几乎所有的容器都在同名的头文件里,比如,vector在<vector>中声明,list在<list>中声明等。例外的 是<set>和<map>。<set>声明了set和multiset,<map>声明了map和 multimap。

● 除了四个算法外,所有的算法都在<algorithm>中声明。例外的是accumulate(参见条款37)、 inner_product、adjacent_difference和partial_sum。这些算法在<numeric>中声明。

● 特殊的迭代器,包括istream_iterators和istreambuf_iterators(参见条款29),在<iterator>中声明。

● 标准仿函数(比如less<T>)和仿函数适配器(比如not1、bind2nd)在<functional>中声明。

条款49:学习破解有关STL的编译器诊断信息

条款50:让你自己熟悉有关STL的网站

本条款推荐了几个学习STL的网站,分别为:

● SGI STL网站,http://www.sgi.com/tech/stl/。

● STLport网站,http://www.stlport.org/。

● Boost网站,http://www.boost.org/。(译注:如果访问不了,可以试试http://boost.sourceforge.net/)

原创文章,转载请注明: 转载自董的博客

本文链接地址: http://dongxicheng.org/cpp/effective-stl-part4/

《Effective STL》学习笔记(第四部分)相关推荐

  1. Effective C++ 学习笔记 第七章:模板与泛型编程

    第一章见 Effective C++ 学习笔记 第一章:让自己习惯 C++ 第二章见 Effective C++ 学习笔记 第二章:构造.析构.赋值运算 第三章见 Effective C++ 学习笔记 ...

  2. Effective Java(第三版) 学习笔记 - 第四章 类和接口 Rule20~Rule25

    Effective Java(第三版) 学习笔记 - 第四章 类和接口 Rule20~Rule25 目录 Rule20 接口优于抽象类 Rule21 为后代设计接口 Rule22 接口只用于定义类型 ...

  3. OpenCV学习笔记(四十一)——再看基础数据结构core OpenCV学习笔记(四十二)——Mat数据操作之普通青年、文艺青年、暴力青年 OpenCV学习笔记(四十三)——存取像素值操作汇总co

    OpenCV学习笔记(四十一)--再看基础数据结构core 记得我在OpenCV学习笔记(四)--新版本的数据结构core里面讲过新版本的数据结构了,可是我再看这部分的时候,我发现我当时实在是看得太马 ...

  4. C++ STL学习笔记

    C++ STL学习笔记一 为何要学习STL: 数据结构与算法是编程的核心,STL中包含各种数据结构和优秀的算法,确实值得深入学习,本文中虽然着重使用,但希望有心的朋友能多看看相关数据结构的实现,对于C ...

  5. Effective STL 读书笔记

    Effective STL 读书笔记 标签(空格分隔): 未分类 慎重选择容器类型 标准STL序列容器: vector.string.deque和list(双向列表). 标准STL管理容器: set. ...

  6. ASP.NET Core 2 学习笔记(四)依赖注入

    原文:ASP.NET Core 2 学习笔记(四)依赖注入 ASP.NET Core使用了大量的依赖注入(Dependency Injection, DI),把控制反转(Inversion Of Co ...

  7. Spring Cloud 学习笔记(四)-Spring Cloud Hystrix

    Spring Cloud 学习笔记(四)-Spring Cloud Hystrix 由于前一阵子项目的原因,今天才继续弄上,今天想学习一下Hystrix组件 这个组件还挺抽象的,最开始我一直没太明白, ...

  8. OpenCV学习笔记(四十六)——FAST特征点检测features2D OpenCV学习笔记(四十七)——VideoWriter生成视频流highgui OpenCV学习笔记(四十八)——PCA算

    OpenCV学习笔记(四十六)--FAST特征点检测features2D 特征点检测和匹配是计算机视觉中一个很有用的技术.在物体检测,视觉跟踪,三维常年关键等领域都有很广泛的应用.这一次先介绍特征点检 ...

  9. Windows x64内核学习笔记(四)—— 9-9-9-9-12分页

    Windows x64内核学习笔记(四)-- 9-9-9-9-12分页 前言 9-9-9-9-12分页 实验一:线性地址转物理地址 页表基址 定位基址 PTE to PXE 实验二:通过页表基址定位各 ...

  10. Intel VT学习笔记(四)—— VMCS(下)

    Intel VT学习笔记(四)-- VMCS(下) 要点回顾 VM-Exit Information Guest state fields 代码实现 参考资料 要点回顾 在上一篇中,我们了解了如何设置 ...

最新文章

  1. 华人博士拿下ACM SIGSOFT杰出博士论文奖,师从北大谢涛教授
  2. c语言填空三个数找中间大小,2013计算机等级考试二级C语言填空题.doc
  3. katalon进行app测试_Katalon API 测试 Demo
  4. 玩转Python? 一文总结30种Python的窍门和技巧,不可错过哈!
  5. python解析原理_python爬虫原理
  6. 如何自动生成和安装requirements.txt依赖
  7. HDU 2531 (BFS搜索)
  8. maven工程打包老是报错_Maven 项目打包及启动时的报错解决
  9. 热敏标签打印机打印不清晰如何解决
  10. 简单的外网映射工具natapp操作
  11. 基于python的电影在线_利用python实现电影推荐
  12. grok java_Java Grok.match方法代码示例
  13. Linux C语言 pthread_cond_wait()、pthread_cond_timedwait()函数(不允许cond被唤醒时产生竞争,所以需要和互斥锁搭配)
  14. percona xtrabackup 之一 setup
  15. 《快速掌握PyQt5》第二章 信号与槽——裁判鸣枪与选手开跑
  16. LiveRTMP推送RTSP视频源进行RTMP直播
  17. 苹果手机显示无法与服务器建立安全连接,苹果手机那个safari浏览器无法与访问器建立安全链接是什么意思...
  18. 基于RFID定位技术的室内定位原理--RFID室内定位--新导智能
  19. 两物体的相对速度公式_为什么以光速相向飞行的两物体的相对速度不是2C?相对论还适用?...
  20. python递归函数特点_Python递归函数特点及原理解析

热门文章

  1. 支付产品必懂的会计基础及如何应用
  2. 诚安聚立总裁刘志军:对标三大征信局与FICO 坚持风控能力输出
  3. 华为电话面试题java_华为java面试题(含电话面试)
  4. SpringBoot - 统一格式封装及高阶全局异常处理
  5. Spring Cloud【Finchley】-06服务消费者整合Feign
  6. java 过滤掉相同请求_java并发访问重复请求过滤问题
  7. 基于Netty+Zookeeper实现Dubbo
  8. 递归 累加和累乘
  9. 编辑距离:最长公共子序列-LCS问题
  10. php 请求转发 重定向,PHP怎么实现页面重定向?(图文+视频)