簡體   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