C 標準模板庫 STL 迭代器的原理與實現

2021-07-29 08:30:17 字數 4364 閱讀 9009

迭代器(iterator)是一種抽象的設計理念,通過迭代器可以在不了解容器內部原理的情況下遍歷容器。除此之外,stl中迭代器乙個最重要的作用就是作為容器(vector,list等)與stl演算法的粘結劑,只要容器提供迭代器的介面,同一套演算法**可以利用在完全不同的容器中,這是抽象思想的經典應用。

如下所示的**演示了迭代器是如何將容器和演算法結合在一起的,其中使用了三種不同的容器,.begin()和.end()方法返回乙個指向容器第乙個元素和乙個指向容器最後乙個元素後面乙個位置的迭代器,也就是說begin()和end()返回的迭代器是前閉後開的一般用[begin,end)表示。對於不同的容器,我們都使用同乙個accumulate函式,原因就在於accumulate函式的實現無需考慮容器的種類,只需要容器傳入的begin()和end() 迭代器能夠完成標準迭代器的要求即可。

std::vector

vec;

std::list

lis;

std::deque

deq;

std::cout

<< std::accumulate(vec.begin(), vec.end(), 0) << std::endl \

<< std::accumulate(lis.begin(), lis.end(), 0) << std::endl \

<< std::accumulate(deq.begin(), deq.end(), 0) << std::endl;

//6//15

//24

迭代器的作用就是提供乙個遍歷容器內部所有元素的介面,因此迭代器的內部必須儲存乙個與容器相關聯的指標,然後過載各種運算操作來方便遍歷,其中最重要的就是

∗ 運算子和->運算子,以及++,–等可能需要的運算子過載。實際上這和c++標準庫中的智慧型指標(smart pointer)很像,智慧型指標也是將乙個指標封裝,然後通過引用計數或是其它方法完成自動釋放記憶體的功能,為了達到和原有指標一樣的功能,也需要對*,->等運算子進行過載,下面參照智慧型指標實現了乙個簡單vector的迭代器,其中幾個typedef暫時不用管,我們後面會提到。veciter主要作用就是包裹乙個指標,不同容器內部資料結構不相同,因此迭代器操作符過載的實現也會不同。比如++操作符,對於線性分配記憶體的陣列來說,直接對指標執行++操作即可,但是如果容器是list就需要採用元素內部的方法,比如ptr->next()之類的方法訪問下乙個元素。因此,stl容器都實現了自己的專屬迭代器。

template

class veciter

item& operator*()const

item* operator->()const

//pre

veciter& operator++()

veciter operator++(int)

bool

operator==(const veciter &iter)

bool

operator!=(const veciter &iter)

};int main();

std::cout

<< std::accumulate(veciter(a), veciter(a + 4), 0);//輸出 10

}

我們都知道type_traits 可以萃取出型別的型別,根據不同型別可以執行不同的處理流程。那麼對於迭代器來說,是否有針對不同特性迭代器的優化方法呢?答案是肯定的。拿乙個stl演算法庫中的distance函式來說,distance函式接受兩個迭代器引數,然後計算他們兩者之間的距離。顯然對於不同的迭代器計算效率差別很大。比如對於vector容器來說,由於記憶體是連續分配的,因此指標直接相減即可獲得兩者的距離;而list容器是鏈式表,記憶體一般都不是連續分配,因此只能通過一級一級呼叫next()或其他函式,每呼叫一次再判斷迭代器是否相等來計算距離。vector迭代器計算distance的效率為o(1),而list則為o(n),n為距離的大小。

因此,根據迭代器不同的特性,將迭代器分為5類:

這五種迭代器的繼承關係如下所示。

了解了迭代器的型別,我們就能解釋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 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{};

回到distance函式,有了前面的基礎,我們可以根據不同迭代器種類實現distance函式。

template

inline

typename

std::iterator_traits::difference_type distance(inputiterator first, inputiterator last)

template

inline

typename

std::iterator_traits::difference_type __distance(inputiterator first, inputiterator last, std::input_iterator_tag)

return n;

}template

inline

typename

std::iterator_traits::difference_type \

__distance(inputiterator first, inputiterator last, std::random_access_iterator_tag)

int main();

std::vector

vec;

std::list

lis;

std::cout

<

<:endl>

std::cout

<< "list distance:"

<< wt::distance(lis.begin(), lis.end())<<:endl>

std::cout

<< "c-array distance:"

<< wt::distance(a,a + sizeof(a) / sizeof(*a)) << std::endl;

//輸出 vec distance:4

// list distance:4

// c-array distance:4

}

這裡通過stl 定義的iterator_traits模板可以將萃取不同種類的迭代器特性,iterator_traits還對指標和常量指標有特化版本,因此也可以萃取原生指標的特性。具體實現如下:

template

struct iterator_traits;

template

struct iterator_traits;

template

struct iterator_traits;

stl使用迭代器將演算法和容器結合,利用迭代器型別可以針對不同迭代器編寫更加高效的演算法,這裡一點很重要的思想就是:利用c++過載機制和引數推導機制將執行期決議問題提前到編譯期決議,也就是說,我們不需要在執行時判斷迭代器的型別,而是在編譯期就已經決定。這很符合c++模板程式設計的理念。在後續的stl學習中,我們會實現自己的各種容器,也必須實現各種各樣的迭代器,因此迭代器的學習還遠沒有停止。

《C 標準程式庫》 STL迭代器

所有容器有含有其各自的迭代器型別 iterator types 所以當你使用一般的容器迭代器時,並不需要含入專門的標頭檔案。不過有幾種特別的迭代器,例如逆向迭代器,被定義於中。迭代器共分為五種,分別為 input iterator output iterator forward iterator b...

C 標準模板庫STL

stl是標準c 庫的一部分。stl模板類為c 提供了完善的資料結構和演算法。stl的特點 型別引數化 即stl的 中可處理任意自定義型別的物件。泛型程式設計 generic programming 它以模板為基礎,弱化了 實體型別的差異,簡化了程式設計時問題抽象的模型,提供了更好的 封裝性和彈性。s...

C 標準模板庫STL

stl 標準模板庫 包括容器,演算法,迭代器 容器用來儲存資料,比如vector,list,堆疊等,string也算 一共有八個 演算法就是對容器進行操作,比如增刪改查資料 迭代器用來遍歷容器itreator 用指標的方式來遍歷容器的資料 注 平時使用的時候大部分時候我們都用上了,但是面試的時候不能...