截至目前為止,已提供的範例都顯示了許多 fbl::
疊代器的用法。fbl::
中的疊代器使用的 API 與 std::
容器中使用的 API 非常類似,因此希望這對您來說非常熟悉。但是,除了說明所有 fbl::
疊代器支援的項目,還有與 std::
疊代器稍微不同的部分,值得花一點時間說明。
iterator
和 const_iterator
與 std::
疊代器相同,fbl::
疊代器分為兩種:非常數和常數版本。如果使用者的容器參照不是 const
,begin()
或 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
都支援透過 --
運算子進行雙向疊代,無論在其套用前和後置後表單中都能支援雙向疊代。如果 SinglyLinkedList
和 HashTable
具有單獨連結的清單值區,則不會。
進階疊代器超過容器結尾時,系統會提供 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
。