我不明白为什么这段代码是准确的
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()
的指针。有点像狗在追自己的尾巴。
用插图和图表更容易理解,阅读双链表和哨兵节点,这一切都是有意义的。