Effective STL

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qcpwxQhi-1585901574462)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585896080697.png)]

条款21: 永远让比较函数对相等的值返回false

set<int, less_equal > s; // s以“<=”排序
s.insert(10); // 插入10
现在尝试再插入一次10:
s.insert(10);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-79gWak6T-1585901574463)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585896126672.png)]

结果当然是false。 也就是说,set得出的结论是10A与10B不等价,因此不一样,于是它将10B插入容器中10A的
旁边。在技术上而言,这个做法导致未定义的行为,但是通常的结果是set以拥有了两个为10的值的拷贝而告
终,也就是说它不再是一个set了。通过使用less_equal作为我们的比较类型,我们破坏了容器!此外,任何对
相等的值返回true的比较函数都会做同样的事情。是不是很酷?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xbUVhZGn-1585901574465)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585896146821.png)]

相等的值绝不该一个大于另一个,所以比较函数总应该对相等的值返回false。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HzTaDNOG-1585901574466)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585896189387.png)]

条款22:避免原地修改set和multiset的键

正如所有标准关联容器,set和multiset保持它们的元素有序,这些容器的正确行为
依赖于它们保持有序。 如果你改了关联容器里的一个元素的值(例如,把10变为1000),新值可能不在正确
的位置,而且那将破坏容器的有序性。很简单,是吗?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5CgYQWcT-1585901574468)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585896440094.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j78woIxX-1585901574470)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585896457880.png)]

只是改变雇员的一个与set排序的方式无关的方面(一个雇员的非键部分),所以这段代码不
会破坏set。

本条款的目的是提醒你如果你改变set或
multiset里的元素, 你必须确保不改变一个键部分——影响容器有序性的元素部分。如果你做了,你会破坏容
器,再使用那个容器将产生未定义的结果, 而且那是你的错误。另一方面,这个限制只应用于被包含对象的
键部分。对被包含元素的所有其他部分来说,是开放的:随便改变!

也就是怎样做才能既正确又可移植。它不难,但是它用到了太多程序员忽略的一个细节:你必须映
射到一个引用。

它们错的原因也相同。在运行期,它们不能修改i!在这两个情
况里,映射的结果是一个
i副本的临时匿名对象,而setTitle是在匿名的物体上调用,不在*i上!*i没被修改,
因为setTitle从未在那个对象上调用,它在那个对象的副本上调用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1llSmc3K-1585901574471)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585896576986.png)]

条款23:考虑用有序vector代替关联容器

当需要一个提供快速查找的数据结构时,很多STL程序员立刻会想到标准关联容器:set、multiset、map和
multimap。直到现在这很好,但不是永远都好。如果查找速度真得很重要,的确也值得考虑使用非标准的散
列容器(参见条款25)。如果使用了合适的散列函数,则可以认为散列容器提供了常数时间的查找。(如果
选择了不好的散列函数或表的太小,散列表的查找性能可能明显下降,但在实际中这相对少见。)对于多数
应用,被认为是常数时间查找的散列容器要好于保证了对数时间查找的set、map和它们的multi同事。
即使你需要的就只是对数时间查找的保证,标准关联容器仍然可能不是你的最佳选择。和直觉相反,对于标
准关联容器,所提供的性能也经常劣于本该比较次的vector。如果你要有效使用STL,你需要明白什么时候和
怎么让一个vector可以提供比标准关联容器更快的查找。
标准关联容器的典型实现是平衡二叉查找树。一个平衡二叉查找树是一个对插入、删除和查找的混合操作优
化的数据结构。换句话说,它被设计为应用于进行一些插入,然后一些查找,然后可能再进行一些插入,然
后也许一些删除,然后再来一些查找,然后更多的插入或删除,然后更多的查找等。这个事件序列的关键特
征是插入、删除和查找都是混合在一起的。一般来说,没有办法预测对树的下一个操作是什么。
在很多应用中,使用数据结构并没有那么混乱。它们对数据结构的使用可以总结为这样的三个截然不同的阶
段:

  1. 建立。通过插入很多元素建立一个新的数据结构。在这个阶段,几乎所有的操作都是插入和删除。几
    乎没有或根本没有查找。
  2. 查找。在数据结构中查找指定的信息片。在这个阶段,几乎所有的操作都是查找。几乎没有或根本没
    有插入和删除。
  3. 重组。修改数据结构的内容,也许通过删除所有现有数据和在原地插入新数据。从动作上说,这个阶
    段等价于阶段1。一旦这个阶段完成,应用程序返回阶段2。
    对于这么使用它们的数据结构的应用来说,一个vector可能比一个关联容器能提供更高的性能(时间和空间
    上都是)。但不是任意的vector都会,只有有序vector。因为只有有序容器才能正确地使用查找算法——
    binary_search、lower_bound、equal_range等

