当迭代器在双端队列中失效时,引用如何有效

时间:2015-09-26 17:48:21

标签: c++ pointers iterator deque

我在抓住这个概念时遇到了一些困难。从这里thread开始说明

  

deque要求保持前面或后面的任何插入   对成员元素的任何引用都有效。迭代器是可以的   无效,但成员自己必须留在同一个地方   存储器中。

我受到来自this帖子的印象

  

指针实际上是一种迭代器。实际上,对于某些容器类型,相应的迭代器可以是   简单地实现为指针。

     

如果我们有一个指针和一个迭代器,每个引用都相同   容器的元素,然后任何使一个容器失效的操作   使另一方无效。

因此,如果迭代器失效,则引用也会失效。 我的问题是这是怎么可能的。如果指向某个内存地址的迭代器变为无效,那么对该地址的引用如何有效?

更新

据我所知,deque是由随机的内存块实现的,这些内存块由独立的数据结构(如动态数组)跟踪。但是我很难理解迭代器如何无效但引用可能是有效的,因为本质上迭代器是数据结构内容的通用指针。这让我觉得当指针指向实际项时,迭代器可能指向其他东西?考虑下面的向量图。

enter image description here

根据我在上图中对矢量的理解,如果指针的内容发生变化,迭代器也会发生变化。对于一个双性人来说,这有什么不同?

2 个答案:

答案 0 :(得分:2)

根据以下内容考虑一个双端队列:

template<typename T>
struct deque_stub {
 using Page = std::array<T, 32>; // Note: Not really, rather uninitialised memory of some size;
 std::vector<std::unique_ptr<Page>> pointers_to_pages;
 std::size_t end_insert{32};
 std::size_t start_elem{0};
 // read further
};

deque基本上是一个容器,存储指向包含一些元素的页面的指针。 (start_elemend_insert成员将跟踪在页面偏移方面的位置开始和结束的有效元素范围。)

当需要新页面时,插入最终会更改此容器:

template<typename X>
void push_back(X&& element) {
 if (end_insert == 32) {
  // get a new page at the end
  pointers_to_pages.push_back(make_unique<Page>());
  end_insert = 0;
 }
 (*(pointers_to_pages.back()))[end_insert] = std::forward<X>(element);
 ++end_insert;
}

template<typename X>
void push_front(X&& element) {
 if (start_elem == 0) {
  pointers_to_pages.insert(
    pointers_to_pages.begin(), std::make_unique<Page>());
  start_elem = 32;
 }
 --start_elem;
 (*(pointers_to_pages.front()))[start_elem] = std::forward<X>(element);
}

进入该双端队列的迭代器需要能够跳跃&#34;跨页。实现这一目标的最简单方法是让它从容器pointers_to_pages中保留一个迭代器到当前页面:

struct iterator {
 std::size_t pos;
 std::vector<std::unique_ptr<Page>>::iterator page;
 // other members to detect page boundaries etc.
};

但是,因为page迭代器(向量中的迭代器)可能在向量发生更改时失效(在需要新页面时发生),因此插入到deque中的整个迭代器可能会在插入时失效元素。 (这可能是&#34;修复&#34;不使用向量作为指针的容器,尽管这可能会产生其他负面影响。)

例如,考虑使用单页但整页的双端队列。因此,持有指向页面的指针的向量仅包含单个元素,在地址0x10处,并且让我们进一步假设其当前容量也仅为1个元素。页面本身存储在某个地址,让我们说0x100

因此,双端队列的第一个元素实际存储在0x100,但是使用迭代器进入双端队列意味着首先查看0x10页面的地址。

现在,如果我们在最后添加另一个元素,我们需要一个新页面来存储它。所以我们分配一个,并将指向该新页面的指针存储到向量中。由于它的容量小于新的大小(1 < 2),它需要分配一个新的更大的内存区域并在那里移动它的当前内容。让我们说,新区域位于0x20

之前已经存储指针的存储器(0x10

现在插入之前的相同元素仍在同一地址(0x100),但它的迭代器将通过0x20。从上面访问0x10的迭代器因此无效。

由于元素位于同一地址,因此指针和对它的引用仍然有效,很难。

答案 1 :(得分:1)

因为您引用的答案是错误的,并且因为迭代器不仅仅是指针。首先,链表迭代器需要一个指向元素的指针,但也需要&#34; next&#34;和&#34;之前&#34;指针。就在那里,用这个简单的例子,你的概念&#34;迭代器是数据结构内容的通用指针&#34; 完全被吹灭了。

deque比完全连续的结构(例如vector)更复杂,并且比完全不连续的结构(即list)更复杂。当deque增长时,它的整体结构会变得合适,而实际元素的重新分配最少(通常 none )。

结果是,即使某些元素不移动,&#34;控制件&#34;允许访问它们可能需要使用新的元数据进行更新,例如,现在相邻元素(可能 移动)的位置。

现在,deque无法神奇地更新已在某处实例化的迭代器:它可以做的就是记录旧迭代器无效并且你将以通常的方式获得新的迭代器。