STL的迭代器和型別萃取

2022-07-11 08:06:08 字數 3332 閱讀 4289

今天就可以把stl庫中迭代器的實現,和型別萃取好好整理一下了

迭代器的設計思維是stl的關鍵所在,在stl的實際運用和泛型思維,迭代器都扮演著十分重要的角色,stl力求把資料容器和演算法的概念分開來,於是就有了stl的兩大部分,容器(container)和泛型演算法(algorithms),泛型演算法有很多引數都是迭代器。

舉乙個栗子!泛型演算法find()的實現!

1 template

2 inputiterator find(inputiterator first, inputiterator last , const t&value)38

return

first;

9 }

接受兩個輸入迭代器的引數

之前發布的智慧型指標也算是迭代器的一種,原生的指標當然也可以說是迭代器,只是這麼說有些狹隘了迭代器的定義,因為自定義型別的指標功能不夠完善,需要過載一些運算子之類的,為什麼智慧型指標也不能直接用作迭代器呢?因為無法獲得智慧型指標所指向的型別!

為什麼這麼說?因為有的時候我們可能想要使用智慧型指標所指向的型別去建立一些變數,不好弄,即使是使用type_id也只是獲得名字,並不能用來建立變數,有種方法可以用的

1 template23

void

fun_impl(t t ,u u)45

10 template

1112

void

fun(t t)

1314

這麼做就可以獲得所指向的型別(並且建立了想要的變數)了,但是當我們需要乙個這樣的返回值的時候應該怎麼辦嘛,也許還是上面這種方法?看似是那麼的完美,但是編譯之後就無情的擊碎了你的希望,編譯是不通過的

但是我們可以通過內嵌型別宣告來獲取他們的型別(通過typeid是不行的,因為typeid只能獲得型別的名稱,並不能用這個名稱來宣告變數)

這種方法顯然是可以的,但是有個隱晦的陷阱,因為不是所有傳過來的都是迭代器,萬一傳過來的就是乙個原生指標,原生指標就沒有內嵌型別(當然沒有,你認為乙個int*內部會有內嵌型別麼,話說int*有什麼內部麼)!!!!這就要用到偏特化的知識

那什麼是偏特化呢?使用模板的時候,模版引數設定為多個template模板是可以特定的

1 template

2class

class13;

7 template

8class class1

9;

上述**中對u進行了特化,嗯!簡單易懂,如果t也特化一下,就是全特化了,偏特化就是沒有全都特化!

但是還有乙個問題,當傳過來的的指標是一種指向常值的指標(pointers to const)const int *ptr的時候,,我們很顯然獲得的型別就是const int,這有些不對勁,因為我們得到的型別拿來宣告臨時變數是沒有辦法使用的,就因為他指向了常值,很簡單,再拿取偏特化一下就可以了(就是在上圖中處改成)

traits就扮演了乙個型別萃取機的角色,用來萃取出迭代器所指向的型別(型別萃取?!)

為什麼要有這麼多的迭代器呢?你看容器人手乙個!因為迭代器中有operator++的實現,每個容器儲存的方式也不盡相同,vector是順序儲存,list是鏈式儲存,operator++內部實現肯定不一樣的,但是又想泛型程式設計,繼承咯,封裝咯。。。

那麼我接下來就講講iterator_category的相關吧

iterator_category幹啥的?對下圖中五種迭代器進行分類標識

這張圖中的箭頭並不是代表繼承關係,而是概念強化關係,就是說下層一定是個上層,但上層絕不是個下層,就跟正方形是矩形的一種特殊一樣,但是矩形就不是正方形

1

struct

input_iterator_tag {};

2struct

output_iterator_tag {};

3struct forward_iterator_tag:public

input_iterator_tag {};

4struct bidirectional_iterator_tag :public

forward_iterator_tag {};

5struct random_access_iterator_tag :public bidirectional_iterator_tag {};

這是五種空殼結構體,沒其他用處就是用來標識迭代器型別,用來啟用函式過載機制(因為泛型演算法中要求的引數都會只給最上層的型別,這樣呼叫時,下層迭代器來了也接受)

為什麼會有繼承?泛型演算法是支援上層迭代器作為引數傳入底層迭代器的形參的!就是在這裡用的,因為迭代器本身沒有繼承,所以用標識名繼承來啟用過載機制

例如advance(iterator ,n)泛型函式,它的作用是將傳過來的迭代器往後走n個,每種迭代器的實現肯定不一樣的,像是forward_iterator只能乙個乙個走,走n次,bidirectional_iterator也一樣,random_access_iterator就可以一下走n個,如果寫成乙個,肯定會影響random_access_iterator的效率,所以分開寫,那麼我怎麼知道改呼叫哪個函式呢?因為我想泛型程式設計(就是我懶,我想省事)肯定就想只寫乙個函式名,傳個引數,讓程式幫我選擇呼叫哪個函式(我的天!這不就是函式過載麼),但是程式需要知道我傳的引數是哪種型別的迭代器(就是型別萃取呢!!!)把型別從迭代器中萃取出來!

(沒弄全,其他的類似)

引數列表中最後乙個就是剛才建立的那五個空殼結構體!!呼叫_advance的時候最後乙個引數給迭代器的iterator_category就行了,對!沒錯就這麼用!讓程式自己判斷用哪個函式!!

具體實現:呼叫

1 template 

2 inline void advance(inputiterator&i,distance n)

3

1 template

2struct

iterator_traits

3;

模擬實現vector 迭代器,型別萃取

vector是c 標準庫中的部分內容,是乙個多功能,能夠操作多種資料機構和演算法的模板類和函式庫。vector被認為是乙個容器,是因為它能像容器一樣存放各種型別的物件,能夠存放任意型別的動態陣列,能夠增加和壓縮資料。適合用順序表實現 include include using namespace s...

STL 原始碼分析之萃取器

在stl中迭代器控制演算法,演算法操作容器。但是,泛型程式設計中演算法並不知道迭代器傳遞給他的是什麼樣的型別,所以在迭代器和演算法之間需要乙個東西來分配型別。這東西就是萃取器。這是一種程式設計的技法。下面上圖後直接上模擬的萃取器 並且說明原理。下面上模擬 都備註好了。includeusing nam...

STL 鍊錶和迭代器

include include include include include include include include 迭代器 空間配置器 using namespace std 鍊錶實現 namespace my 構造 template class t1,class t2 void con...