考虑一个Widget的关联容器和一个有序vector。如果我们选择一个关联容器,我们几乎确定了要使
用平衡二叉树。这样的树是由树节点组成,每个都不仅容纳了一个Widget,而且保存了一个该节点到左孩子
的指针,一个到它右孩子的指针,和(典型的)一个到它父节点的指针。这意味着在关联容器中用于存储一
个Widget的空间开销至少会是三个指针。
与之相对的是,当在vector中存储Widget并没有开销:我们简单地存储一个Widget。当然,vector本身有开
销,在vector结尾也可能有空的(保留)空间(参见条款14),但是每个vector开销是可以忽略的(通常是三
个机器字,比如,三个指针或两个指针和一个int),而且如果必要的话,末尾空的空间可以通过“交换技
巧”去掉(看见条款17)。即使这个附加的空间没有去掉,也并不影响下面的分析,因为当查找时不会引用
那段内存

引用局部性,这些节点会分散在所有你的内存空间。那会导
致更多的页面错误。即使使用了自定义群集内存管理器,关联容器也会导致很多页面错误,因为,不像连续
内存容器,比如vector,基于节点的容器更难保证在容器的遍历顺序中一个挨着一个的元素在物理内存上也
是一个挨着一个。

概要:在有序vector中存储数据很有可能比在标准关联容器中保存相同的数据消耗更少的内存;当页面错误
值得重视的时候,在有序vector中通过二分法查找可能比在一个标准关联容器中查找更快

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KUXfHUMq-1585901574472)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585896705214.png)]

条款24:当关乎效率时应该在map::operator[]和map-insert之间仔细选择

map的operator[]函数是个奇怪的东西。它与vector、deque和string的operator[]函数无关,也和内建的数组
operator[]无关。相反,map::operator[]被设计为简化“添加或更新”功能。即,给定

map<K, V> m;
这个表达式

m[k] = v;
检查键k是否已经在map里。如果不,就添加上,以v作为它的对应值。如果k已经在map里,它的关联值被更
新成v。
这项工作的原理是operator[]返回一个与k关联的值对象的引用。然后v赋值给所引用(从operator[]返回的)的
对象。当要更新一个已存在的键的关联值时很直接,因为已经有operator[]可以用来返回引用的值对象。但是
如果k还不在map里,operator[]就没有可以引用的值对象。那样的话,它使用值类型的默认构造函数从头开始
建立一个,然后operator[]返回这个新建立对象的引用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SkYzeGaI-1585901574473)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585896871588.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gyr0bkUf-1585901574474)(C:\Users\w00448203\Documents\MarkDownDoc\1585896953033.png)]

也就是当关乎效率时应该在
map::operator[]和map-insert之间仔细选择。如果你要更新已存在的map元素,operator[]更好,但如果你要增
加一个新元素,insert则有优势。

条款25:熟悉非标准散列容器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NGZ8qJQy-1585901574475)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585897009904.png)]

迭代器

条款26:尽量用iterator代替const_iterator,reverse_iterator和const_reverse_iterator

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NTwdXyMf-1585901574477)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585897058034.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J96enOQu-1585901574478)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585897082539.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-msSnugGb-1585901574479)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585897096539.png)]

