为什么vector :: iterator在重新分配时失效?

时间:2012-06-11 00:58:06

标签: c++ vector iterator invalidation

我不明白为什么在重新分配时应该使vector的迭代器失效。

仅仅通过在迭代器中存储偏移 - 而不是指针 - 来阻止这种情况吗?

为什么vector没有这样设计?

7 个答案:

答案 0 :(得分:15)

因为迭代器要这样做,他们需要存储一个指向矢量对象的指针。对于每次数据访问,他们需要按照指向矢量的指针,然后按照其中的指针到数据数组的当前位置,然后添加偏移量*元素大小。这会慢得多,并且size_type成员需要更多内存。

当然,它有时是一个很好的折衷方案,如果能够在需要时选择它会很好,但它比(C风格)直接阵列使用更慢,更笨重。在引入STL时,std::vector被无情地审查了性能,并且正常实现针对空间和速度优化了这个便利/可靠性因素,就像数组等效operator[]和数组一样快但不如at()安全。

答案 1 :(得分:15)

只是为与性能相关的理由添加一个引用:在设计C ++时,Stroustrup认为像std::vector这样的模板类接近本机数组的性能特征至关重要:

  

强调运行时效率的一个原因是我想要的   模板在时间和空间上足够有效,可用于   低级类型,如数组和列表。

     

...

     

更高级别的替代方案 - 例如,带有大小()的范围检查数组   操作,多维数组,具有适当数字的矢量类型   向量操作和复制语义等 - 将被接受   用户只有在他们的运行时,空间和符号方便的情况下   接近了内置数组。

     

换句话说,提供参数化类型的语言机制   应该是有关用户应该能够负担得起的   消除数组的使用,转而使用标准库类。

Bjarne Stroustrup, C ++的设计和演变,p.342。

答案 2 :(得分:5)

您可以通过包装标准std::vector<T>::iterator来增加安全性,但不能通过包装extension::vector<T>::safe_iterator来增加速度。这是一般原则,并解释了许多C ++设计选择。

答案 3 :(得分:3)

这些决定有很多原因。正如其他人所指出的,向量的iterator的最基本实现是指向元素的普通指针。为了能够处理push_back,必须修改迭代器以处理指向矢量和位置的指针,在通过运算符访问时,矢量指针将被取消引用,指向所获得数据的指针和添加位置,额外取消引用。

虽然这不是最有效的实施,但这并不是一个限制因素。 VS / Dinkumware库中的迭代器的默认实现(即使在发布中)是经过检查的迭代器,它们管理等量的信息。

实际问题伴随着其他变异操作。考虑在向量的中间插入/擦除。为了保持所有迭代器的有效性,容器必须跟踪迭代器的所有实例并调整位置字段,以便它们仍然引用相同的元素(已被插入/移除替换)。

答案 4 :(得分:1)

您需要存储偏移量和指向矢量对象本身的指针。

如指定的那样,迭代器可以只是一个指针,占用的空间更少。

答案 5 :(得分:1)

TL; DR - 因为你为非常复杂的远距离动作交易了无效的简单规则。

请注意,“存储指向矢量对象的指针”会导致新的失效案例。例如,今天swap保留了迭代器的有效性,如果向量的指针(或引用)存储在迭代器中,它就不再可能。移动矢量元数据本身的所有操作(矢量矢量任何人?)都会使迭代器失效。

交易是“当元素的指针/引用无效时,迭代器变为无效”,“当向量的指针/引用无效时,迭代器变为无效”。

性能参数并不重要,因为提议的替代实现甚至不正确。

答案 6 :(得分:0)

我的迭代器没有失效,它是否指向相同的元素或插入之前的相同位置?换句话说,即使没有性能问题,决定使用哪种替代定义也是非常重要的。