C++ allocator類學習理解

前言

在學習STL中containers會發現C++ STL裏定義了不少的容器(containers),每個容器的第二個模板參數都是allocator類型,並且默認參數都是allocator。可是allocator究竟是什麼?有什麼做用呢?ios

接下來就圍繞着是什麼和有什麼做用來展開,其中最後補充一下如何去使用默認的allocator。數組

因爲本人學習尚淺,各類blog和msdn學習了幾天,依然仍是不是特別理解。這是把本身的學習經驗,進行一次梳理和記錄。函數

 

What?

 

std::allocator

template <class T> class allocator;    // 默認分配器

 

默認分配器性能

全部的分配器都定義在 <memory> 頭文件中,被用於標準庫中的STL containers學習

若是標準容器中最後一個模板參數沒有指定,那麼就是allocator默認參數this

 對分配或釋放存儲的成員函數的調用將發生在一個總的順序中,而且每一個這樣的釋放將在下一個分配(若是有的話)以前發生。
 spa

 主要成員函數指針

  • address
     
     函數原型:
     pointer address (reference x) const noexcept;
     
     const_pointer address ( const_referece x );
     
     //    reference => T&          const_reference => const T&

  返回x的地址code

 

  • allocate
     
     函數原型:
     pointer allocate(size_type n, allocator<void>::const_pointer hint = 0);
     //hint:0 or 經過另一個allocate得到的非零值且沒有使用deallocate釋放
     //當 hint != 0 時,這個值能夠做爲一個提示,經過分配接近指定的新存儲塊來提升性能。相鄰元素地址是一個不錯的選擇
     pointer => T* const_pointer => const T* size_type => size_t
     
     分配存儲塊
     嘗試分配n個T類型的存儲空間,而後返回第一個元素的起始地址
     只是分配空間,不構造對象
     
     在標準默認allocator,存儲塊是使用 一次或屢次 ::operator new 進行分配,若是他不能分配請求的存儲空間,則拋出bad_alloc異常

 

  • construct

     原型函數:
     template <class U, class... Args>
     void construct(U* p, Args&&... args);

     在p指向的位置構建對象U,此時該函數不分配空間,pointer p是allocate分配後的起始地址
     constructor將其參數轉發給相應的構造函數構造U類型的對象,至關於 ::new ((void*) p) U(forward<Args> (args)...);

 

  • deallocate
     
     原型函數:
     void deallocate(pointer p, size_t n);
     
     釋放先前allocate分配的且沒有被釋放的存儲空間
     
     p:指向之前使用allocator :: allocate分配的存儲塊的指針。
     n:在調用allocator :: allocate時爲這個存儲塊分配的元素數量。
     對象

     在默認的allocator中,使用 ::operator delete進行釋放

 

  • destroy
     
     原型函數:
     template <class U>void destroy (U* p);
     
     銷燬p指向的對象,可是不會釋放空間,也就意味着,這段空間依然可使用
     該函數使用U的析構函數,就像使用下面的代碼同樣:P->〜U();

 

  • max_size

     原型函數:
     size_type max_size() const noexcept;

     返回最大可能分配的大小

How?

有關allocator的最重要的事實是它們只是爲了一個目的:封裝STL容器在內存管理上的低層細節。你不該該在本身的代碼中直接調用 allocator 的成員函數,除非正在寫一個本身的STL容器。你不該該試圖使用allocator來實現operator new[];這不是allocator該作的。 若是你不肯定是否須要使用allocator,那就不要用。

 

基本上不多有人會自定義一個allocator。一來,默認的allocator已經夠用了;二來,確實不知道該怎麼用。通常來講,咱們沒有必要從新定義一個allocator。自定義的方式主要是爲了提升內存分配相關操做的性能。而STL提供的方式性能已經足夠好了。

 

使用默認allocator

使用步驟:

因爲allocator將內存空間的分配和對象的構建分離,故使用allocator分爲如下幾步:

  1. allocator與類綁定,由於allocator是一個泛型類
  2. allocate()申請指定大小空間
  3. construct()構建對象,其參數爲可變參數,因此能夠選擇匹配的構造函數
  4. 使用,與其它指針使用無異
  5. destroy()析構對象,此時空間仍是可使用
  6. deallocate()回收空間

請認真遵照這個順序使用,否則會沒法預料的異常

( 下面該程序也能夠解決無默認參數來構造對象數組的問題)

//#include "CAnimal.h"
#include <memory>
#include <iostream>

using namespace std;

class Animal
{
public:
#if 1        //即便爲0,沒有默認構造也是能夠,
    Animal() : num(0)
    {
        cout << "Animal constructor default" << endl;
    }
#endif
    Animal(int _num) : num(_num)
    {
        cout << "Animal constructor param" << endl;
    }

    ~Animal()
    {
        cout << "Animal destructor" << endl;
    }

    void show()
    {
        cout << this->num << endl;
    }

private:
    int num;
};

int main()
{
    allocator<Animal> alloc;        //1.
    Animal *a = alloc.allocate(5);    //2.

    //3.
    alloc.construct(a, 1);
    alloc.construct(a + 1);
    alloc.construct(a + 2, 3);
    alloc.construct(a + 3);
    alloc.construct(a + 4, 5);

    //4.
    a->show();
    (a + 1)->show();
    (a + 2)->show();
    (a + 3)->show();
    (a + 4)->show();

    //5.
    for (int i = 0; i < 5; i++)
    {
        alloc.destroy(a + i);
    }
    //對象銷燬以後還能夠繼續構建,由於構建和內存的分配是分離的
    //6.
    alloc.deallocate(a, 5);

    cin.get();
    return 0;
}

經過運行結果能夠看出,不管是否有默認構造,allocator會選擇出最匹配的構造函數(重載) 

 

結語:因爲如今本身木有工做經驗和項目經驗,實在對這個allocator的使用,懵懵懂懂,在使用STL containers時,也沒有看見自定義的Allocator,如今只能簡單學習瞭解,以便之後工做撿起來不那麼難或者在看大神的代碼的時候不在那麼懵逼。。。

好吧!就這樣吧。。。

相關文章
相關標籤/搜索