STL小結 仿寫TinySTL專案有感

2022-10-07 21:45:11 字數 3247 閱讀 9731

六大元件:容器(類模板)、演算法(函式模板)、迭代器(類模板,設計模式)、配置器(類模板)、配接器(容器介面卡、函式配接器)、仿函式(類或類模板)。整個專案採用大多的是泛型程式設計的思想(模板機制)。使得多種資料型別在同乙個演算法或結構上都可以操作,在編譯器就確定聚類資料型別。

(一)配置器

一般new都是要先申請空間,再在空間構造物件。delete是要先析構物件,再釋放空間。

專案為了更好的分工,把物件的構造和析構(construct和destroy)、空間的申請和釋放(malloc和自由鍊錶)分成兩大塊。

一級配置器(class __malloc_alloc_template):呼叫malloc和realloc,如果不成功,改呼叫oom_malloc和oom_realloc(丟擲異常或exit(1))。

二級配置器(class __default_alloc_template):16個自由鍊錶

記憶體配置過程:小於128bytes就找16個自由鍊錶裡面合適的那個鍊錶(上調到8的倍數)還有沒有,有就直接返回。如果沒有,就向記憶體池申請所需塊的大小掛到自由鍊錶(refill/chunk_alloc(),可能獲得20個新節點,也可能小於20個)。如果還是沒有,使用malloc從堆中申請,申請的大小是需求的兩倍(一倍放在自由鍊錶,一倍放在記憶體池)。如果malloc還是失敗,那再從剩下的自由鍊錶找找。實在不行就bad_alloc異常。

(二)迭代器。

精髓在於trait思想。trait可以被稱為萃取機,通過它來獲取迭代器的一些資訊,在stl裡面發揮巨大作用。

template

struct

iterator_traits

//下面針對原生指標。通過類模板的特化來支援原生指標。(模板偏特化)

template

struct iterator_traits<_tp>

可以通過iterator_traits::itereator_category獲取迭代器的資訊,如果要直接使用typename後面的那個簡單一點的iterator::iterator_type來獲取迭代器的資訊,前提是迭代器必須是乙個類。但是實際上並不是所有的迭代器都是乙個類,原生指標也是一種迭代器,但是原生指標不是類,所以沒法定義內嵌型別typeof。

迭代器類別有五種。input_iterator是唯讀的,支援==/!=/++/*/->等操作。output_iterator是只寫的,支援++/*等操作。

forward_iterator是單向一步的,可讀寫。bidirection_iterator是雙向一步的。randomaccess_iterator是隨機多步的。

(三)容器和介面卡

stl容器有:vector、list雙向鍊錶、deque、map(unordered/multi)、set(unordered/multi)

介面卡是將乙個類的介面轉換成客戶希望的另乙個介面。比如把香港插頭轉成內地插頭。

stl容器介面卡有:stack(可以使用deque做底層),queue(可以使用deque做底層),heap(平時雖然都是靜態陣列,但可以使用vector動態的來實現),priority_queue(使用vector[容器]和heap[push和pop演算法]做底層),slist是單向鍊錶。

序列式容器是通過元素在容器的位置,順序儲存和訪問元素。關聯式容器時通過鍵key來儲存和讀取元素。

分別從每個容器的迭代器、建構函式、屬性獲取來展開介紹。

(1)關於vector。

線性儲存結構。需要三個迭代器分別指向資料的頭(start)、資料的尾(finish)、陣列的尾(end_of_storage)。

屬性end()返回的是finish。

push_back函式,插入新元素的時候,會先檢查是否有備用空間,如果有就直接在備用空間構造元素,並調整迭代器finish。如果沒有備用空間,就要呼叫insert_aux函式擴充空間(重新配置-移動資料-釋放原空間)。pop_back函式就是直接移動finish迭代器。

erase函式可以刪除[first, last)範圍中的所有元素,也可以刪除指定迭代器位置的元素。刪除範圍內的元素,第一步要將finish迭代器後面的元素拷貝,然後返回拷貝完成的尾部迭代器,最後再刪除。刪除指定位置的就是將指定位置後面所有元素向前移動,最後析構最後乙個元素。

insert函式分成三種情況。如果備用空間足夠而且插入點後面的現有元素多於新增元素;如果備用空間足夠而且插入點後面的現有元素少於新增元素;如果備用空間不夠。

由於元素空間重新配置會導致之前的迭代器失效,比如插入元素或者刪除元素。

優點:動態擴容,便於隨機訪問,節省空間。

缺點:插入刪除的時間複雜度高,只能在末端pop和push,重分配麻煩。

(2)關於list。

雙向鍊錶,鏈式儲存結構。有pre和next兩個指標。可以頭插和尾插。插入刪除就是雙向鍊錶的。

優點:插入刪除方便,兩端可push和pop。

缺點:不支援隨機訪問,相比vector占用空間多。

(3)關於deque。

線性儲存+鏈式儲存(分段連續空間)。邏輯上也是線性儲存。和vector的區別在於,deque可以在常數時間內插入刪除(頭尾都可以),但是deque沒有容量概念,因為它是動態以分段連續空間組合而成,隨時可以增加一塊新的空間並拼接起來。

中控器:採用雜湊(指標,緩衝區)作為deque的儲存空間主體。其中緩衝區就是另外一段比較大的連續線性空間。每個緩衝區都設計了三個迭代器(cur表示當前所指的位置,first表示當前陣列中頭的位置,last表示當前陣列中尾的位置)

優點:隨機訪問方便,插入刪除方便,可在兩端push和pop。

缺點:因為涉及比較複雜,採用分段連續空間,所以占用記憶體多。

(4)關於map和set。 只有前面帶unordered的才是雜湊表實現的,否則一定是紅黑樹實現的(因為要排序)

紅黑樹特性:每個節點都是紅色或者黑色。根節點一定是黑色。紅節點的左右一定是黑色。每個葉子節點一定是黑色。(三個一定黑)。從某節點到其所有葉子節點都包含相同數量的黑色節點。

應用於linux系統的網路epoll呼叫、虛擬記憶體管理、stl的set和map。

鍵是不能改變的,因為紅黑樹的排序的,如果改變鍵的話整棵樹都亂了。

如果字首帶multi就是允許鍵重複,那就採用insert_equal,而不是insert_unique。

(三)演算法

resize是直接改變容器內元素的數量,賦初始值。

reverse只是改變容器的最大容量,不會放值進去。如果reverse的容量大於之前的,那麼會重新分配一塊能存len個物件的空間,然後把之前的物件複製過來,再銷毀之前的記憶體。

STL之仿函式

這裡介紹仿函式,謂詞,內建函式物件,介面卡的用法 仿函式 又稱函式物件,實際上是過載操作符 其中不定義構造和析構函式 只有乙個引數是一元仿函式,兩個就是二元 eg class func intmain 優點 函式物件超越了普通函式的概念,可以在內部儲存其狀態 比如呼叫次數 函式物件可以作為引數呼叫 ...

仿射affine 小結

hom mat2d identity hommat2didentity 生成乙個同質2d轉換矩陣。平移仿射 hom mat2d translate hommat2didentity,100,100,hommat2dtranslate 矩陣平移變換,形成新的矩陣hommat2dtranslate af...

JokeClient Swift 仿寫學習

在init關鍵字後面新增問號 init?可失敗構造器會建立乙個型別為自身型別的可選型別的物件。你通過return nil語句來表明可失敗構造器在何種情況下應該 失敗 struct animal self.species species 1 從派生類轉換為基類,向上轉型 upcasts class a...