迭代器

截至目前為止,已提供的範例都顯示了許多 fbl:: 疊代器的用法。fbl:: 中的疊代器使用的 API 與 std:: 容器中使用的 API 非常類似,因此希望這對您來說非常熟悉。但是,除了說明所有 fbl:: 疊代器支援的項目,還有與 std:: 疊代器稍微不同的部分,值得花一點時間說明。

iteratorconst_iterator

std:: 疊代器相同,fbl:: 疊代器分為兩種:非常數和常數版本。如果使用者的容器參照不是 constbegin()find() 等作業會傳回簡單的疊代器,否則會傳回 const_iterator。在 const_iterators 上執行的解除參照作業會提供 const T&,因此基礎物件的 const 存取權。

begin()/end()

std:: 一樣,容器上的 begin() 方法會將疊代器傳回至容器中的第一個元素,而 end() 則會將疊代器傳回超過容器最後一個元素的元素。在對容器的 const 參照上呼叫時,開始和結束都會自動傳回 const_iterator,但如果要在容器的可變動參照中明確使用 const_iterator,則可使用 cbegin()/cend()

比較疊代及預設初始化疊代器和 end()

std:: 一樣:疊代器支援使用 ==!= 運算子測試等式。與 std:: 不同,疊代器都沒有隨機存取疊代器語意,且任何 fbl:: 容器的疊代器類型都不支援 >>=<<= 運算子。

此外,預設初始化的疊代器與呼叫容器 end() 方法傳回的疊代器之間,會有些許內部差異。從語意上來說,兩者相同。預設的初始化疊代器和 end() 的值都無效,因此兩者之間的比較會傳回 true。然而,這兩個執行個體中包含的位元不同。測試兩個疊代器之間的相等性時,請務必使用比較運算子。

fbl::DoublyLinkedList<Obj*> the_list;
fbl::DoublyLinkedList<Obj*>::iterator default_init;
auto end_init = the_list.end();

if (default_init == end_init) { }                            // This comparison is true
if (!memcmp(&default_init, &end_init, sizeof(end_init))) { } // This is not.

疊代器進展

所有疊代器都支援 ++ 運算子的前置與後置形式。預先修正表單會將疊代器移至序列中的下一個元素,然後傳回現在指向下一個元素的疊代器副本。修正後表單會將疊代器移至序列中的下一個元素,同時傳回進階前疊代器的副本。

// Assuming that the list starts containing objects with values
// "5 7 9", in that order.
fbl::DoublyLinkedList<Obj*> the_list;
auto iter   = the_list.begin();   // iter points to "5".
auto second = iter++;             // iter points to "7", but second points to "5"
auto third  = ++iter;             // iter points to "9", and so does third
++iter;                           // iter is now equal to the_list.end()

DoublyLinkedList 的疊代器、HashTable 具有完全連結的清單值區,以及 WAVLTree 都支援透過 -- 運算子進行雙向疊代,無論在其套用前和後置後表單中都能支援雙向疊代。如果 SinglyLinkedListHashTable 具有單獨連結的清單值區,則不會。

進階疊代器超過容器結尾時,系統會提供 container.end()。試圖進一步推進是合法的,但不會變更疊代器的值。使用 -- 運算子備份目前設為 container.end() 的雙向疊代器將產生指向清單中最後一個元素的疊代器,但備份已初始化的疊代器並不是。相反地,在預設的初始化狀態上執行 ++--,會導致疊代器處於預設的初始化狀態。最後,備份值等於 container.begin() 的疊代器將產生值等於 container.end() 的疊代器。後續的 -- 應用程式會從最後一個元素開始反向導覽元素。

解參考疊代器

fbl:: 容器中的元素一律是物件,因此除了一元 * 運算子以外,這些元素也一律支援 -> 運算子。兩者都會根據疊代器是否為 const_iterator 產生 T&const T& (隨後會存取成員的 ->)。

嘗試延遲無效的疊代器是無效的,且會根據建構的性質觸發 ZX_DEBUG_ASSERT 或未定義的行為。

使用 container::make_iterator() 從物件建立疊代器

由於容器的干擾性質,您可以使用物件的現有參照來建立容器疊代器。例如,假設以索引鍵排序物件的樹狀結構,且接受物件的函式會在索引鍵序列中先傳回該物件的參照,這種編寫方式可能是:

using ObjectTree = fbl::WAVLTree<uint64_t, fbl::RefPtr<Object>>;

fbl::RefPtr<Object> FetchPrior(ObjectTree& tree, Object& obj) {
  ZX_DEBUG_ASSERT(obj.InContainer());
  auto iter = tree.make_iterator(obj);
  return (--iter).IsValid() ? iter.CopyPointer() : nullptr;
}

iterator::IsValid()

您可以使用疊代器執行個體本身的 IsValid 方法,測試所有 fbl:: 疊代器執行個體是否有效。透過這種方式測試疊代器是否有效,等同於測試 iter != container.end(),但 IsValid 方法可能會產生「不太」更有效率的程式碼,具體取決於編譯器的聰明程度,以及容器 end() 方法實作的瀏覽權限。

iterator::CopyPointer()

最後,fbl:: 疊代器會提供名為 CopyPointer 的方法,可用於產生容器所用指標類型的副本。對於原始指標的容器,這沒有任何特別之處。只是指向物件的 T* 副本。事實上,原始指標的 iter.CopyPointer() == &(*iter) 應一律為 true。

CopyPointer 不適用於具有獨特語意的代管指標。如果嘗試對使用 std::unique_ptr 追蹤的物件容器呼叫 CopyPointer,就會產生錯誤。

最後,在可複製代管指標的容器上執行 CopyPointer 時,系統會使用指標類型的複製建構函式,產生新的指標副本。換句話說,它會對物件產生新的代管參照。

如果嘗試對無效的疊代器呼叫 CopyPointer 至可複製指標類型,就會產生 nullptr