当在iterator和const_iterator之间作选择的时候,你有更充分的理由选择iterator,即使const_iterator同样可行而
且即使你并不需要调用容器类的任何成员函数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9c7esF7o-1585901574479)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585897436212.png)]

条款27:用distance和advance把const_iterator转化成iterator

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4xNbmeVq-1585901574480)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585897469476.png)]

typedef。在这种实现的情况下,用const_cast把const_iterator映射成iterator当然可以编译而且没有问题,因为
const_iterator与iterator之间的const_cast映射被最终解释成const T到T的映射。但是,即使是在这种实现中,
reverse_iterator和const_reverse_iterator也是真正的类,所以你仍然不能直接用const_cast把const_reverse_iterator映
射成reverse_iterator。

这种方法看上去非常简单,直截了当,也很让人吃惊吧。要得到与const_iterator指向同一位置的iterator,首先
将iterator指向容器的起始位置,然后把它向前移到和const_iterator距离容器起始位置的偏移量一样的位置即
可!这个任务得到了两个函数模板advance和distance的帮助,它们都在中声明。distance返回两个指
向同一个容器的iterator之间的距离;advance则用于将一个iterator移动指定的距离。如

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5p2mtn3t-1585901574480)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585897499860.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cPeSCrKa-1585901574481)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585897529352.png)]

条款28:了解如何通过reverse_iterator的base得到iterator

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8i2bPTH1-1585901574481)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585897567655.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u3rlByFG-1585901574482)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585897580686.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q1hCNe7M-1585901574482)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585897609181.png)]

条款29:需要一个一个字符输入时考虑使用istreambuf_iterator

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hI1a8jt8-1585901574482)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585897661947.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lKdCP2kh-1585901574482)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585897670893.png)]

算法

条款30:确保目标区间足够大

条款31:了解你的排序选择

条款32:如果你真的想删除东西的话就在类似remove的算法后接上erase

因为remove是STL中最糊涂的算法。误解remove很容易,

这是remove的声明:
template<class ForwardIterator, class T>
ForwardIterator remove(ForwardIterator first, ForwardIterator last,
const T& value);
就像所有算法,remove接收指定它操作的元素区间的一对迭代器。它不接收一个容器,所以remove不知道它
作用于哪个容器。此外,remove也不可能发现容器,因为没有办法从一个迭代器获取对应于它的容器。

想想怎么从容器中除去一个元素。唯一的方法是调用那个容器的一个成员函数,几乎都是erase的某个形式,
(list有几个除去元素的成员函数不叫erase,但它们仍然是成员函数。)因为唯一从容器中除去一个元素的方
法是在那个容器上调用一个成员函数,而且因为remove无法知道它正在操作的容器,所以remove不可能从一
个容器中除去元素。这解释了另一个令人沮丧的观点——从一个容器中remove元素不会改变容器中元素的个
数: 这解释了另一个令人沮丧的观点——从一个容器中remove元素不会改变容器中元素的个
数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q53r14Bz-1585901574483)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585897927668.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n4GRjBt3-1585901574484)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585897958037.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pzucdAqH-1585901574485)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585897981585.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CUDDFau0-1585901574486)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585897993652.png)]

你可以想象remove完成了一种压缩,被删除的值表演了在压缩中被填充的洞的角色。对于我们的vector v,它
按照下面的表演:

  1. remove检测v[0],发现它的值不是要被删除的,然后移动到v[1]。同样的情况发生在v[1]和v[2]。
  2. 发现v[3]应该被删除,所以它记录下v[3]的值应该被覆盖,然后它移动到v[4]。这类似记录v[3]是一个

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-msref7Ck-1585901574486)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585898059243.png)]

道remove不从容器中除去任何元素因为它做不到就够了。只有容器成员函数可以除去容器元素,而那是本条
款的整个要点:如果你真的要删除东西的话,你应该在remove后面接上erase。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bp8sOu6u-1585901574488)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585898080401.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qZTTAyAt-1585901574489)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585898091700.png)]

