STL之Ranges区间讨论
描述
STL里的算法用来处理一个或多个区间内的元素,这样的区间可以涵盖容器内的全部元素,但非强制要求。为了得以操作容器元素的某个子集,我们必须将区间首尾当做两个参数传递给算法。需要注意的是调用者需要保证由这个两个参数定义出来的区间是有效的。有效性是指,从起点出发,逐一前进,能够到达终点。即调用者必须确保两个迭代器隶属于同一个容器,而且前后位置放置正确。
STL算法所处理的都是半开区间(half-open ranges),包括起始位置元素但不包括结尾元素位置。表达式:[begin, end).
半开半并区间的由以下优点:
1.为“遍历元素时,循环的结束时机”提供了一个简单的判断依据。迭代器未到达end(),还可以继续遍历
2.不必为空区间采取特殊处理,空区间时begin()= end()。
不利的的地方:
1.可能会对一个空区间采取操作,但编译器不会去检查 比如:reverse(iVec.begin(), iVec.begin());
2.需要注意到STL算法不会处理尾元素,容器造成“少1”的情况,尽管不会报错。
处理单一区间
int _tmain(int argc, _TCHAR* argv[]) { list<int> ilist; list<int>::iterator pos25, pos35; for (int i = 20; i <= 40; ++i) { ilist.push_back(i); } pos25 = find(ilist.begin(), ilist.end(), 25); pos35 = find(ilist.begin(), ilist.end(), 35); cout << "max: " << *max_element(pos25, pos35) << endl; //exclude pos35, max number = 34 cout << "max: " << *max_element(pos25, ++pos35) << endl;//include pos35, max number = 35 //ilist存储数字:20 21 22 ...40,顺序为递增的方式,25 一定在 35的前面 //因此[pos25, pos35)list<int>::iterator迭代器++能够到达 pos35,该区间是有效的 return 0; }
例子1中我们我们知道 pos25 一定在pos35之前,因此[pos25, pos35)是个有效区间。假设 我们不清楚pos25 或者pos35的前后关系,甚至它们是否存在都不清楚,我们就需要确定区间的有效性,否则会导致未定义行为,现在分几种情况进行讨论和确定区间有效性的方法。
场景一:pos25 和pos35 是随机存取迭代器,方法:利用operator < 进行检查
if (pos25 < pos35) { //[pos25, pos35) is valid ... } else if (pos35 < pos25) { //[pos35, pos25) is valid ... } else { //pos25 == pos35 == end() ... }
场景二:pos25 和pos35 非随机存取迭代器,方法:“起点和某个迭代器之间”以及“某个迭代器和终点”之间寻找另外一个迭代器。
pos25 = find(ilist.begin(), ilist.end(), 25); pos35 = find(ilist.begin(), pos25, 35); if (pos35 != pos25) { // pos35 is in front of pos25 //so only[pos35, pos25) ... } else { pos35 = find(pos25, ilist.end(), 35); if (pos35 != pos25) { // pos25 is in front of pos35 //so only[pos25, pos35) ... } else { //both are equal, so both must be end() ... } }
处理多个区间
STL算法中需要同时处理多个区间。通常情况下必须设定第一个区间的起点和终点,对于其他区间只需要设定起点即可,其他区间的终点可由第一个区间的元素数量推倒出来。特别需要注意的地方是:
某个算法用来处理多个区间时,务必需要确保第二个之后的区间所拥有的个数,至少和第一个区间的元素相同。特别是执行涂写动作(赋值)时,务必确保目标区间有足够的大小。
如下代码程序就会奔溃:
int _tmain(int argc, _TCHAR* argv[]) { list<int> ilist; vector<int> iVec;//ivec初始未指定大小,默认是0 for (int i = 0; i < 6; i++) { ilist.push_back(i); } copy(ilist.begin(), ilist.end(), iVec.begin());//执行涂写操作,目标区间为0,运行奔溃 return 0; }</span>
若想避免以上错误,可以采用两种方式,但这两种方式只适用于序列式容器(vector,deques,list):
1.确认目标空间有足够的大小
2.采用安插型迭代器
形如如下程序:
<span style="font-size:18px;">int _tmain(int argc, _TCHAR* argv[]) { list<int> ilist; vector<int> iVec;//ivec初始未指定大小,默认是0 for (int i = 0; i < 6; i++) { ilist.push_back(i); } //iVec.resize(ilist.size()); 方式一 copy(ilist.begin(), ilist.end(), back_inserter(iVec));//方式二:采用迭代器 back_inserter for (int i = 0; i < 6; i++) { cout << i + 1 <<"th:" << iVec[i] <<endl; } return 0; }