讨论:给定v1和v2两个矢量(vector),使v1的内容和v2的后半部分相同的最简单操作如何实现?
方案1:v1.assign(v2.begin()+v2.size()/2,v2.end());
方案2:v1.clear(); copy(v2.begin()+v2.size()/2,v1.end(),back_inserter(v1));
方案3:使用循环将所需要插入的元素一个一个地插入到目标容器中。
vector<int>::iterator insertLoc(v.begin());
for(int i=0;i<numValues;i++)
{
insertLoc=v.insert(insertLoc,data[i]);
++insertLoc;
}
方案3中必须要实时更新insertLoc的值,否则可能会有两个方面的问题:1)如果不实时更新insertLoc的值,那么第一次循环后,insertLoc可能会失效,导致后面的循环无法进行。2)即使该值是有效的,但是由于不更新该值,那么元素都将会插入在容器的最前端。
上面三种方案中最合适的方案首选方案一。这也是这篇博文的主题:区间成员函数优先于单元素的成员函数。
方案三中需要调用numValues次insert()函数,而方案一中只需要调用一次足矣。insert()函数的操作是每次插入一个元素或者多个元素时,需要从插入位置开始,后面的每一个元素都需要向后移动若干个位置。所以方案3中,每次有需要insertLoc后面的每个元素向后移动一个位置,一共需要移动numValues次。虽然方案1和方案3移动的位置个数相同,但是方案1一次就可以把所有的位置腾出来,而方案3却需要多次挪动,会降低程序的执行效率。对于用户自定义的数据类型,比如用户自己定义的类,那么就不仅仅是移动位置这么简单了。在每次移动中,还需要执行拷贝构造函数和赋值操作符。以上面的问题为例,那么如果单元素插入的话,共需要调用(n-1)*numValues次构造函数和赋值操作符。那么两部分的时间加在一起就是不小的时间开销。
另外当vector开始分配的内存已满,那么vector会自动增加内存大小,同时再把原来旧容器中的元素装到新容器中,并把旧容器的内存释放掉。上面针对vector的分析同样适用于deque和string,但是在内存管理上deque和vector不同,关于内存管理的分析则不再适用。
上图是容器list的数据结构,每次有新的节点在A节点前插入时,A会把其pre指针指向新指针。如果在A节点前面插入numValues个指针,那么,对所有插入的节点的next指针就会有numValues-1次是多余赋值的。同理,如果是一个一个插入那么A节点的pre指针也要多余赋值numValues次。
区间创建:所有的标准容器都提供如下形式的构造函数:
container::container(InputIterator begin,//区间开始
InputIterator end);//区间结束
区间插入:所有的标准序列容器都提供如下形式的insert:
void container::insert(iterator position, //在何处插入区间
InputIterator begin, //区间开始
InputIterator end); //区间结束
区间删除:所有的标准容器都提供区间形式的删除操作,但对于序列和关联容器,其返回值有所不同。
iterator container::erase(iterator begin,iterator end);
而关联容器则提供如下形式:
void container::erase(iterator begin,iterator end);