我不明白为什么这段代码是准确的
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()可以用作容器的末尾,即使添加新内容也是如此?
此外,您无法将代码应用于列表容器 - 它会卡住。这很重要,因为在向量的情况下,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);
}
答案 0 :(得分: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
它应该适用于list
,因为std::list::push_back
不会使迭代器无效。
你认为std::vector::push_back
在重新分配时使迭代器无效是正确的,所以进行reserve
调用非常重要,因为它确保不需要重新分配。
答案 1 :(得分:5)
调用函数时,用于确定要复制的内容的begin()
和end()
指针/迭代器将被计算一次。所以基本上std::copy()
会增加它的光标迭代器,最终会达到end()
,因为vector<T>::const_iterator
只是旧校数组上的一个奇特的T*
指针。
正如您正确提到的,如果push_back
使vector
重新分配并将数据移动到内存中的其他位置,则复制的下一个元素将具有错误的源地址,这很可能会结束分段故障。
对于列表,它永远不会终止,因为end()
是一个标记/保护指针,并且只能通过在列表的最后一个元素上递增迭代器来达到end()
。所以end()
本身的地址永远不会改变,但是因为你不断在列表的末尾添加一个元素,你永远不会到达最后一个元素,所以std::copy()
永远不会获得指向{{{ 1}}。有点像追逐它尾巴的狗。
通过插图和图表,双重链接列表和标记节点的读取更容易理解,这一切都有意义。