Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/160.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么向后推或向前推会使deque失效';什么是迭代器?_C++_Stl_Iterator_Deque - Fatal编程技术网

C++ 为什么向后推或向前推会使deque失效';什么是迭代器?

C++ 为什么向后推或向前推会使deque失效';什么是迭代器?,c++,stl,iterator,deque,C++,Stl,Iterator,Deque,正如标题所要求的那样 我对deque的理解是它分配了“块”。我不知道分配更多的空间如何使迭代器无效,如果有什么不同的话,人们会认为deque的迭代器比vector的迭代器有更多的保证,而不是更少。即使是在分块分配时,如果没有足够的空间(如vectors),插入也会导致重新分配特定的分块 关键是不要做任何假设,只要把迭代器当作是无效的 即使它现在工作正常,编译器的更高版本或用于不同平台的编译器可能会出现并破坏您的代码。或者,一个同事可能会出现,决定把你的DEQUE变成一个向量或链表。 < P> C

正如标题所要求的那样


我对deque的理解是它分配了“块”。我不知道分配更多的空间如何使迭代器无效,如果有什么不同的话,人们会认为deque的迭代器比vector的迭代器有更多的保证,而不是更少。

即使是在分块分配时,如果没有足够的空间(如vectors),插入也会导致重新分配特定的分块

关键是不要做任何假设,只要把迭代器当作是无效的


即使它现在工作正常,编译器的更高版本或用于不同平台的编译器可能会出现并破坏您的代码。或者,一个同事可能会出现,决定把你的DEQUE变成一个向量或链表。

< P> C++标准没有指定如何实现DEQE。不需要通过分配一个新的块并将其链接到以前的块来分配新的空间,所需要的只是在每一端的插入都以固定时间摊销

因此,虽然很容易看到如何实现deque,从而提供您想要的保证[*],但这不是唯一的方法


[*]迭代器有一个对元素的引用,加上对它所在的块的引用,这样当它们到达块的末端时,就可以继续向前/向后移动。再加上我想是指德克本身,因此,
操作符+
可以像随机访问迭代器所期望的那样是常数时间——从一个块到另一个块跟踪链接链是不够的。

更有趣的是,
向后推
向前推
不会使对deque元素的任何引用无效。仅假定迭代器无效


据我所知,标准没有说明原因。但是,如果实现的迭代器知道它的近邻(如列表所示),那么如果迭代器指向的元素同时位于数据块的边缘和块的边缘,则该迭代器将变得无效。

,因为标准说它可以。它并不要求deque作为块列表来实现。它要求特定的接口具有特定的前置和后置条件以及特定的算法复杂性最小值

实现者可以自由地以他们选择的任何方式实现,只要它满足所有这些需求。一个合理的实现可能会使用块列表,也可能会使用其他一些具有不同权衡的技术


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

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

此代码可能会或不会因运行时错误而失败。在我的VisualStudio上,它在调试模式下失败,但在发布模式下运行到最后。在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;
        }
    }
}
#包括
#包括
int main(){
标准:德克x(1),y(1);
std::deque::迭代器iterx=x.begin();
std::deque::迭代器itery=y.begin();

对于(inti=1;i,迭代器不仅仅是对数据的引用。它必须知道如何递增,等等

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

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


还要注意的是,deque的迭代器保证不少于vector的,当容器增长时,vector的保证也会失效。

iirc,gcc的deque实现保留了一个指向这些块的指针数组……如果需要重新分配数组,那么迭代器可能会失效。也许这就是原因?我不确定……至少是这样解释为什么插入到两端都会使迭代器失效,但不会使元素的引用/指针失效。我认为另一个原因是,可以通过保留元素的索引来实现迭代器。如果在deque的开始/结束处插入了某些内容,则该索引现在不再有效(关闭1),尽管指向它所指向的元素的任何指针/引用仍然有效,因为当然没有发生重新分配)。当我们将索引保留在块指针数组中时也是如此(正如我在对问题的评论中所猜测的)。请参阅我的,以获取基于简单实现的对此的解释。我认为你指的不是列表(例如,std::list)的块,因为当时随机访问迭代器不能是O(1)。粗略地看一看一个实现,就会发现deque更像std::vector,其中内部向量是固定保留的,永远不会增长;然而这并不完全正确,因为向量的波前会移动元素。然而,deque::迭代器实际上是一对迭代器。内部迭代器永远不会失效s(因此解引用仍然有效),但如果外部容器重新分配,则外部块可能会坏掉。因此,在几次++iter之后,您可能会爬过内部块的末尾,必须通过外部迭代器重置到下一个块,而boom.deque不是列表,块没有链接!重新分配是这里的关键。