[C++]STL-空間配置器(一)__C++

來源:互聯網
上載者:User
空間配置器

從STL的實現來說,首先需要明白的就是空間配置器,因為整個STL的操作對象都放在容器中,而容器需要一定配置空間以置放資料。 空間配置器的標準介面

//  標準介面,一些typedefallocator::value_type;allocator::pointer;allocator::const_pointer;allocator::reference;allocator::const_reference;allocator::size_type;allocator::difference_type;allocator::rebind//  一個嵌套的class template,class rebind<U>擁有唯一成員other,那是一個typedef,代表allocator<U>allocator::allocator()//  default constructorallocator::allocator(const allocator&)// copy constructortemplate <class U>allocator::allocator<const allocator<U>&)//  generic copy constructorallocator::~allocator()//  default constructorpointer allocator::address(reference x) const//  返回對象的地址,等同於&xconst_pointer allocator::address(const_reference x)const//  返回對象的地址,等同於&x pointer allocator::allocate(size_type n, const void* = 0)//  配置空間,足以儲存n個T對象,第二參數是個提示,實現上可能利用它來增進地區性,或完全忽略之void allocator::deallocate(pinter p, size_type n)//  歸還之前配置的空間size_type allocator::max_size() const//  按返回可成功配置的最大空間void allocator::constructor(pointer p, const T&x)//  等同於 new(const void* p ) T(x) 即placement newvoid allocator::destroy(pinter p)//  等同於 p->~T()
設計一個簡單的空間配置器
#ifndef _Test_Alloc_#define _Test_Alloc_#include <new> // for placement new#include <cstddef> // for ptrdiff_t, size_t#include <cstdlib> // for exit()#include <climits> // for unit_max#include <iostream> // for cerrnamespace Test_Alloc {    template <class T>    inline T* _allocate(ptrdiff_t size, T*) {        std::set_new_handler(0);        T* tmp = (T*)(::operator new((size_t)(size * sizeof(T))));        if (tmp == 0)        {            std::cerr << "out of memory" << std::endl;        }        return tmp;    }    template <class T>    inline void _deallocate(T* buffer) {        ::operator delete(buffer);    }    template <class T1, class T2>    inline void _construct(T1* p, const T2& value) {        new (p) T1(value);    }    template <class T>    inline void _destroy(T* ptr) {        ptr->~T();    }    template <class T>    class allocator {    public:        typedef T                   value_type;        typedef T*                  pointer;        typedef const T*            const_pointer;        typedef T&                  reference;        typedef const T&            const_reference;        typedef size_t              size_type;        typedef ptrdiff_t           difference_type;        template <class U> struct rebind { typedef allocator<U> other; };        pointer allocate(size_type n, const void* hint = 0) {            return _allocate((difference_type)n, (pointer)0);        }        void deallocate(pointer p, size_type n) {            _deallocate(p);        }        void construct(pointer p, const value_type& value) {            _construct(p, value);        }        void destroy(pointer p) { _destroy(p); }        pointer address(reference x) {            return (pointer)&x;        }        const_pointer const_address(const_reference x) {            return (const_pointer)&x;        }        size_type max_size() const {            return size_type(UINT_MAX/sizeof(T));        }    };}   // end of namespace#endif#include <vector>int main() {    int temp[5]{1, 2, 3, 4, 5};    std::vector<int, Test_Alloc::allocator<int>> iv(temp, temp+5);    for (int i = 0; i < 5; ++i)    {        std::cout << iv[i] << std::endl;    }    return 0;}

(值得注意的是,在terminal中必須加上 -std=c++11 命令來使用C++11)

SGI STL使用一個專屬的、擁有次層配置能力的,效率優越的特殊配置器,在稍後會提及。事實上,SGI STL仍然提供了一個標準的配置器介面,這個標準介面的配置器名為simple_alloc。 具備次配置力的SGI空間配置器

SGI STL的配置器與眾不同,也與標準規範不同,其名稱是alloc而非allocator,而且不接受任何參數。

vector<int, std::alloc> iv;

在STL中,每個容器都指定了其預設的空間配置器。 1. SGI特殊的空間配置器,std::alloc

一般而言,C++的記憶體配置操作和釋放操作是調用new,delete。

class Foo{...};Foo* pf = new Foo;delete pf;

new實際包含兩個操作 1)調用::operator new配置記憶體 2)調用Foo::Foo()構造對象內容。

delete也包含兩個階段: 1)調用解構函式 2)釋放記憶體

