C++中std::allocator的使用
標準庫中包含一個名為allocator的類,允許我們將分配和初始化分離。使用allocator通常會提供更好的效能和更靈活的記憶體管理能力。
new有一些靈活性上的侷限,其中一方面表現在它將記憶體分配和物件構造組合在了一起。類似的,delete將物件析構和記憶體釋放組合在了一起。我們分配單個物件時,通常希望將記憶體分配和物件初始化組合在一起。因為在這種情況下,我們幾乎肯定知道物件應有什麼值。當分配一大塊記憶體時,我們通常計劃在這塊記憶體上按需構造物件。在此情況下,我們希望將記憶體分配和物件構造分離。這意味著我們可以分配大塊記憶體,但只在真正需要時才真正執行物件的建立操作(同時付出一定開銷)。一般情況下,將記憶體分配和物件構造組合在一起可能會導致不必要的浪費。
標準庫allocator類定義在標頭檔案memory中,它幫助我們將記憶體分配和物件構造分離開來。它提供一種型別感知的記憶體分配方法,它分配的記憶體是原始的、未構造的。類似vector,allocator是一個模板。為了定義一個allocator物件,我們必須指明這個allocator可以分配的物件型別。當一個allocator物件分配記憶體時,它會根據給定的物件型別來確定恰當的記憶體大小和對齊位置。allocator支援的操作,如下:
allocatro分配的記憶體是未構造的(unconstructed)。我們按需要在此記憶體中構造物件。在新標準庫中,construct成員函式接受一個指標和零個或多個額外引數,在給定位置構造一個元素。額外引數用來初始化構造的物件。類似make_shared的引數,這些額外引數必須是與構造的物件的型別相匹配的合法的初始化器。
在早期版本的標準庫中,construct只接受兩個引數:指向建立物件位置的指標和一個元素型別的值。因此,我們只能將一個元素拷貝到未構造空間中,而不能用元素型別的任何其它建構函式來構造一個元素。還未構造物件的情況下就使用原始記憶體是錯誤的。為了使用allocator返回的記憶體,我們必須用construct構造物件。使用未構造的記憶體,其行為是未定義的。
當我們用完物件後,必須對每個構造的元素呼叫destroy來銷燬它們。函式destroy接受一個指標,對執行的物件執行解構函式。我們只能對真正構造了的元素進行destroy操作。一旦元素被銷燬後,就可以重新使用這部分記憶體來儲存其它string,也可以將其歸還給系統。釋放記憶體通過呼叫deallocate來完成。我們傳遞給deallocate的指標不能為空,它必須指向由allocate分配的記憶體。而且,傳遞給deallocate的大小引數必須與呼叫allocate分配記憶體時提供的大小引數具有一樣的值。
標準庫還為allocator類定義了兩個伴隨演算法,可以在未初始化記憶體中建立物件。它們都定義在標頭檔案memory中,如下:
在C++中,記憶體是通過new表示式分配,通過delete表示式釋放的。標準庫還定義了一個allocator類來分配動態記憶體塊。分配動態記憶體的程式應負責釋放它所分配的記憶體。記憶體的正確釋放是非常容易出錯的地方:要麼記憶體永遠不會被釋放,要麼在仍有指標引用它時就被釋放了。新的標準庫定義了智慧指標型別------shared_ptr、unique_ptr和weak_ptr,可令動態記憶體管理更為安全。對於一塊記憶體,當沒有任何使用者使用它時,智慧指標會自動釋放它。現代C++程式應儘可能使用智慧指標。
std::allocator是標準庫容器的預設記憶體分配器。你可以替換自己的分配器,這允許你控制標準容器分配記憶體的方式。
以上內容主要摘自:《C++Primer(Fifth Edition 中文版)》第12.2.2章節
下面是從其他文章中copy的測試程式碼,詳細內容介紹可以參考對應的reference:
#include "allocator.hpp"
#include <iostream>
#include <memory>
#include <string>
#include <vector>
namespace allocator_ {
////////////////////////////////////////////////
// reference: C++ Primer(Fifth Edition) 12.2.2
int test_allocator_1()
{
std::allocator<std::string> 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<int> vi{ 1, 2, 3, 4, 5 };
// 分配比vi中元素所佔用空間大一倍的動態記憶體
std::allocator<int> 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-allocator
int test_allocator_3()
{
std::cout << std::endl;
std::allocator<int> 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<double> doubleAlloc;
std::cout << "doubleAlloc.max_size(): " << doubleAlloc.max_size() << std::endl;
std::cout << std::endl;
std::allocator<std::string> 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/allocator
int test_allocator_4()
{
std::allocator<int> 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<std::string> a2;
// same, but obtained by rebinding from the type of a1
decltype(a1)::rebind<std::string>::other a2_1;
// same, but obtained by rebinding from the type of a1 via allocator_traits
std::allocator_traits<decltype(a1)>::rebind_alloc<std::string> 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_
GitHub: https://github.com/fengbingchun/Messy_Test
相關文章
- C++ 標準庫 std::set std::multiset swap()的使用C++
- C++/C++11中std numeric limits的使用C++MIT
- C++,std::shared_future的使用C++
- C++(std::vector)C++
- c++ std::vector 切記C++
- 【C++併發實戰】(三) std::future和std::promiseC++Promise
- C++(std::cout 處理 char*)C++
- allocator、polymorphic allocator 與 memory_resource
- error C4996: 'std::_Uninitialized_copy0':與錯誤 LNK2001 無法解析的外部符號 "private: static class std::allocatorError996Zed符號
- std::async的使用總結
- Go's AllocatorGo
- C++中檔案開頭寫的 using namespace std 有什麼作用?C++namespace
- 請問golang 中是否有內建的可排序容器,比如類似c++中的std::vector?Golang排序C++
- C++ 智慧指標詳解: std::unique_ptr 和 std::shared_ptrC++指標
- c++中關於智慧指標std::tr1::shared_ptr的用法C++指標
- (不要)使用std::threadthread
- C++中extern的使用C++
- std::reserve和std::resize的區別
- C++霧中風景17:模板的非推斷語境與std::type_identityC++IDE
- 【譯】對Rust中的std::io::Error的研究RustError
- C++ std::call_once 實現單例模式C++單例模式
- C++霧中風景16:std::make_index_sequence, 來試一試新的黑魔法吧C++Index
- `std::packaged_task`、`std::thread` 和 `std::async` 的區別與聯絡Packagethread
- C++ STL:std::unorderd_map 物理結構詳解C++
- C++ std::list實現大整數加法運算C++
- C++:模板的非推斷語境與std::type_identityC++IDE
- C++語言中std::array的神奇用法總結,你需要知道!C++
- Eigen::aligned_allocator
- C++ 中各種map的使用C++
- C++11中std::move、std::forward、左右值引用、移動建構函式的測試C++Forward函式
- c++11:std::boolalpha、std::noboolalphaC++
- std::vector 和 std::list 區別
- C++11 執行緒同步介面std::condition_variable和std::future的簡單使用C++執行緒
- C++ folly庫解讀(三)Synchronized —— 比std::lock_guard/std::unique_lock更易用、功能更強大的同步機制C++synchronized
- C++中compare函式的使用C++函式
- c++ builder中的ado使用 (轉)C++UI
- Rust 標準庫中的 async/await (async-std)RustAI
- C++ vector<std::tuple<XXX, XXX, XXX>>C++