为什么push_back或push_front会使双端队列的迭代器失效?

发布于 2024-07-22 06:03:11 字数 103 浏览 5 评论 0原文

正如标题所问。

我对双端队列的理解是它分配“块”。 我不明白分配更多空间如何使迭代器失效,如果有的话,人们会认为双端队列的迭代器比向量的迭代器有更多的保证,而不是更少。

As the title asks.

My understanding of a deque was that it allocated "blocks". I don't see how allocating more space invalidates iterators, and if anything, one would think that a deque's iterators would have more guarantees than a vector's, not less.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

对不⑦ 2024-07-29 06:03:11

C++ 标准没有指定双端队列是如何实现的。 不需要通过分配新块并将其链接到先前的块来分配新空间,所需要的只是在每一端的插入以恒定时间分摊。

因此,虽然很容易了解如何实现双端队列以提供您想要的保证[*],但这并不是唯一的方法。

[*] 迭代器具有对元素的引用,以及对其所在块的引用,以便它们在到达块的末端时可以继续前进/后退。 另外,我认为对双端队列本身的引用,使得operator+可以像随机访问迭代器所期望的那样是恒定时间的——遵循从块到块的链接链还不够好。

The C++ standard doesn't specify how deque is implemented. It isn't required to allocate new space by allocating a new chunk and chaining it on to the previous ones, all that's required is that insertion at each end be amortized constant time.

So, while it's easy to see how to implement deque such that it gives the guarantee you want[*], that's not the only way to do it.

[*] Iterators have a reference to an element, plus a reference to the block it's in so that they can continue forward/back off the ends of the block when they reach them. Plus I suppose a reference to the deque itself, so that operator+ can be constant-time as expected for random-access iterators -- following a chain of links from block to block isn't good enough.

不甘平庸 2024-07-29 06:03:11

更有趣的是,push_backpush_front 不会使任何对双端队列元素的引用无效。 只有迭代器才被假定为无效。

据我所知,该标准没有说明原因。 但是,如果实现的迭代器知道其直接邻居(如列表),则如果迭代器指向既位于双端队列边缘又位于块边缘的元素,则该迭代器将变得无效。

What's more interesting is that push_back and push_front will not invalidate any references to a deque's elements. Only iterators are to be assumed invalid.

The standard, to my knowledge, doesn't state why. However if an iterator were implemented that was aware of its immediate neighbors - as a list is - that iterator would become invalid if it pointed to an element that was both at the edge of the deque and the edge of a block.

允世 2024-07-29 06:03:11

我猜。 push_back/push_front可以分配新的内存块。 双端队列迭代器必须知道递增/递减运算符何时应跳转到下一个块。 该实现可以将该信息存储在迭代器本身中。 在 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;
        }
    }
}

My guess. push_back/push_front can allocate a new memory block. A deque iterator must know when increment/decrement operator should jump into the next block. The implementation may store that information in iterator itself. Incrementing/decrementing an old iterator after push_back/push_front may not work as intended.

This code may or may not fail with run time error. On my Visual Studio it failed in debug mode but run to the conclusion in release mode. On Linux it caused segmentation fault.

#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;
        }
    }
}
別甾虛僞 2024-07-29 06:03:11

关键是不要做出任何假设,只是将迭代器视为失效。

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

The key thing is not to make any assumptions just treat the iterator as if it will be invalidated.

Even if it works fine now, a later version of the compiler or the compiler for a different platform might come along and break your code. Alternatively, a colleague might come along and decide to turn your deque into a vector or linked list.

暖伴 2024-07-29 06:03:11

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

为了支持随机访问,实现将具有指向块的动态指针数组。 双端队列迭代器将指向这个动态数组。 当双端队列增长时,可能需要分配新的块。 动态数组将增长,使其迭代器无效,从而使双端队列的迭代器无效。

所以并不是重新分配块,而是指向这些块的指针数组可以。 事实上,正如约翰内斯·绍布(Johannes Schaub)指出的那样,引用并没有失效。

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

An iterator is not just a reference to the data. It must know how to increment, etc.

In order to support random access, implementations will have a dynamic array of pointers to the chunks. The deque iterator will point into this dynamic array. When the deque grows, a new chunk might need to be allocated. The dynamic array will grow, invalidating its iterators and, consequently, the deque's iterators.

So it is not that chunks are reallocated, but the array of pointers to these chunks can be. Indeed, as Johannes Schaub noted, references are not invalidated.

Also note that the deque's iterator guarantees are not less than the vector's, which are also invalidated when the container grows.

方圜几里 2024-07-29 06:03:11

即使您以块的形式进行分配,如果没有足够的空间(如向量的情况),插入也会导致该特定块被重新分配。

Even when you are allocating in chunks, an insert will cause that particular chunk to be reallocated if there isn't enough space (as is the case with vectors).

韵柒 2024-07-29 06:03:11

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

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

对于所有情况下的所有用户来说,可能不可能说一种技术完全优于另一种技术。 这就是为什么该标准给予实施者一些选择的自由。

Because the standard says it can. It does not mandate that deque be implemented as a list of chunks. It mandates a particular interface with particular pre and post conditions and particular algorithmic complexity minimums.

Implementors are free to implement the thing in whatever way they choose, so long as it meets all of those requirements. A sensible implementation might use lists of chunks, or it might use some other technique with different trade-offs.

It's probably impossible to say that one technique is strictly better than another for all users in all situations. Which is why the standard gives implementors some freedom to choose.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文