STL allocator把這兩個階段分開,記憶體配置操作和釋放由allocate() 和 deallocate()負責,物件建構和析構由construct()和destroy()負責。

2. 構造和析構工具:construct()和destroy()

以下給出 < stl _ construct.h > 的部分內容。

#include <new> // placement newtemplate <class T1, class T2>inline void construct(T1* p, const T2^ value) {    new (p) T1(value);}template <class T>inline void destroy(T* pointer) {    pointer->~T();}//  destroy帶區間範圍的版本template <class ForwardIterator>inline void destroy(ForwardIterator first, ForwardIterator last) {    __destroy(first, last, value_type(first));}//  判讀元素的數實值型別是否有trivial destructortemplate <class ForwardIterator, class T>inline void __destroy(ForwardIterator first, ForwardIterator last, T*) {    typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;    __destroy_aux(first, last, trivial_destructor());}//  如果有trivial destructortemplate <class ForwardIterator, class T>inline void __destroy_aux(ForwardIterator first, ForwardIterator last, __flase_type) {    for ( ; first < last; ++first) {        destroy(&*first);    }}//  如果有trivial destructor,那麼就什麼都不做template <class ForwardIterator, class T>inline void __destroy_aux(ForwardIterator first, ForwardIterator last, __true_type) {}//  正對char*和wchar_t*的特化版本inline void destroy(char*, char*) {}inline void destroy(wchar_t*, wchar_t* ) {}

值得注意的是,上述的constructor()接受一個指標p和一個初值value,該函數的用途是將初值設定到指標所指的空間上,也就是placement new運運算元的作用。

而destroy則是接受指標,調用其解構函式。其中接受範圍的版本需要特別注意,當範圍非常大時,不斷調用解構函式可能會導致效率的極具降低,所以此時我們需要判斷解構函式是否有必須被調用的必要,(__type_traits),由此來區別兩種不同的情況。至於如何判斷解構函式是否為trivial,會在未來的文章中給出解釋。 3. 空間的配置與釋放 std::alloc

上文解釋了記憶體配置後的物件建構和析構過程,現在我們來討論記憶體的配置和釋放。

物件建構前的空間配置和對象析構後的空間釋放,由< stl_alloc.h >負責,SGI對此的設計哲學為: 向system heap要求空間。 考慮多線程狀態。 記憶體不足時的應變措施。 考慮過多“小型區塊”可能造成的記憶體片段問題。

(此處排除多線程的處理)

考慮到小型區塊所可能造成的記憶體破碎問題,SGI設計了雙層配置器,第一季配置器直接調用malloc() 和free()(也就是operator new 和 operator delete), 第二級配置器則視情況採用不同的策略:當配置區塊大於128bytes,視之為“足夠大”,便調用第一級配置器。當配置區塊小於128bytes時,視為“過小”,為了降低額外負擔,便採用複雜的memory pool整理方式,而不求助於第一級配置器。整個設計是否開放第二級配置器,取決於__USE_MALLOC是否被定義。

#ifdet __USE_MALLOC...typedef __malloc_alloc_template<0> malloc_alloc;typedef malloc_alloc alloc;  //  令alloc為第一級配置器#else...typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;//  令alloc為第二級配置器#endif

尤其注意,alloc並不接受任何template型別參數。

無論alloc被定義為第一級或第二級配置器,SGI還為它再封裝一個介面如下,使配置器的介面能夠符合STL規格:

template<class T, class Alloc>class simple_alloc {public:    static T* allocate(size_t n) {        return 0 == n ? 0 : (T*)Alloc::aloocate(n * sizeof(T));     }    static T* allocate(void) {        return (T*) Alloc::allocate(sizeof(T));    }    static void deallocate(T *p, size_t n) {        if  (0 != n) {            Alloc::deallocate(p, n * sizeof(T));        }    }    static void deallocate(T *p) {        Alloc::deallocate(p, sizeof(T));    }};

其內部的四個成員函數其實都是單純的轉調用,調用傳遞給配置器的成員函數,這個介面使配置器的配置單位從bytes轉為個別元素的大小。

SGI STL容器都使用這種介面。

template <class T, class Alloc = alloc>class vector {protected:    typedef simple_alloc<value_type, Alloc> data_allocator;    void deallocate() {        if (...) {            data_allocator::deallocate(start, end_of_storage - start);        }    }    ...};

一、二級配置器的關係,
介面封裝,以及實際運用方式,見下圖:

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.