C++中std::allocator的使用

標準庫中包含一個名為allocator的類,允許我們將分配和初始化分離。使用allocator通常會提供更好的效能和更靈活的記憶體管理能力。

new有一些靈活性上的侷限,其中一方面表現在它將記憶體分配和物件構造組合在了一起。類似的,delete將物件析構和記憶體釋放組合在了一起。我們分配單個物件時,通常希望將記憶體分配和物件初始化組合在一起。因為在這種情況下,我們幾乎肯定知道物件應有什麼值。當分配一大塊記憶體時,我們通常計劃在這塊記憶體上按需構造物件。在此情況下,我們希望將記憶體分配和物件構造分離。這意味著我們可以分配大塊記憶體,但只在真正需要時才真正執行物件的建立操作(同時付出一定開銷)。一般情況下,將記憶體分配和物件構造組合在一起可能會導致不必要的浪費。

標準庫allocator類定義在標頭檔案memory中,它幫助我們將記憶體分配和物件構造分離開來。它提供一種型別感知的記憶體分配方法,它分配的記憶體是原始的、未構造的。類似vector,allocator是一個模板。為了定義一個allocator物件,我們必須指明這個allocator可以分配的物件型別。當一個allocator物件分配記憶體時,它會根據給定的物件型別來確定恰當的記憶體大小和對齊位置。allocator支援的操作,如下:

C++中std::allocator的使用

allocatro分配的記憶體是未構造的(unconstructed)。我們按需要在此記憶體中構造物件。在新標準庫中,construct成員函式接受一個指標和零個或多個額外引數,在給定位置構造一個元素。額外引數用來初始化構造的物件。類似make_shared的引數,這些額外引數必須是與構造的物件的型別相匹配的合法的初始化器。

在早期版本的標準庫中,construct只接受兩個引數:指向建立物件位置的指標和一個元素型別的值。因此,我們只能將一個元素複製到未構造空間中,而不能用元素型別的任何其它建構函式來構造一個元素。還未構造物件的情況下就使用原始記憶體是錯誤的。為了使用allocator返回的記憶體,我們必須用construct構造物件。使用未構造的記憶體,其行為是未定義的。

當我們用完物件後,必須對每個構造的元素呼叫destroy來銷燬它們。函式destroy接受一個指標,對執行的物件執行解構函式。我們只能對真正構造了的元素進行destroy操作。一旦元素被銷燬後,就可以重新使用這部分記憶體來儲存其它string,也可以將其歸還給系統。釋放記憶體透過呼叫deallocate來完成。我們傳遞給deallocate的指標不能為空,它必須指向由allocate分配的記憶體。而且,傳遞給deallocate的大小引數必須與呼叫allocate分配記憶體時提供的大小引數具有一樣的值。

標準庫還為allocator類定義了兩個伴隨演算法,可以在未初始化記憶體中建立物件。它們都定義在標頭檔案memory中,如下:

C++中std::allocator的使用

在C++中,記憶體是透過new表示式分配,透過delete表示式釋放的。標準庫還定義了一個allocator類來分配動態記憶體塊。分配動態記憶體的程式應負責釋放它所分配的記憶體。記憶體的正確釋放是非常容易出錯的地方:要麼記憶體永遠不會被釋放,要麼在仍有指標引用它時就被釋放了。新的標準庫定義了智慧指標型別————shared_ptr、unique_ptr和weak_ptr,可令動態記憶體管理更為安全。對於一塊記憶體,當沒有任何使用者使用它時,智慧指標會自動釋放它。現代C++程式應儘可能使用智慧指標。

std::allocator是標準庫容器的預設記憶體分配器。你可以替換自己的分配器,這允許你控制標準容器分配記憶體的方式。

以上內容主要摘自:《C++Primer(Fifth Edition 中文版)》第12。2。2章節

下面是從其他文章中copy的測試程式碼,詳細內容介紹可以參考對應的reference:

