STL容器中的end()函数是如何实现的?

14

因此,当我们需要从头到尾遍历一个容器时,我们会写出如下的代码:

for (i = v->begin(); i != v->end(); i++)

假设 i 是容器 v 中的迭代器。

我的问题是,“什么保证 end 始终指向容器中最后一个元素的下一个元素?”STL 如何确保这种行为,并且是否存在这种情况不成立的可能性?


4
STL不能保证这种行为。STL根据标准定义的要求实现了这种行为。标准规定了操作应该如何工作,实现STL的开发人员应确保它能正确地运行。 - Martin York
6
如果你不需要存储值,最好使用前缀递增运算符++i而非后缀递增运算符i++。对于许多类型来说,这样会更快,这是最佳实践。 - Jive Dadson
@Jive Dadson: 这不是我的主要关注点 --> 在循环的每个回合重新计算 v->end() 显然效率较低... for (auto it = v.begin(), end = v.end(); it != end; ++it) 是规范形式,但在 C++0x 中也可以使用 for (auto val : v)std::foreach 和 lambda 函数。 - Matthieu M.
6个回答

25

STL通过始终存储如下内容来确保这种行为:

vector

最终 (双关语),重要的是 end()是什么,只要它始终为end() (并且显然不能与任何其他节点混淆)。


19
伙计,你因为时髦的图形得到了+1分 :-) - paxdiablo
下面这句来自C++标准的引用难道不会否定这个答案吗?特别是图表。 - yasouser

3

STL规范保证end将超过结尾(参见此处)。这总是如此。它的具体实现可能会有所不同(例如,有时将值设置为null),但只要v是有效指针,您的循环就可以正常工作。


3

C++03第23.1/7节规定:

begin()函数返回指向容器中第一个元素的迭代器。

end()函数返回指向容器末尾(即超出最后一个元素)的迭代器。

如果容器为空,则begin() == end()


这是 slist 没有被正式纳入 STL 的原因吗?slist 的 end() 是 0。 - vrdhn

2
"

“end将始终指向容器中最后一个元素的下一个位置”,这意味着,如果你增加一个指向最后一个元素的迭代器,它将等于end()的结果。实现可能不同。在Visual C++中,std::vector::end()返回一个特定于实现的迭代器,其中包含零指针。

"

2
我非常怀疑这一点,因为std::vector<T>::iterator是一个随机访问迭代器。如果std::vector<T>::end()返回(T*)0,那么end()-begin()将是非法的,但它需要返回std::vector::size() - MSalters
1
更准确地说:end返回一个实现特定的迭代器,它持有零指针。 - Kirill V. Lyadvinsky
1
持有零指针的部分是不必要和不精确的。在gcc向量实现中,vector<>::end()被定义为vector<>::begin()+vector<>::size(),并且实际上存储在vector<>对象中(GCC向量是通过三个指针:beginendend_of_capacity来实现的,其中如果向量为空,则begin==end,始终begin+size()==end,如果size()==capacity()end==end_of_capacity。没有任何零指针可见。 - David Rodríguez - dribeas
@David Rodríguez,那是我回答中“实现可以不同”的部分。 “零指针”与Visual C ++实现有关,而不是gcc。 - Kirill V. Lyadvinsky

1

你在询问所有STL容器...没有特别提到vector,其中end() 可能被实现为你显然期望的那样。在std::map<>中,"end是最后一个使用的节点之后"只是一个逻辑概念,表达了你可以安全地从最后一个使用的节点递增,将其与抽象概念"end"区分/等同,并进行一些节点算术,在其中end被认为比最后一个使用的节点多一个。不要太字面理解。


0

正如之前的一些帖子所述,end()是超过最后一个元素的。如果您需要通过迭代器访问最后一个元素,请使用iter = container.end() - 1;。否则,在向量的情况下,variable = someVector.back();,假设变量是someVector包含的数据类型。

关于它指向末尾的保证,容器本身在内部处理。您只需像对待任何其他对象的黑盒子一样对待它,并相信它正确地执行即可。

每当容器被调整大小时,它将跟踪结束位置,并在您再次访问end()之前更新。但是,根据容器的不同,如果您有一个迭代器并以某种方式更改它,则可能会使迭代器无效并中断迭代过程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接