繁体   English   中英

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

[英]How can references be valid while iterators become invalidated in a deque

我在理解这个概念时遇到了一些困难。 从此线程在这里声明

双端队列要求在前面或后面的任何插入均应使对成员元素的任何引用保持有效。 可以使迭代器无效,但是成员本身必须保留在内存中的同一位置。

我从这个线程中得到的印象是

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

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

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

更新:

我知道双端队列是由随机的内存块实现的,而这些内存块是由独立的数据结构(例如动态数组)跟踪的。 但是,我很难理解迭代器如何无效,而引用却是有效的,因为本质上来说,迭代器是数据结构内容的通用指针。 这使我认为迭代器可能指向其他对象,而指针指向实际项目? 考虑下面的向量图。

在此处输入图片说明

根据我在上图中对向量的理解,如果指针的内容更改,则迭代器也会更改。 对于双端队列,这有何不同?

考虑以下方面的双端队列:

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
};

双端队列基本上是一些容器,用于存储指向包含某些元素的页面的指针。 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);
}

进入双端队列的迭代器需要能够跨页面“跳转”。 实现此目的最简单的方法是让它保留一个迭代器,使其从容器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迭代器(即向量中的迭代器)在向量被更改时可能会失效(这种情况在需要新页面时发生),因此,插入双端队列的整个迭代器在插入元素时可能会失效。 (这可以通过不使用向量作为指针的容器来“修复”,尽管这可能还会带来其他负面影响。)


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

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

现在,如果我们在末尾添加另一个元素,则需要一个新页面来存储它。 因此,我们分配一个,并将指向该新页面的指针存储到向量中。 由于其容量小于新的大小( 1 < 2 ),因此它需要分配一个新的更大的内存区域并将其当前内容移动到那里。 假设新区域为0x20 指针之前已存储的内存( 0x10 )被释放。

现在,插入之前与上面相同的元素仍位于相同的地址( 0x100 ),但是对其进行迭代的对象将通过0x20 因此,从上面访问0x10的迭代器无效。

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

因为您引用的答案是错误的 ,并且因为迭代器不只是指针。 首先,链表迭代器需要一个指向元素的指针,还需要“下一个”和“上一个”指针。 就在那儿,通过一个简单的示例,您的想法“迭代器是数据结构内容的通用指针”完全消失了。

deque比完全连续的结构(例如, vector )更复杂,并且比完全非连续的结构(例如, list )更复杂。 当双端队列增长时,其整体结构会适应,而实际元素的重新分配最少(通常为 )。

结果是,即使某些元素不移动,允许访问它们的“控件”也可能需要使用新的元数据进行更新,例如有关相邻元素(可能确实移动了)的位置的信息。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM