【C++11新特性】 - 空间配置allocator类
原文鏈接?http://blog.csdn.net/Xiejingfa/article/details/50955295
今天我們來講講C++的allocator類。
C++提供了new和delete操作符來管理動態內存空間。new操作通常需要完成兩部分工作:一是在系統中申請內存空間,二是在分配的內存上構造對象。delete操作也通常需要完成對應的兩部分工作:一個調用相應的析構函數銷毀對象,二是回收內存。從這點看,new和delete操作符把內存空間的分配回收與對象的構建銷毀緊緊關聯在一起。對于new和delete的這種特性,如果我們只是申請單個對象的時候倒是很合適。因為我們幾乎可以確定單個對象一定會被使用同時我們也希望將單個對象的內存和初始化組合在一起。
那如果我們需要分配一大塊內存呢?這種情形就有點不同。考慮下面這段代碼:
#include <iostream> using namespace std;class Example { public:Example() { cout << "example default constructor..." << endl; }Example(int x) : a(x) { cout << "example constructor..." << endl; }~Example() { cout << "example destructor..." << endl; }int a; };int main() {// 如果Example沒有默認構造函數,則無法動態分配數組Example *p = new Example[10];delete[] p; }在上面這段代碼中,我們new了一個長度為10的Example數組,這時new操作符不僅申請了相應大小的內存空間,還在分別執行了每個元素的默認構造函數。所以它的輸出如下:
example default constructor... example default constructor... example default constructor... example default constructor... example default constructor... example default constructor... example default constructor... example default constructor... example default constructor... example default constructor... example destructor... example destructor... example destructor... example destructor... example destructor... example destructor... example destructor... example destructor... example destructor... example destructor...如果我們new了長度為10的數組,但并沒有全部使用呢?這樣剩下那些沒有使用的元素就這就產生了額外的對象構造的成本。可見,將內存分配和對象構造結合在一起可能會導致不必要的浪費。而且如果Example中沒有默認的構造函數,我們也無法new出動態數組。
有沒有一種方法可以將內存的分配回收和對象的構造銷毀分離開,讓我們在一塊內存空間中按需分配對象呢?這就是我們今天要介紹的allocator類。
allocator類是一個模板類,定義在頭文件memory中,用于內存的分配、釋放、管理,它幫助我們將內存分配和對象構造分離開來。具體地說,allocator類將內存的分配和對象的構造解耦,分別用allocate和construct兩個函數完成,同樣將內存的釋放和對象的析構銷毀解耦,分別用deallocate和destroy函數完成。下面我們就來介紹一下這幾種函數。
1、內存分配和對象構造
我們可以使用下面語句定義一個名為alloc為類型T分配內存的allocator對象:
allocator<T> alloc;有了allocator對象后,我們可以使用下面的函數讓系統為我們分配一段原始的、未構造的、可以保持n個類型為T的對象的內存空間:
alloc.allocate(n)與new操作類似,allocate函數調用成功后返回一個指向該段內存第一個元素的指針。allocator分配的對象是未構造的,我們在使用前需要調用construct函數在此內存中構造對象,如果使用為構造的內存,其行為是未定義的。construct的使用方法如下:
alloc.construct(p, args)p必須是一個類型為T*的指針,指向一塊由allocator分配的未構造內存空間。arg為類型T構造函數的參數,用來在p指向的內存空間中構造一個T類型對象。
示例:
#include <iostream> #include <memory> using namespace std;class Example { public:Example() : a(0) { cout << "example default constructor..." << endl; }Example(int x) : a(x) { cout << "example constructor..." << endl; }~Example() { cout << "example destructor..." << endl; }int a; };int main() {allocator<Example> alloc;Example *p = alloc.allocate(2);alloc.construct(p);alloc.construct(p + 1, 3);cout << p->a << endl;p++;cout << p->a << endl; }在上面這段代碼中,我們先申請了可以保存兩個Example對象的內存空間,然后在第一個位置上調用Example的默認構造函數來構造對象,在第二個位置調用另一個構造函數來構造對象。輸出如下:
example default constructor... example constructor... 0 3從輸出中我們看到,我們只是完成了內存分配和對象構造的工作,構造好的對象并沒有析構,內存也沒有回收。下面我們來看看如何使用allocator類來完成對象析構和內存回收工作。
2、對象析構和內存回收
當我們使用完對象后,需要對每個構造的對象調用destroy函數來銷毀它們。
alloc.destroy(p)destroy函數接受一個T *的指針,對其指向對象執行構造函數。對象被析構銷毀后,分配好的內存空間依然存在,我們可以重新在這塊內存上繼續構造對象,重復利用,也可以對該內存進行回收操作,歸還給系統。內存釋放用deallocate函數完成:
alloc.deallocate(p, n)deallocate函數釋放從p開始的長度為n的內存空間,其中p是allocate的返回值,n是該段內存 保存的元素個數,應和allocate的參數n保持一致。需要注意的是,必須由用戶來保證調用deallocate前對每個在這塊內存中創建的對象調用destroy函數。
示例:
class Example { public:Example() : a(0) { cout << "example default constructor..." << endl; }Example(int x) : a(x) { cout << "example constructor..." << endl; }~Example() { cout << "example destructor..." << endl; }int a; };int main() {allocator<Example> alloc;Example *p = alloc.allocate(2);alloc.construct(p);alloc.construct(p + 1, 3);cout << p->a << endl;cout << (p + 1)->a << endl;alloc.destroy(p);alloc.destroy(p + 1);alloc.deallocate(p, 2);}輸出如下:
example default constructor... example constructor... 0 3 example destructor... example destructor...總結:
下面自己試試,做幾個例子:
#include <string> #include <iostream> #include <memory> using namespace std; int main() { allocator<string> alloc; int n = 100; //分配n個元素類型為string的空間,allocate返回指向首元素的地址 //allocate分配的對象是沒有構造的,我們需要單獨去構造對象 string * p = alloc.allocate(100); //下面來構造對象 string *q1 = p; //每個對象都初始化為"abcde" while(q1 != p + n){//調用construct:格式是 a.construct(p,argc),argc是//參數alloc.construct(q1 ++,"abcde");} string str; string *q = p; while(q != p + 100){cout << *q ++ << endl;}q = p; q1 = p;while(q != p + 100) { //調用 a.destroy(p),p是指向構造的元素的一個指針,此算法對p指向的元素執行 //析構函數alloc.destroy(q ++); }//調用a.deallocate(p,n)算法釋放由于allocate分配的空間,其中p必須是allocate返回的//指針 alloc.deallocate(q1,100); return 0; }運行結果是:
abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde總結,從上面可以看出,如果要使用allocator動態分配內存,特點是內存分配和對象的構造分離,內存的釋放和對象的析構分離。
首先,假設現在分配內存要存儲的元素類型是T,那么
第一步:先調用 allocator<T>? alloc,得到一個alloc,可以用它分配和釋放空間,構造和析構對象。
第二步:分配未構造的空間,如下代碼:
string *p = alloc.allocate(n);其中調用allocate分配空間,n是分配n未構造的原始的空間。返回類型是T*
第三步:在剛才分配的空間上構造元素,方法是調用a.construct(p,args),其中a是allocator對象,p是第二步分配的未構造的原始的空間,argc是被傳遞給類型為T的構造函數。例如:
//n是一個類似整形的一個數 alloc.construct(p,n);第四步:調用a.destroy析構allocator對象中的每個元素,格式是a.destroy(p),此算法對p指向的對象執行元素的析構函數。例如:
q = p; while(q != p + n)alloc.destroy(q ++);第五步:調用a.deallocate(p,n)來釋放allocate分配的空間,一次性釋放。p是allocate分配后得到的指向第一個對象的指針,allocate分配的存放n個元素的空間。例如:
alloc.deallocate(p,n);指向完上述過程就是allocator類就的完整的使用過程。
總結
以上是生活随笔為你收集整理的【C++11新特性】 - 空间配置allocator类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Visual Studio 2017 、
- 下一篇: 关于c++ pair自己遇到的一个问题?