STL 中容器的插入、删除、赋值都有相应的区间成员函数,相比于单元素的成员函数而言,使用区间成员函数使得代码更加清晰,更加高效。

区间函数示例

示例1:给定 v1 和 v2 两个向量,使得 v1 的内容和 v2 的后半部分相同。

方法一,使用单元素成员函数,依次将 v2 的元素 push back 到 v1 中

v1.clear();
for(auto iter = v2.begin()+v2.size()/2; iter != v2.end(); iter ++)
{
  v1.push_back(*iter);
}

方法二,使用区间成员函数 assign

v1.assign(v2.begin() + v2.size()/2, v2.end())

示例2:如何将一个 int 数组拷贝到一个 vector 的前端

方法一,通过循环依次将数组中的元素 insert 到 vector

int data[numValues];
vector<int> v;
//...
auto insertLoc = v.begin();
for(int i = 0; i < numValues; i++)
{
  insertLoc = v.insert(insertLoc, data[i]);
  insertLoc++;
}

方法二,使用 vector 的区间 insert 函数

int data[numValues];
vector<int> v;
v.insert(v1.begin(), data, data+numValues);

通过以上两个示例,明显可以感觉到使用区间函数是非常的方便,而且意图清晰,代码更加的直接。而且使用区间函数往往比与之对应的单元素成员函数更加高效。

区间删除 erase 结合 remove 算法

如何删除 vector 容器中指定值的元素? 第一个想到的是 vector 提供的 erase 方法,但是该方法只能删除特定位置或者某个区间的元素值,那怎么删除指定值得元素呢,这里就要结合算法 remove 去实现了。

先看下 remove 声明如下,remove 需要一对迭代器来指定所要进行操作的元素区间:

template<class ForwardIterator, class T>
ForwardIterator remove(ForwardIterator first, ForwardIterator last, const T& value);

因为从容器中删除元素的唯一方法就是调用该容器的成员函数,而 remove 并不知道它操作的元素所在的容器,所以 remove 不可能从容器中删除元素。remove 不是真正意义上的删除元素,只是返回一个指向新的区间结尾的迭代器。

在内部,remove 遍历整个区间,用需要保留的元素的值覆盖掉要被删除的元素的值。为了删除这些元素,需要调用区间形式的 erase 方法从新的逻辑结尾一直到原区间的结尾都删除。

vector<int> v;
v.erase(remove(v.begin(), v.end(), 99), v.end);

但是有一个例外,就是 list 的 remove 成员函数,这是 STL 中唯一一个名为 remove 并且确实删除了容器中元素的函数:

list<int> lst;
//...

lst.remove(99); //删除所有值为99的元素,并且list的大小也会改变

区间函数分类

区间创建

container::container(InputIterator begin, InputIterator end);

区间插入

所有标准序列容器(vector, string, deque, list)都提供了如下形式的 insert:

void container::insert(iterator position,  // 在何处插入区间
                     InputIterator begin, 
                     InputIterator end);

关联容器(set, multiset, map, multimap)利用比较函数来决定元素该插入何处,它们提供了一个省去position参数的函数原型:

void container::insert(InputIterator begin, InputIterator end);

对于标准序列容器,当看到使用 push_front 或者 push_back 循环调用时,或者 front_inserter 或者 back_inserter 被当做参数传递给 copy 函数时,你会发现在这里区间形式的 insert 可能是更好的选择。

区间删除

所有的标准序列容器都提供了区间形式的删除操作,但对于序列和关联容器,其返回值有所不同。序列容器提供了这样的形式:

iterator container::erase(iterator begin, iterator end);

而关联容器则提供了如下形式:

void container::erase(iterator begin, iterator end);

对于 vector 和 string 来说,内存会反复分配(对 erase,会是反复的释放),但是当元素数目减少时却不会自动减少,这个时候就需要使用 swap 来去除多余容量了。比如:

class CTest
vector<CTest> cTestVec;
vector<CTest>(cTestVec).swap(cTestVec);

这里表达式 vector(cTestVec) 创建一个临时的向量, 它是cTestVec的拷贝,然而 vector 的拷贝构造函数置为所拷贝的元素分配所需的内存,所以这个临时向量没有多余的容量。然后将临时向量中的数据和 cTestVec 中的数据做swap 。

区间赋值

void container::assign(iterator begin, iterator end);

所有容器也都提供了区间形式的 assign 。

参考阅读

《Effective STL》 - 第5条,32条