C++中std::allocator的使用#include “allocator。hpp”#include #include #include #include namespace allocator_ { ////////////////////////////////////////////////// reference: C++ Primer(Fifth Edition) 12。2。2int test_allocator_1(){ std::allocator alloc; // 可以分配string的allocator物件 int n{ 5 }; auto const p = alloc。allocate(n); // 分配n個未初始化的string auto q = p; // q指向最後構造的元素之後的位置 alloc。construct(q++); // *q為空字串 alloc。construct(q++, 10, ‘c’); // *q為cccccccccc alloc。construct(q++, “hi”); // *q為hi std::cout << *p << std::endl; // 正確:使用string的輸出運算子 //std::cout << *q << std::endl; // 災難:q指向未構造的記憶體 std::cout << p[0] << std::endl; std::cout << p[1] << std::endl; std::cout << p[2] << std::endl; while (q != p) { alloc。destroy(——q); // 釋放我們真正構造的string } alloc。deallocate(p, n); return 0;} int test_allocator_2(){ std::vector vi{ 1, 2, 3, 4, 5 }; // 分配比vi中元素所佔用空間大一倍的動態記憶體 std::allocator alloc; auto p = alloc。allocate(vi。size() * 2); // 透過複製vi中的元素來構造從p開始的元素 /* 類似複製演算法,uninitialized_copy接受三個迭代器引數。前兩個表示輸入序列,第三個表示 這些元素將要複製到的目的空間。傳遞給uninitialized_copy的目的位置迭代器必須指向未構造的 記憶體。與copy不同,uninitialized_copy在給定目的位置構造元素。 類似copy,uninitialized_copy返回(遞增後的)目的位置迭代器。因此,一次uninitialized_copy呼叫 會返回一個指標,指向最後一個構造的元素之後的位置。 */ auto q = std::uninitialized_copy(vi。begin(), vi。end(), p); // 將剩餘元素初始化為42 std::uninitialized_fill_n(q, vi。size(), 42); return 0;} ////////////////////////////////////////////////////////////// reference: http://www。modernescpp。com/index。php/memory-management-with-std-allocatorint test_allocator_3(){ std::cout << std::endl; std::allocator intAlloc; std::cout << “intAlloc。max_size(): ” << intAlloc。max_size() << std::endl; int* intArray = intAlloc。allocate(100); std::cout << “intArray[4]: ” << intArray[4] << std::endl; intArray[4] = 2011; std::cout << “intArray[4]: ” << intArray[4] << std::endl; intAlloc。deallocate(intArray, 100); std::cout << std::endl; std::allocator doubleAlloc; std::cout << “doubleAlloc。max_size(): ” << doubleAlloc。max_size() << std::endl; std::cout << std::endl; std::allocator stringAlloc; std::cout << “stringAlloc。max_size(): ” << stringAlloc。max_size() << std::endl; std::string* myString = stringAlloc。allocate(3); stringAlloc。construct(myString, “Hello”); stringAlloc。construct(myString + 1, “World”); stringAlloc。construct(myString + 2, “!”); std::cout << myString[0] << “ ” << myString[1] << “ ” << myString[2] << std::endl; stringAlloc。destroy(myString); stringAlloc。destroy(myString + 1); stringAlloc。destroy(myString + 2); stringAlloc。deallocate(myString, 3); std::cout << std::endl; return 0;} //////////////////////////////////////////////////////// reference: http://en。cppreference。com/w/cpp/memory/allocatorint test_allocator_4(){ std::allocator a1; // default allocator for ints int* a = a1。allocate(1); // space for one int a1。construct(a, 7); // construct the int std::cout << a[0] << ‘\n’; a1。deallocate(a, 1); // deallocate space for one int // default allocator for strings std::allocator a2; // same, but obtained by rebinding from the type of a1 decltype(a1)::rebind::other a2_1; // same, but obtained by rebinding from the type of a1 via allocator_traits std::allocator_traits::rebind_alloc a2_2; std::string* s = a2。allocate(2); // space for 2 strings a2。construct(s, “foo”); a2。construct(s + 1, “bar”); std::cout << s[0] << ‘ ’ << s[1] << ‘\n’; a2。destroy(s); a2。destroy(s + 1); a2。deallocate(s, 2); return 0;}} // namespace allocator_

最後,如果你想學C/C++可以私信小編“01”獲取素材資料以及開發工具和聽課許可權哦!