条款33:提防在指针的容器上使用类似remove的算法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6nWOBBlc-1585901574490)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585898144961.png)]

资源泄漏的理由现在很明朗了。指向Widget B和C的“删除的”指针被vector中后面的“不删除的”指针覆
盖。没有什么指向两个未通过检验的Widget,它们也没有被删除,它们的内存和其他资源泄漏了。
一旦remove_if和erase返回后,情况看起来像这样:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QBB6A4Nb-1585901574490)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585898165284.png)]

条款34:注意哪个算法需要有序区间

不是所有算法可以用于任意区间。比如,remove(参见条款32和33)需要前向迭代器和可以通过这些迭代器
赋值的能力。所以,它不能应用于由输入迭代器划分的区间, 很多排序算法(参见条款31)需要随机访问迭代器,所以不可能
在一个list的元素上调用这些算法。

既可以和有序又可以和无序区间合作的算法很少,但当操作有序区间的时候它们最有用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yuzT6b0a-1585901574491)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585898284992.png)]

搜索算法binary_search、lower_bound、upper_bound和equal_range(参见条款45)需要有序区间,因为它们使 用二分法查找来搜索值。像C库中的bsearch,这些算法保证了对数时间的查找,但作为交换的是,你必须给
它们已经排过序的值。
实际上,这些算法保证对数时间查找不是很正确。仅当传给它们的是随机访问迭代器时它们才能保证有那样
的性能。如果给它们威力比较小的迭代器(比如双向迭代器),它们仍然进行对数次比较,但运行是线性时
间的。那是因为,缺乏进行“迭代器算术(arithmetic)”的能力。它们在搜索的区间中需要花费线性时间来
从一个地方移动到另一个地方。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hCeI5KjP-1585901574492)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585898321267.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nxmxXnQu-1585901574492)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900036117.png)]

条款35:通过mismatch或lexicographical比较实现简单的忽略大小写字符串比较

条款36:了解copy_if的正确实现

条款37:用accumulate或for_each来统计区间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IG5NcB90-1585901574493)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900100164.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4hvG86Hu-1585901574498)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900153511.png)]

就个人来说,我更喜欢用accumulate来统计,因为我认为它最清楚地表达了正在做什么,但是for_each也可以,而且不像accumulate,副作用的问题并不跟随for_each。两个算法都能用来统计区间。使用最适合你的那个

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

在你看到的STL中的每个地方,你都可以看见仿函数和仿函数类。包括你的源代码中。如果不知道怎
么写行为良好的仿函数就不可能有效地使用STL。

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

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

假设函数对象总是值传递。实际上,这事实上总是真的。
因为函数对象以值传递和返回,你的任务就是确保当那么传递(也就是拷贝)时你的函数对象行为良好。这
暗示了两个东西。第一,你的函数对象应该很小。否则它们的拷贝会很昂贵。第二,你的函数对象必须单态
(也就是,非多态)——它们不能用虚函数。

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sI77uYRv-1585901574503)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900305357.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cxA8HUDO-1585901574506)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900318322.png)]

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SljA0wdI-1585901574508)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900359412.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gM3FJrOR-1585901574509)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900369715.png)]

在这里,传给binary_function的类型和operator()所带的类型一样。用于带有或返回指针的仿函数的一般规则是传给unary_function或binary_function的类型是operator()带有或返回的类型。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ftqCVJjl-1585901574509)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900414960.png)]

STL暗中假设每个
仿函数类只有一个operator()函数,而且这个函数的参数和返回类型要被传给unary_function或binary_function

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

mem_fun和mem_fun_ref完成这个的方式很简单,虽然如果你看一下这些函数之一的声明会稍微清楚些。它们是真的函数模板,而且存在mem_fun和mem_fun_ref模板的几个变体,

