C++STL學習筆記(4) 分配器(Allocator)

在前面的博客《C++ STL學習筆記(3) 分配器Allocator,OOP, GP簡單介紹》中,簡單的介紹了分配器再STL的容器中所擔當的角色,這一節對STL六大部件之一的分配器進行詳細的學習,從STL源代碼的角度去理解分配器的具體細節:

operator new 和malloc

在不同的平臺上調用C++中的operator new,最終都會是去調用malloc去分配內存,而malloc又會根據不同的操作系統,去調用相應的API分配內存。malloc分配的內存長什麼樣子?

其中,size是申請的內存的大小,但是malloc會在此基礎上搭配其他的東西,然後將內存地址返回。

灰色部分表示: 在debug_mode 模式下會添加的東西

紅色部分表示: 在上下兩部分添加的cookie,記錄的是分配的內存的大小,因爲在free的時候,只是傳入了指針,而free就是通過這個區域獲得要釋放的內存的大小。

綠色部分表示:加上上述部分之後調整到某個邊界(這部分只是大概提了一下)

從上圖可以看出,malloc實際上返回給我們的東西多餘我們申請的東西。

VC6中allocator的實現:

例如,調用allocator分配內存:

int* p = allocator<int>().allocate(512, (int*)0);   // 創建一個臨時對象 分配內存
allocator<int>().deallocate(p, 512);                // 釋放內存

Allocator對於內存的分配會有額外的開銷,如上面上所述,實際分配的內存大於所需的內存(添加了額外的部分),Gnu2.9所附的標準庫中,有一個實現更優的分配器alloc,它的行爲模式如下:

Gnu的分配器爲了實現節省內存,他需要減少malloc的次數,所以設計了16條鏈表,每條鏈表負責不同大小的區塊,第0條鏈表負責的是8字節大小區塊,第一條負責16字節大小的區塊,以此類推....所以在容器需要內存的時候,內存中元素的大小會被分配器調整到8的倍數,然後再具體確定選擇哪一條鏈表。

分配器主要是爲容器服務/

容器結構與分類:

適配器的理解:
deque --> stack, queue相當於deque的適配器,因爲stack, queue中用到了deque,所以將它們稱爲適配器更加合適。

1. 容器list

節點的設計:

關於迭代器:
扮演指針的功能,定義爲一個類:

對於vector,array這種連續內存的容器的迭代器,和其他內存不連續的對象的迭代器,是有一定的區別的。除此之外迭代器需要重載->, *, ++, --等運算符。

Iterator需要遵循的原則:

traits: 人爲設計的一種萃取機,萃取出特徵,有type_traits, character_traits, pointer_traits,  那麼iterator_traits又是什麼呢? 
所以iterator_traits的作用是萃取出iterator的特徵,那麼iterator有什麼特性呢?

前面講過,iterator是算法和容器之間的橋樑,它讓算法知道要處理的元素的範圍,在算法知道要處理的元素的範圍後,算法需要對元素進行一些操作,在這些操作的過程中,算法很可能需要知道這個iterator有哪些性質,從而選擇一個最佳化的動作對元素進行操作,例如:
舉個例子,有一個rotate函數,

可以看到rotate 函數調用了std::_rotate 和std::_iterator_category

可以看到std::_iterator_category的定義:

可以看到std::_iterator_category中調用了iterator_traits<Iter>::iterator_category,可以看到這是要萃取迭代器Iter的iterator_category特徵,也就是迭代器的分類(關於迭代器的分類前面講過,有輸入迭代器,輸出迭代器,前向迭代器,雙向迭代器,以及random access迭代器)。

Input iterator(輸入迭代器) 讀,不能寫 只支持自增運算
Output iterator(輸出迭代器) ; 寫,不能讀 只支持自增運算
Forward iterator(前向迭代器)  讀和寫; 只支持自增運算
Bidirectional iterator(雙向迭代器) ; 讀和寫 支持自增和自減運算
Random access iterator(隨機訪問迭代器)  讀和寫; 支持完整的迭代器算術運

(查了一下我沒記錯,就是這五種)

所以說,rotate算法知道了迭代器的分類,以便他能夠採取最佳的操作方式,

接着看std::_rotate函數的定義:

可以看到,這裏也調用了iterator_traits,分別獲迭代器(random access)的value_type, 以及difference_type

value_type: iterator所指向的元素的類型

difference_type: 兩個迭代器之間的距離,應該用什麼數據類型來表現

(C++標準庫中,共有五種迭代器特性,除了上面的terator_category, value_type, value_type, 還有reference_type以及pointer_type兩種特徵, 稱之爲迭代器的associative types,相關類型 , 作爲一個迭代必須設計出這五種特性,以便能夠回答算法的提問。)

可以看到,在rotate算法中,它需要知道iterator的三個特徵。

舉一個例子,list的迭代器的實現如下所示:

可以看到。它實現了迭代器的5種associative types, 能夠滿足算法的提問。

算法中對iterator的五種類型的獲取:

如果算法接收到的迭代器不是class,而是一個native pointer(也就是C++中的自然指針,這也是C++迭代器,退化的迭代器),那麼此時迭代器是如何回答算法的提問的呢?

C++通過添加一箇中間層(萃取機)來解決這一問題:(侯捷老師說:解決計算機問題的尚方寶劍:添加一箇中介層)

所以iterator_traits用來分離class_iterators和non_class_iterators:

可以看到iterator的定義如下,出了泛化的定義之外,還有特化的版本:

從上面的iterator_traits的定義可以看到它作爲一箇中間層的分離功能。

可以順便看一下迭代器完整的定義:

在C++ STL中,還有其他的各種Traits

關於traits的知識就介紹到這裏

---------------------------------------------------------------分割線-----------------------------------------------------------------

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章