使用back_inserter复制算法

copy algorithm with back_inserter

本文关键字:复制 算法 inserter back 使用      更新时间:2023-10-16

我不明白为什么这段代码是准确的

vector<int> coll;
coll.reserve(2*coll.size());
copy (
  coll.begin(), coll.end(),    // zrodlo
  back_inserter(coll)          // przeznaczenie
);

coll.end()表示向量的结束。在我推出任何东西(如back_insert_iterator所做的)之后,coll.end()返回的是与之前相同的东西还是不同的东西?是否有多个终止迭代器?为什么end()可以用作容器的结束,即使添加了新内容?

此外,您不能将代码应用于列表容器—它会卡住。这很重要,因为在vector push_back的情况下,迭代器在重新分配数据后不可靠(当size()==capacity()push_back()调用时),而在list的情况下则不是这种情况。那么为什么代码挂起list?

编辑:(sscce)

#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
template <class T>
inline void PRINT_ELEMENTS (const T& coll, const char* optcstr="")
{
    typename T::const_iterator pos;
    std::cout << optcstr;
    for (pos=coll.begin(); pos!=coll.end(); ++pos) {
        std::cout << *pos << ' ';
    }
    std::cout << std::endl;
}
int main(){
  list<int> coll;
  list<int>::iterator end = coll.end();
  copy (
    coll.begin(), coll.end(),    // zrodlo
    back_inserter(coll)          // przeznaczenie
    );
  cout << *end << endl;
  PRINT_ELEMENTS(coll);
}

coll.end()在开始复制和反向插入之前被称为,因此本质上代码与

相同
coll.reserve(2*coll.size());
auto oldEnd = coll.end();
copy (coll.begin(), oldEnd, back_inserter(coll) ); 

的意思是,copy不会重新求值coll.end(),所以它不会注意到/麻烦它插入到同一个向量中,并且曾经是向量的末尾的地方不是第一次插入后的末尾。

同样的算法不会编译列表,因为std::list没有reserve方法。但是,如果没有reserve,它应该适用于list s,因为std::list::push_back不会使迭代器无效。您是对的,当重新分配发生时,std::vector::push_back会使迭代器无效,因此执行reserve调用非常重要,因为它确保不需要重新分配。

用于确定复制对象的begin()end()指针/迭代器在调用函数时求值一次。所以本质上,std::copy()将增加它的游标迭代器,最终到达end(),因为vector<T>::const_iterator只是一个老式数组上的T*指针。

正如您正确提到的,如果push_back使vector重新分配并将数据移动到内存中的其他地方,那么复制的下一个元素将具有错误的源地址,这可能最终导致分段错误。

对于列表,它永远不会终止,因为end()是一个哨兵/保护指针,并且只能通过在列表的最后一个元素上加1迭代器才能到达end()。所以end()本身的地址永远不会改变,但是因为你不断地在列表的末尾添加一个元素,你永远不会到达最后一个元素,所以std::copy()永远无法获得指向end()的指针。有点像狗在追自己的尾巴。

用插图和图表更容易理解,阅读双链表和哨兵节点,这一切都是有意义的。