C STL之迭代器介紹 原理 失效

2021-10-22 07:16:46 字數 4775 閱讀 3751

今天抽空來看看c++標準庫中迭代器的相關知識。

我們知道,stl標準庫一共有六大部件:分配器、容器、迭代器、演算法、仿函式、介面卡。其中,迭代器就是用來「聯結」演算法、仿函式與容器的紐帶。除此之外,在設計模式中有一種模式叫迭代器模式,簡單來說就是提供一種方法,在不需要暴露某個容器的內部表現形式情況下,使之能依次訪問該容器中的各個元素,這種設計思維在stl中得到了廣泛的應用,是stl的關鍵所在,通過迭代器,容器和演算法可以有機的粘合在一起,只要對演算法給予不同的迭代器,就可以對不同容器進行相同的操作。(參考:

比如下面這個find函式,展示了容器、演算法和迭代器如何合作:

template

<

typename inputiterator,

typename t>

inputiterator find

(inputiterator first, inputiterator last,

const t &value)

上述的find函式,只需要傳遞容器的迭代器,就可以實現對不同的容器實現相同的演算法,這其實是一種泛型程式設計的思想。

我們來看看在vector中對於iterator的實現:

template

<

typename t,

class

alloc

= alloc >

class

vector

;

在此可以看到iterator在vector中也只是簡單的被定義成了我們傳入的型別引數t的指標。

#ifndef cpp_primer_my_list_h

#define cpp_primer_my_list_h

#include

template

<

typename t>

class

node

node

(t val, node *p =

nullptr):

value

(val)

,next

(p)}

;template

<

typename t>

class

my_list

//過載++、--、*、->等基本操作

//返回引用,方便通過*it來修改物件

t &operator*(

)const

node

*operator

->()

const

list_iterator &

operator++(

) list_iterator operator++(

int)

bool

operator==(

const list_iterator &t)

const

bool

operator!=(

const list_iterator &t)

const};

public

:typedef list_iterator iterator;

//型別別名

my_list()

//從鍊錶尾部插入元素

void

push_back

(const t &value)

else

size++;}

//列印鍊錶元素

void

print

(std::ostream &os = std::cout)

const

public

://操作迭代器的方法

//返回鍊錶頭部指標

iterator begin()

const

//返回鍊錶尾部指標

iterator end()

const

//其它成員函式 insert/erase/emplace};

#endif

//cpp_primer_my_list_h

對於其他的容器迭代器分析,暫略。

在stl中,除了原生指標以外,迭代器被分為五類:

input iterator

顧名思義,input——此迭代器不允許修改所指的物件,即是唯讀的。支援==、!=、++、*、->等操作。

output iterator

允許演算法在這種迭代器所形成的區間上進行只寫操作。支援++、*等操作。

forward iterator

允許演算法在這種迭代器所形成的區間上進行讀寫操作但只能單向移動,每次只能移動一步。支援input iterator和output iterator的所有操作。

bidirectional iterator

允許演算法在這種迭代器所形成的區間上進行讀寫操作,可雙向移動,每次只能移動一步。支援forward iterator的所有操作,並另外支援–操作。

random access iterator

包含指標的所有操作,可進行隨機訪問(vector容器支援),隨意移動指定的步數。支援前面四種iterator的所有操作,並另外支援it + n、it - n、it += n、 it -= n、it1 - it2和it[n]等操作。

上述五種迭代器的分類和聯絡可參考下圖:

了解了迭代器的型別,我們就能解釋vector的迭代器和list迭代器的區別了。顯然vector的迭代器具有所有指標算術運算能力,而list由於是雙向鍊錶,因此只有雙向讀寫但不能隨機訪問元素。**故vector的迭代器種類為random access iterator,list 的迭代器種類為bidirectional iterator。**我們只需要根據不同的迭代器種類,利用traits程式設計技巧萃取出迭代器種類,然後由c++的過載機制就能夠對不同型別的迭代器採用不同的處理流程了。為此,對於每個迭代器都必須定義型別iterator_category,也就是原始碼中的typedef std::forward_iterator_tag iterator_category;實際中可以直接繼承stl中定義的iterator模板,模板後三個引數都有預設值,因此繼承時只需要指定前兩個模板引數即可。如下所示,stl定義了五個空型別作為迭代器的標籤:

template

<

class

category

,classt,

class

distance

= ptrdiff_t,

class

pointer

=t*,

class

reference

=t&>

class

iterator

;struct input_iterator_tag

;struct output_iterator_tag

;struct forward_iterator_tag:

public input_iterator_tag

;struct bidirectional_iterator_tag:

public forward_iterator_tag

;struct random_access_iterator_tag:

public bidirectional_iterator_tag

;

當使用乙個容器的insert或者erase函式通過迭代器插入、刪除或者修改元素(如map、set,因為其底層是紅黑樹)"可能"會導致迭代器失效,因此為了避免危險,應該重新獲取的新的有效的迭代器進行正確的操作。plus

vector

1、插入操作(insert)和接合操作(splice)不會造成原有的list迭代器失效,這在vector中是不成立的,因為vector的插入操作可能造成記憶體重新配置,導致所有的迭代器全部失效。

2、list的刪除操作(erase)也只有指向被刪除元素的那個迭代器失效,其他迭代器不受影響。(list目前只發現這一種失效的情況)

關聯容器

對於關聯容器(如map, set,multimap,multiset),刪除當前的iterator,僅僅會使當前的iterator失效,只要在erase時,遞增當前iterator即可。這是因為map之類的容器,使用了紅黑樹來實現,插入、刪除乙個結點不會對其他結點造成影響(雖然刪除了乙個元素,整棵樹也會調整,以符合紅黑樹或者二叉樹的規範,但是單個節點在記憶體中的位址沒有變化,變化的是各節點之間的指向關係)。erase迭代器只是被刪元素的迭代器失效,但是返回值為void,所以要採用**erase(iter++)(分三步走,先把iter傳值到erase裡面,然後iter自增,然後執行erase,所以iter在失效前已經自增了)**的方式刪除迭代器。

C STL 迭代器失效

2 刪除 當進行刪除操作 erase,pop back 後,指向刪除點的迭代器全部失效 指向刪除點後面的元素的迭代器也將全部失效。刪除點之前的迭代器仍有效。二 deque迭代器的失效情況 1 插入 1 在deque容器首部或者尾部插入元素不會使得任何迭代器失效。但是指向存在的元素的引用和指標不會失效...

C STL迭代器失效

迭代器失效就是說,對容器進行了一些操作後,先前的迭代器無法進行解引用操作去訪問容器的元素。迭代器失效可能會造成程式崩潰,如下圖 如果插入元素導致vector達到最大容量,那麼會重新分配記憶體並將老的元素拷貝到新的記憶體中。元素的位址都改變了,顯然迭代器和引用都將失效 如果插入元素沒有引起重新分配記憶...

C STL 迭代器失效問題

之前看 c primier 的時候,也解到在順序型視窗裡insert erase會涉及到迭代器失效的問題,並沒有深究。今天寫程式的時候遇到了這個問題。最初我的程式是醬紫的,別說話,我知道這樣是有問題的,可這樣是最直觀的想法 int arr vector int a arr,arr sizeof ar...