2012-09-23 153 views
14

STL容器有一個用於選擇自定義分配器的模板參數。這花了一段時間,但我想我明白它是如何工作的。不知何故,它不是很好,因爲給定的分配器類型不是直接使用,而是反彈到另一種類型的分配器。最後我可以使用它。STL容器:構造函數的分配器參數和作用域分配器

讀完API之後,我意識到也有可能將分配器作爲構造器參數。但是,我怎麼知道容器使用哪種分配器,如果它從模板參數內部重新綁定給定的分配器?

此外,我讀了C++ 11現在使用範圍分配器,它允許重複使用容器的分配器來容納其容器。範圍化分配器啓用容器的實現與不知道範圍化容器的實現有何區別?

不幸的是,我無法找到任何可以解釋這一點的東西。感謝您的回答!

回答

13

但是我怎麼知道容器使用哪種分配的,如果它 內部從重新綁定模板參數給定的分配?

始終提供一個Allocator<T>給構造(其中T是容器的value_type)。該容器將它轉換爲Allocator<U>是必要的,其中U是容器的一些內部數據結構。所述Allocator需要提供這樣的轉換構造,例如:

template <class T> class allocator { 
    ... 
    template <class U> allocator(const allocator<U>&); 

此外我讀C++ 11現在使用範圍的分配器,其允許 重用容器的分配器爲包含它的容器。

好,更精確地說,C++ 11有一個名爲scoped_allocator_adaptor分配器適配器

template <class OuterAlloc, class... InnerAllocs> 
class scoped_allocator_adaptor : public OuterAlloc 
{ 
    ... 
}; 

從C++ 11:

類模板scoped_allocator_adaptor是一個分配器模板 ,它指定一個容器使用的內存資源(外部分配器)(與任何其他分配器一樣)並指定一個內部的 分配器資源被傳遞給容器內的每個元素 的構造器。該適配器實例化爲一個外部和零個或多個內部分配器類型。如果只有一個 分配器類型實例化,所述內分配器成爲 scoped_allocator_adaptor本身,從而使用用於容器相同的分配器 資源和所述容器內的每個元件和,如果 元素本身是CON組tainers,每個它們的元素遞歸地爲 。如果使用多個分配器實例化,則第一個分配器是容器使用的外部分配器,第二個分配器將被傳遞給容器元素的構造函數 ,如果元素本身是容器,則第三個分配器是 傳遞給元素的元素,依此類推。如果將容器嵌套 的深度大於分配器的數量,則最後一個分配器 會在單分配器情況下重複使用,對於任何剩餘的 遞歸。 [注意scoped_allocator_adaptor源自 外部分配器類型,所以它可以代替大多數表達式中的外部分配器 類型。 - 注完]

所以,只有當您指定一個scoped_allocator_adaptor作爲分配器爲您的容器得到作用域分配器行爲。

作用域分配器啓用容器 的實現如何與不知道作用域容器的實現大致不同?

關鍵是容器現在通過一個名爲allocator_traits的新類來處理它的分配器,而不是直接處理分配器。而容器必須使用allocator_traits進行某些操作,例如在容器中構建和銷燬value_type。容器不得直接與分配器通信。

例如,分配器可以提供一種構件稱爲construct將使用給定的參數在一定的地址構造類型:

template <class T> class Allocator { 
    ... 
    template<class U, class... Args> 
     void construct(U* p, Args&&... args); 
}; 

如果分配器不提供此構件,allocator_traits將提供一個默認實現。在任何情況下,容器必須構建使用所有value_type s此construct功能,但使用它通過allocator_traits,而不是使用allocator直接:

allocator_traits<allocator_type>::construct(the_allocator, *ugly details*); 

scoped_allocator_adaptor定製提供construct功能,這allocator_traits將轉發到利用uses_allocator特性並將正確的分配器傳遞給value_type構造函數。容器仍然對這些細節無知。容器只需知道它必須使用allocator_traits construct函數構造value_type

容器必須處理的更多細節才能正確處理有狀態的分配器。雖然這些細節也通過讓容器不作任何假設來處理,但通過allocator_traits獲得所有屬性和行爲。該容器甚至不能假定pointerT*。而是通過詢問allocator_traits發現它是什麼。

簡而言之,要構建一個C++ 11容器,請研究allocator_traits。然後,當客戶使用scoped_allocator_adaptor時,您可以免費獲得有限範圍的分配器行爲。

4

容器使用的分配器的類型由其構造函數參數定義:它恰好是容器的構造函數中預期的這種類型。但是,任何分配器都需要能夠提供不同於它所定義的類型的類型。例如,對於std::list<T, A>,分配器預期能夠分配T對象,但它永遠不會用於分配這些對象,因爲std::list<T, A>實際上需要分配節點。也就是說,分配器將被反彈以分配不同的類型。不幸的是,這使得使用分配器來服務特定類型變得很困難:您不知道分配器實際將提供的類型。

對於作用域分配器,它的工作非常簡單:容器確定它是否具有構造函數採用匹配的分配器的任何成員。如果是這種情況,它將重新綁定它使用的分配器並將此分配器傳遞給成員。不直截了當的是確定是否正在使用分配器的邏輯。爲了確定一個成員是否使用分配器,使用std::uses_allocator<T, A>的特徵:它確定T是否具有嵌套的typedef allocator_type哪些以及如果A可以轉換爲這種類型。 20.6.7.2 [allocator.uses.construction]中描述了構造成員對象的規則。

實際上,這意味着分配器對於處理容器及其成員使用的池很有用。在某些情況下,當分配類似大小的對象時,它也可能合理。對於任何基於節點的容器,都要保留一個相同大小的對象池。但是,從與分配器一起使用的模式(如節點或包含的某些字符串)來看,沒有必要清楚。此外,由於使用不同的分配策略會改變類型,因此似乎最合理的是堅持默認分配或使用分配器類型,該分配器類型是實際定義分配策略的多態分配器的代理。當然,當你擁有有狀態的分配器時,你可能擁有不同分配器的對象,例如swap()他們可能無法正常工作。