总的来说,mem_fun适配语法#3——也就是当和Widget*指针配合时Widget::test要求的——到语法1,也就是
for_each用的。因此也不奇怪像mem_fun_t这样的类被称为函数对象适配器。知道这个不应该使你惊讶,完全
类似上述的,mem_fun_ref函数适配语法#2到语法#1,并产生mem_fun_ref_t类型的适配器对象

for_each没有使用ptr_fun增加的typedef,所以当把test传给for_each时不必使用
ptr_fun。

如果你关于什么时候使用ptr_fun什么时候不使用而感到困惑,那就考虑每当你传递一个函数给STL组件时都
使用它。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pTnFo8H9-1585901574510)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900574492.png)]

条款42:确定less表示operator<

operator<不仅是实现less的默认方式,它还是程序员希望less做的。让less做除operator<以外的事情是对程序员
预期的无故破坏。它与所被称为“最小惊讶的原则”相反。

multiset<Widget, MaxSpeedCompare> widgets;
这条代码确切地说出了它的意思。它建立了一个Widget的multiset,

这个表示widgets是一个以默认方式排序的Widget的multiset。在技术上,那表示它使用了less,但是实
际上每人都要假设那真的意味着它是按operator<来排序。
不要通过把less的定义当儿戏来误导那些程序员。如果你使用less(明确或者隐含),保证它表示operator<。
如果你想要使用一些其他标准排序对象,建立一个特殊的不叫做less的仿函数类。它真的很简单。

使用STL编程

总结由容器、迭代器、算法和函数对象组成的STL是个惯例,

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

所以,算法内部是一个循环。此外,STL算法的广泛涉及面意味着很多你本来要用循环来实现的任务,现在
可以改用算法来实现了。比如,如果你有一个支持重画的Widget类:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1DpDXeen-1585901574511)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900653796.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-49sEiUrw-1585901574513)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900668349.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5M3PtfAN-1585901574517)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900678821.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9FbIF5dM-1585901574518)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900687692.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kntgcEcZ-1585901574523)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900705134.png)]

“bind2nd(plus(), 41)”可能会花上一些时间才能看明白(尤其是如果不常用STL的bind族的
话),但是与迭代器相关的唯一烦扰就是指出源区间的起始点和结束点(而这从不会成为问题),并确保在
目的区间的起始点上使用inserter(参见条款30)。实际上,为源区间和目的区间指出正确的初始迭代器通常
都很容易,至少比确保循环体没有于无意中将需要持续使用的迭代器变得无效要容易得多。
难以正确实现循环的情况太多了,这个例子只是比较有代表性。因为在使用迭代器前,必须时刻关注它们是
否被不正确地操纵或变得无效。

算法为什么可以比手写的循环更高效,也描述了为什么循环将艰难地穿行于与迭代器相关的荆
棘丛中,而算法正避免了这一点。

有理由认为专业的C++程序员知道(或应该去看一下)每
个算法都完成了什么。因此,当程序员调用transform时,他们认为对区间内的每个元素都施加了某个函数,
而结果将被写到另外一个地方。当程序员调用replace_if时,他(她)知道区间内满足判定条件的对象都将被
修改。当调用partition时,她(他)明白区间中所有满足判定条件的对象将被聚集在一起(参见条款31)。
STL算法的名字传达了大量的语义信息,这使得它们比随机的循环清晰多了

明摆着,算法的名字暗示了其功能。“for”、“while”和“do”却做不到这一点。

没道理用循环来实现出已存在的STL算法的等价版本。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sfZNvmrK-1585901574526)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900764341.png)]

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eq07UVJ8-1585901574527)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900789189.png)]

效率不是find成员函数和find算法间的唯一差别。正如条款19所解释的,STL算法判断两个对象是否相同的方
法是检查的是它们是否相等,而关联容器是用等价来测试它们的“同一性”。 因此,find算法搜索727用的是
相等,而find成员函数用的是等价。相等和等价间的区别可能造成成功搜索和不成功搜索的区别。比如说,
条款19演示了用find算法在关联容器搜索失败而用find成员函数却搜索成功的情况!因此,如果使用关联容器
的话,你应该尽量使用成员函数形式的find、count、lower_bound等等,而不是同名的算法,因为这些成员函
数版本提供了和其它成员函数一致的行为。由于相等和等价间的差别,算法不能提供这样的一致行为。

