5

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

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

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

此外,您不能将代码应用于列表容器 - 它会卡住。这很重要,因为在向量 push_back 的情况下,在重新分配数据(何时size()==capacity()push_back()调用)后,迭代器会变得不可靠,而在列表的情况下,情况并非如此。比为什么代码挂起列表?

编辑:(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);
}
4

2 回答 2

7

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它应该适用于lists,因为std::list::push_back不会使迭代器无效。您是对的,std::vector::push_back在发生重新分配时使迭代器无效,因此进行reserve调用非常重要,因为它确保不需要重新分配。

于 2013-06-14T08:37:13.970 回答
5

用于确定要复制的内容的begin()和指针/迭代器在调用函数时被评估一次。end()所以本质上std::copy()会增加它的游标迭代器,它最终会到达end(),因为vector<T>::const_iterator它只是一个T*老式数组上的一个花哨的指针。

正如您正确提到的,如果 apush_back进行vector重新分配并将数据移动到内存中的其他位置,则复制的下一个元素将具有错误的源地址,这可能会导致分段错误。

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

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

于 2013-06-14T08:57:49.417 回答