为什么push_back或push_front使deque的迭代器无效?

时间:2009-05-26 22:23:22

标签: c++ stl iterator deque

正如标题所示。

我对deque的理解是它分配了“块”。我没有看到如何分配更多的空间使迭代器失效,如果有的话,人们会认为deque的迭代器比矢量更有保证,而不是更少。

7 个答案:

答案 0 :(得分:17)

C ++标准没有规定如何实现deque。不需要通过分配新的空间并将其链接到先前的空间来分配新的空间,所需要的只是每端的插入是按照固定时间摊销。

所以,虽然很容易看到如何实现deque以便它提供你想要的保证[*],但这不是唯一的方法。

[*]迭代器具有对元素的引用,以及对其所在块的引用,以便它们在到达时可以继续向前/向后移出块的末尾。另外,我假设对deque本身的引用,以便operator+可以像随机访问迭代器一样保持恒定时间 - 从块到块之间的链接链不够好。

答案 1 :(得分:13)

更有趣的是,push_backpush_front使任何引用无效到deque的元素。只有迭代器才被认为是无效的。

据我所知,标准没有说明原因。但是,如果实现了一个知道其直接邻居的迭代器 - 如列表所示 - 如果迭代器指向一个同时位于双端队列边缘和块边缘的元素,那么迭代器将变为无效。

答案 2 :(得分:6)

我的猜测。 push_back / push_front 可以分配新的内存块。 deque迭代器必须知道递增/递减运算符何时应跳转到下一个块。实现可以将该信息存储在迭代器本身中。在 push_back / push_front 之后递增/递减旧迭代器可能无法按预期工作。

此代码可能会或可能不会因运行时错误而失败。在我的Visual Studio上,它在调试模式下失败,但在发布模式下运行结束。在Linux上它导致了分段错误。

#include <iostream>
#include <deque>

int main() {
    std::deque<int> x(1), y(1);
    std::deque<int>::iterator iterx = x.begin();
    std::deque<int>::iterator itery = y.begin();

    for (int i=1; i<1000000; ++i) {
        x.push_back(i);
        y.push_back(i);
        ++iterx;
        ++itery;
        if(*iterx != *itery) {
            std::cout << "increment failed at " << i << '\n';
            break;
        }
    }
}

答案 3 :(得分:5)

关键是不做任何假设只是将迭代器视为无效。

即使它现在工作正常,编译器的更高版本或不同平台的编译器可能会出现并破坏您的代码。或者,同事可能会出现并决定将您的双端队列转换为向量或链接列表。

答案 4 :(得分:2)

即使在分配块时,如果没有足够的空间(如向量的情况),插入也会导致重新分配该特定块。

答案 5 :(得分:2)

迭代器不仅仅是对数据的引用。它必须知道如何增量等。

为了支持随机访问,实现将有一个指向块的动态指针数组。 deque迭代器将指向此动态数组。当deque增长时,可能需要分配新的块。动态数组将增长,使迭代器失效,从而使deque的迭代器失效。

所以不是重新分配块,而是指向这些块的指针数组。事实上,正如Johannes Schaub所指出的那样,引用并没有失效。

另请注意,deque的迭代器保证不小于向量,当容器增长时也会失效。

答案 6 :(得分:0)

因为标准说它可以。它并没有强制要求将deque实现为块列表。它要求具有特定前后条件和特定算法复杂度最小值的特定接口。

只要满足所有这些要求,实施者就可以以他们选择的任何方式自由地实施该事物。一个明智的实现可能会使用块列表,或者它可能会使用其他一些具有不同权衡的技术。

在所有情况下,对于所有用户而言,可能无法说一种技术严格优于另一种技术。这就是为什么该标准赋予实现者一定的选择自由。