对于标准的关联容器,选择成员函数而不是同名的算法有几个好处。首先,你得到的是对数时间而不是线性
时间的性能。其次,你判断两个元素“相同”使用的是等价,这是关联容器的默认定义。第三,当操纵map
和multimap时,你可以自动地只处理key值而不是(key, value)对。这三点给了优先使用成员函数完美的铁甲。

牢牢记住这一点很重要:list成员函数的行为和它们的算法兄弟的行为经常不相同。正如条款32所解释的,如
果你真的想从容器中清除对象的话,调用remove、remove_if和unique算法后,必须紧接着调用erase函数;但
list的remove、remove_if和unique成员函数真的去掉了元素,后面不需要接着调用erase。
在sort算法和list的sort成员函数间的一个重要区别是前者不能用于list。作为单纯的双向迭代器,list的迭代器不
能传给sort算法。merge算法和list的merge成员函数之间也同样存在巨大差异。这个算法被限制为不能修改源
范围,但list::merge总是修改它的宿主list。
所以,你明白了吧。当面临着STL算法和同名的容器成员函数间进行选择时,你应该尽量使用成员函数。几
乎可以肯定它更高效,而且它看起来也和容器的惯常行为集成得更好。

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

有序区间。如果是,你就可以通过binary_search、
lower_bound、upper_bound和equal_range来加速(通常是对数时间——参见条款34)搜索。如果迭代器并没有
划分一个有序区间,你就只能用线性时间的算法count、count_if、find和find_if。

count回答的问题是:“是否存在这个值,如果有,那么存在几份拷贝?”而find回答的问题
是:“是否存在,如果有,那么它在哪儿?”

这里示范了一种惯用法:把count用来作为是否存在的检查。count返回零或者一个正数,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l8IYEccg-1585901574529)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900869143.png)]

find算法都用相等来搜索,而binary_search、lower_bound、upper_bound和equal_range则用等价

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fn9ISnGj-1585901574536)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900888737.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E3sCzSdH-1585901574541)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900908907.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aCxlzF0r-1585901574544)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900935564.png)]

第二个要注意的是equal_range返回的东西是两个迭代器,对它们作distance就等于区间中对象的数目,也就是,等价于要寻找的值的对象。结果,equal_range不光完成了搜索有序区间的任务,而且完成了计数。比

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Eq3kQ3ar-1585901574547)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900971861.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jbtiNcH4-1585901574549)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585900983935.png)]

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GRWSSJg2-1585901574553)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585901004329.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yzdog6wc-1585901574555)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585901015308.png)]

把函数对象作为算法的参数所带来的不仅是巨大的效率提升。在让你的代码可以编译方面,它们也更稳健。

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-meiRk52S-1585901574556)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585901279613.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y4EqLfXo-1585901574556)(C:\Users\w00448203\AppData\Roaming\Typora\typora-user-images\1585901292764.png)]

e就等于区间中对象的数目,也就是,等价于要寻找的值的对象。结果,equal_range不光完成了搜索有序区间的任务,而且完成了计数。比

[外链图片转存中…(img-Eq3kQ3ar-1585901574547)]

[外链图片转存中…(img-jbtiNcH4-1585901574549)]

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

[外链图片转存中…(img-GRWSSJg2-1585901574553)]

[外链图片转存中…(img-yzdog6wc-1585901574555)]

把函数对象作为算法的参数所带来的不仅是巨大的效率提升。在让你的代码可以编译方面,它们也更稳健。

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

[外链图片转存中…(img-meiRk52S-1585901574556)]

[外链图片转存中…(img-y4EqLfXo-1585901574556)]

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值