《Effective STL》学习笔记(第三部分)
2016-04-09 21:30
169 查看
4、 迭代器
标准STL容器提供了四种不同的迭代器:iterator、const_iterator、reverse_iterator和const_reverse_iterator
为什么有四种迭代器?它们之间的关系是什么?它们可以互相转化吗?在调用算法和STL实用函数时不同类型可以混合使用吗?这些类型是怎么关联到容器和它们的成员函数的?本章回答了这些问题,同时介绍了一个比通常更值得注意的迭代器类型:istreambuf_iterator。
条款26:尽量用iterator代替const_iterator,reverse_iterator和const_reverse_iterator
每个标准容器类都提供四种迭代器类型。对于container<T>而言,iterator的作用相当于T*,而const_iterator则相当于const T*;增加一个iterator或者const_iterator可以在一个从容器开头趋向尾部的遍历中让你移动到容器的下一个元素。reverse_iterator与const_reverse_iterator同样相当于对应的T*和const T*,所不同的是,增加reverse_iterator或者const_reverse_iterator会在从尾到头的遍历中让你移动到容器的下一个元素。
迭代器使用的一个重要指导方针是:尽量使用iterator代替其他三种迭代器,原因有:
(1) insert和erase的一些版本要求iterator。如果你需要调用这些函数,你就必须产生iterator,而不能用const或reverse iterators。
(2)不可能把const_iterator隐式转换成iterator。
条款27:用distance和advance把const_iterator转化成iterator
如果你只有一个const_iterator,而你要在它所指向的容器位置上插入新元素呢?也就是如何把const_iterator转化为iterator呢?前面提到:并不存在从const_iterator到iterator之间的隐式转换,也就是说下面操作是不可以的:
正确方法如下:
注:distance返回两个指向同一个容器的iterator之间的距离;advance则用于将一个iterator移动指定的距离。如果i和ci指向同一个容器,那么表达式advance(i, distance(i, ci))会将i移动到与ci相同的位置上。
条款28:了解如何通过reverse_iterator的base得到iterator
条款29:XXXXXXXXXXXXXXXXXXXX(不常见)
5、算法
条款30:确保目标区间足够大
“把transform的结果放入叫做results容器的结尾”的正确方法是调用back_inserter来产生指定目标区间起点的迭代器:
更高效的方法是:
条款31:了解你的排序选择
(1) 如果你需要在vector、string、deque或数组上进行完全排序,你可以使用sort或stable_sort。
(2) 如果你有一个vector、string、deque或数组,你只需要排序前n个元素,应该用partial_sort。
(3) 如果你有一个vector、string、deque或数组,你需要鉴别出第n个元素或你需要鉴别出最前的n个元素,而不用知道它们的顺序,nth_element是你应该注意和调用的。
(4) 如果你需要把标准序列容器的元素或数组分隔为满足和不满足某个标准,你大概就要找partition或stable_partition。
(5) 如果你的数据是在list中,你可以直接使用partition和stable_partition,你可以使用list的sort来代替sort和stable_sort。如果你需要partial_sort或nth_element提供的效果,你就必须间接完成这个任务,主要有三种方法:
[1] 把元素拷贝到一个支持随机访问迭代器的容器中,然后对它应用需要的算法。
[2] 另一个方法是建立一个list::iterator的容器,对那个容器使用算法,然后通过迭代器访问list元素。
[3]使用有序的迭代器容器的信息来迭代地把list的元素接合到你想让它们所处的位置
条款32:如果你真的想删除东西的话就在类似remove的算法后接上erase
先看一个错误的实例:
正确的删除元素的方法是:
需要注意的是remove和erase是亲密联盟,这两个整合到list成员函数remove中。这是STL中唯一名叫remove又能从容器中除去元素的函数:
条款33:提防在指针的容器上使用类似remove的算法
该条款是对条款32的补充,采用remove/erase方式删除指针容器中的数据会造成内存泄漏,如:
Widget C和Widget B的内存不会被释放,造成内存泄漏,正确的方法有两种:
(1)在应用erase-remove惯用法之前先删除指针并设置它们为空,然后除去容器中的所有空指针:
(2)采用智能指针,如boost库中的shared_ptr和scoped_ptr
条款34:注意哪个算法需要有序区间
STL中只能操作有序数据(升序)的算法有:
(1) binary_search:二分查找
(2) lower_bound:下界
(3) upper_bound:上街
(4) equal_range:所有等于某个值的元素
(5) set_union:集合并集
(6) set_intersection:集合交集
(7) set_difference :集合差集
(8) set_symmetric_difference:包含在第一个集合但是不包含在第二个集合中的元素,包含在第2个集合但是不包含在第1个集合中的元素
(9) merge:合并两个有序表
(10) inplace_merge:合并两个有序表
(11) includes:检测一个区间的所有对象是否在另一个区间中
另外,下面的算法一般用于有序区间,虽然它们不要求:
(12) unique:去重,相同的元素必须紧挨着,排序是个特例
(13) unique_copy:同上
条款35:通过mismatch或lexicographical比较实现简单的忽略大小写字符串比较
实现忽略大小写字符串比较有两种方法,一种是使用mismatch函数:
第二种时使用lexicographical函数:
条款36:了解copy_if的正确实现
STL有很多有趣的地方,其中一个是虽然有11个名字带“copy”的算法:
(1) copy
(2) copy_backward
(3) replace_copy
(4) reverse_copy
(5) replace_copy_if
(6) unique_copy
(7) remove_copy
(8) rotate_copy
(9) remove_copy_if
(10) partial_sort_copy
(11) unintialized_copy
但没有一个是copy_if,这需要自己实现,非常经典的一个实现是:
条款37:用accumulate或for_each来统计区间
本条款总结了用自定义的方式统计(summarize)区间的方法,主要有两种(在头文件<numeric>中):
(1) accumulate存在两种形式:第一种是: 带有一对迭代器和初始值的形式,它可以返回初始值加由迭代器划分出的区间中值的和:
注意初始值指定为0.0,0.0的类型是double,所以accumulate内部使用了一个double类型的变量来存储计算的和。如果这么写这个调用:
初始值是int 0,所以accumulate内部就会使用一个int来保存它计算的值,这是不正确的,因为每次加法后会将结果转换为一个int,这造成小数点后面的数值丢失。
另一种形式带有一个初始和值与一个任意的统计函数,实例:
(2) for_each,带有一个区间和一个函数(一般是一个函数对象)来调用区间中的每个元
素,但传给for_each的函数只接收一个实参(当前的区间元素),而且当完成时for_each返回它的函数。
标准STL容器提供了四种不同的迭代器:iterator、const_iterator、reverse_iterator和const_reverse_iterator
为什么有四种迭代器?它们之间的关系是什么?它们可以互相转化吗?在调用算法和STL实用函数时不同类型可以混合使用吗?这些类型是怎么关联到容器和它们的成员函数的?本章回答了这些问题,同时介绍了一个比通常更值得注意的迭代器类型:istreambuf_iterator。
条款26:尽量用iterator代替const_iterator,reverse_iterator和const_reverse_iterator
每个标准容器类都提供四种迭代器类型。对于container<T>而言,iterator的作用相当于T*,而const_iterator则相当于const T*;增加一个iterator或者const_iterator可以在一个从容器开头趋向尾部的遍历中让你移动到容器的下一个元素。reverse_iterator与const_reverse_iterator同样相当于对应的T*和const T*,所不同的是,增加reverse_iterator或者const_reverse_iterator会在从尾到头的遍历中让你移动到容器的下一个元素。
迭代器使用的一个重要指导方针是:尽量使用iterator代替其他三种迭代器,原因有:
(1) insert和erase的一些版本要求iterator。如果你需要调用这些函数,你就必须产生iterator,而不能用const或reverse iterators。
(2)不可能把const_iterator隐式转换成iterator。
条款27:用distance和advance把const_iterator转化成iterator
如果你只有一个const_iterator,而你要在它所指向的容器位置上插入新元素呢?也就是如何把const_iterator转化为iterator呢?前面提到:并不存在从const_iterator到iterator之间的隐式转换,也就是说下面操作是不可以的:
条款28:了解如何通过reverse_iterator的base得到iterator
条款29:XXXXXXXXXXXXXXXXXXXX(不常见)
5、算法
条款30:确保目标区间足够大
“把transform的结果放入叫做results容器的结尾”的正确方法是调用back_inserter来产生指定目标区间起点的迭代器:
(1) 如果你需要在vector、string、deque或数组上进行完全排序,你可以使用sort或stable_sort。
(2) 如果你有一个vector、string、deque或数组,你只需要排序前n个元素,应该用partial_sort。
(3) 如果你有一个vector、string、deque或数组,你需要鉴别出第n个元素或你需要鉴别出最前的n个元素,而不用知道它们的顺序,nth_element是你应该注意和调用的。
(4) 如果你需要把标准序列容器的元素或数组分隔为满足和不满足某个标准,你大概就要找partition或stable_partition。
(5) 如果你的数据是在list中,你可以直接使用partition和stable_partition,你可以使用list的sort来代替sort和stable_sort。如果你需要partial_sort或nth_element提供的效果,你就必须间接完成这个任务,主要有三种方法:
[1] 把元素拷贝到一个支持随机访问迭代器的容器中,然后对它应用需要的算法。
[2] 另一个方法是建立一个list::iterator的容器,对那个容器使用算法,然后通过迭代器访问list元素。
[3]使用有序的迭代器容器的信息来迭代地把list的元素接合到你想让它们所处的位置
条款32:如果你真的想删除东西的话就在类似remove的算法后接上erase
先看一个错误的实例:
该条款是对条款32的补充,采用remove/erase方式删除指针容器中的数据会造成内存泄漏,如:
Widget C和Widget B的内存不会被释放,造成内存泄漏,正确的方法有两种:
(1)在应用erase-remove惯用法之前先删除指针并设置它们为空,然后除去容器中的所有空指针:
条款34:注意哪个算法需要有序区间
STL中只能操作有序数据(升序)的算法有:
(1) binary_search:二分查找
(2) lower_bound:下界
(3) upper_bound:上街
(4) equal_range:所有等于某个值的元素
(5) set_union:集合并集
(6) set_intersection:集合交集
(7) set_difference :集合差集
(8) set_symmetric_difference:包含在第一个集合但是不包含在第二个集合中的元素,包含在第2个集合但是不包含在第1个集合中的元素
(9) merge:合并两个有序表
(10) inplace_merge:合并两个有序表
(11) includes:检测一个区间的所有对象是否在另一个区间中
另外,下面的算法一般用于有序区间,虽然它们不要求:
(12) unique:去重,相同的元素必须紧挨着,排序是个特例
(13) unique_copy:同上
条款35:通过mismatch或lexicographical比较实现简单的忽略大小写字符串比较
实现忽略大小写字符串比较有两种方法,一种是使用mismatch函数:
STL有很多有趣的地方,其中一个是虽然有11个名字带“copy”的算法:
(1) copy
(2) copy_backward
(3) replace_copy
(4) reverse_copy
(5) replace_copy_if
(6) unique_copy
(7) remove_copy
(8) rotate_copy
(9) remove_copy_if
(10) partial_sort_copy
(11) unintialized_copy
但没有一个是copy_if,这需要自己实现,非常经典的一个实现是:
本条款总结了用自定义的方式统计(summarize)区间的方法,主要有两种(在头文件<numeric>中):
(1) accumulate存在两种形式:第一种是: 带有一对迭代器和初始值的形式,它可以返回初始值加由迭代器划分出的区间中值的和:
另一种形式带有一个初始和值与一个任意的统计函数,实例:
素,但传给for_each的函数只接收一个实参(当前的区间元素),而且当完成时for_each返回它的函数。
相关文章推荐
- 《Effective STL》学习笔记(第二部分)
- CommonJs,AMD,CMD是什么?
- 一些常见跨浏览器方法的封装(basic.js)
- 【剑指 offer】(二十二)—— 栈的压入、弹出序列
- HDU 4715 Difference Between Primes (筛法求素数)
- JSP九大内置对象详解(一)
- 使用clipboard.js复制页面内容到剪切板
- 图说js中的this——深入理解javascript中this指针
- CSS 入门学习篇 - 2
- JavaScript数组删除项的方法
- JavaScript数组增加新的项
- js跨域请求小结
- html5学习系列之klm类标签
- jQuery中添加自定义或函数方法
- LeetCode117—Populating Next Right Pointers in Each Node II
- JavaScript编写人机对战五子棋(终)
- jQuery事件
- LeetCode116—Populating Next Right Pointers in Each Node
- js程序练习
- HTML中用i作小图标