迭代器
标准STL容器提供了四种不同的迭代器:
iterator、const_iterator、reverse_iterator和const_reverse_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代替其他三种迭代器,原因有:
- insert和erase的一些版本要求iterator。如果你需要调用这些函数,你就必须产生iterator,而不能用const或reverse iterators。
- 不可能把const_iterator隐式转换成iterator。
条款27:用distance和advance把const_iterator转化成iterator
如果你只有一个const_iterator,而你要在它所指向的容器位置上插入新元素呢?也就是如何把const_iterator转化为iterator呢?前面提到:并不存在从const_iterator到iterator之间的隐式转换,也就是说下面操作是不可以的:
typedef deque<int> IntDeque;
// 方便的typedef
typedef IntDeque::iterator Iter;
typedef IntDeque::const_iterator ConstIter;
ConstIter ci;
// ci是const_iterator
...
Iter i(ci);// 错误!没有从const_iterator 到iterator隐式转换的途径
Iter i( const_cast<Iter>(ci)); // 仍是个错误!不能从const_iterator映射为iterator!
正确方法如下:
typedef deque<int> IntDeque;
typedef IntDeque::iterator Iter;
typedef IntDeque::const_iterator ConstIter;
IntDeque d;
ConstIter ci;
...
// 让ci指向d
Iter i(d.begin());
// 初始化i为d.begin()
advance(i, distance <ConstIter> (i, ci));
// 把i移到指向ci位置
注:distance返回两个指向同一个容器的iterator之间的距离;advance则用于将一个iterator移动指定的距离。如果i和ci指向同一个容器,那么表达式advance(i, distance(i, ci))会将i移动到与ci相同的位置上。
5、算法
条款30:确保目标区间足够大
目标区间不会进行内存检查,也不会动态扩充内存,所以要提前估算目标区间的内存大小,也可以使用插入迭代器。
条款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提供的效果,你就必须间接完成这个任务
条款32:如果你真的想删除东西的话就在类似remove的算法后接上erase
先看一个错误的实例:
// 建立一个vector<int> 用1-10填充它
vector<int> v;
v.reserve(10);
for(int i = 1; i <= 10; ++i) {
v.push_back(i);
}
cout << v.size();
// 打印10
v[3] = v[5] = v[9] = 99;
// 设置3个元素为99
remove (v.begin(), v.end(), 99);
// 删除所有等于99的元素
cout << v.size();
// 仍然是10!
正确的删除元素的方法是:
vector< int> v;
// 正如从前
v.erase( remove (v.begin(), v.end(), 99), v.end());
// 真的删除所有等于99的元素
cout << v.size();
// 现在返回7
需要注意的是remove和erase是亲密联盟,这两个整合到list成员函数remove中。这是STL中唯一名叫remove又能从容器中除去元素的函数:
list<int> li;
// 建立一个list
// 放一些值进去
li.remove(99);
// 除去所有等于99的元素:真的删除元素,所以它的大小可能改变了
remove_if ,unique和remove
类似,都需要跟erase连用才可以真正删除数据,同样,对于list是个特殊。
条款33:提防在指针的容器上使用类似remove的算法
该条款是对条款32的补充,采用remove/erase方式删除指针容器中的数据会造成内存泄漏, 正确的方法有两种:
(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:同上
原创文章,转载请注明: 转载自董的博客
本文链接地址: http://dongxicheng.org/cpp/effective-stl-part3/