空间配置器allocator

先解决一个坑 allocator

在源码剖析的第二章,便是讲的allocator,(第一次看的时候一脸蒙B)

因为所有的STL的操作对象都存放在容器内,而容器一定要配置空间以置放资料,都要用到allocator

SGI标准的空间适配器 std::allocator

下面的代码,只是把C++的:::operator new::operator delete 做一层薄薄的包装,SGI 从未使用过它,也不建议我们使用,主要原因是效率不佳

//这个程序在CODEBLOCKS上运行失败,有很多错误

#ifndef DEFALLOC_H
#endif DEFALLOC_H 

#include <new.h> //for placement new
#include <stddef.h> //for ptrdiff_t, size_t
#include <stdlib.h> //for exit()
#include <iostream.h> //for cerr
#include <algobase.h>

    template<class T>
    inline T* allocate(ptrdiff_t size, T*)
    {
        set_new_handler(0);
        T* tmp = (T*)(::operator new((size_t)(size * sizeof(T)));
        if(tmp == 0)
        {
            cerr<<"out of memory" <<endl;
            exit(1);
        }

        return tmp;
    }

    template<class T>
    inline void deallocate(T* buffer)
    {
        ::operator delete(buffer);
    }
    //_________________________________

    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;

        pointer allocate(size_type n)
        {
            return  ::allocate((difference_type)n,(pointer)0)   //(pointer)0 ?????????
        }

        void deallocate(pointer p)
        {::deallocate(p);}

        pointer address (reference x) 
        { return (pointer)&x; }

        const_pointer const_address (const_reference x) 
        { return (const_pointer)&x; }

      size_type init_page_size() 
        {
            return max(size_type(1),size_type(4096/sizeof(T));       
        }


        size_type max_size() const
        {
            return max(size_type(1),size_type(UINT_MAX/sizeof(T));
            //UINT_MAX没有初值啊?
        }
    };


#endif

SGI特殊的空间配置器 std::alloc

class Foo {....};
Foo* pf = new Foo;  //配置内存,然后构造对象
delete pf;          //将对象析构,然后释放内存

new内含俩阶段操作:
1.调用::operator new 配置内存
2.调用Foo::Foo() 构造对象内容
delete也是俩阶段操作:
1.调用Foo::~Foo() 将对象析构
2.调用 ::operator delete 释放内存

为了精密分工,STL allocator决定将这俩阶段操作区分开来
内存配置操作由alloc:allocator() 负责
内存释放操作由alloc:deallocate() 负责
对象构造操作由 ::construct() 负责
对象析构操作由 ::destroy() 负责

STL allocator的整体结构
空间配置器allocator_第1张图片

构造和析构基本工具: construct()destroy()

#include<new.h> //for placement new

template<class T1, class T2>
inline void construct(T1*p, const T2& value)
{
    new (p) T1(value);      //placement new; 调用 T1::T1(value);
}

//以下是destroy()第一个版本,接受一个指针

template<class T>
inline void destroy(T* pointer)
{
    pointer->~T();          //调用dtor ~T()

}

//以下是destroy()的二个版本,接受俩个迭代器,此函数设法找出元素的数值型别
//进而利用 _type_traits<>求取最适当措施

template <class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIterator last)
{
    _destroy(first,last,value_type(first));
}

//判断元素的数值型别(value type)是否有trivial destructor

template<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());
}

//如果元素的数值型别(value type)有non-trivial destructor...(有意义)

template<class ForwardIterator>
inline void
_destroy_aux (ForwardIterator first, ForwardIterator last, _false_type)
{
    for(; first<last; ++first)
        destroy(&*first);
}

//如果元素的数值型别(value type)有trivial destructor...

template<class ForwardIterator>
inline void 
_destroy_aux (ForwardIterator, ForwardIterator, _true_type) {}  //什么都不做

//以下是detroy()第二版本针对迭代器为char* 和 wchar_t* 的特化版

inline void destroy(char*, char*) {}
inline void destroy(wchar_t*, wchar_t*) {}

这幅图片很好的概括了以上代码

这里再说一下上面代码几个点:
1.trivial destructor
has_trivial_destructor函数
trivial的意思是”不重要的,无意义的“的,所以non-trivial就是”有意义的“
现在肤浅的理解是:如果不定义析构函数,用系统自带的,则是trivial distructor,什么都不用做。但如果是non_trivial distructor的话,说明用户自定义了析构函数(问题来了。那么不应该直接调用它定义的析构函数吗?

STL源码剖析中有这么一段话:(P53)
第二版本destroy,接受first,last俩个迭代器,将[first, last)范围内的所有对象析构掉,我们不知道这个范围多大,万一很大,而每个对象的析构函数都无关痛痒(所谓trivial destructor),那么一次次调用这些无关痛痒的析构函数对效率是一种伤害 因此,首先利用 value_type() 获得迭代器所指对象的型别,再利用 _type_traits<T> 判断该型别的析构函数是否无关痛痒。若是(_true_type),则什么也不做就结束;若否(_false_type),这才以循环方式巡防整个范围,并在循环中每经历一个对象就调用第一个版本的destroy()

空间的配置与释放 std::alloc

上面说了对象的构造和析构
然后再说空间(内存)的配置和释放
内存配置操作由alloc:allocator() 负责
内存释放操作由alloc:deallocate() 负责

看到很有趣的一短话:

SGI对此的设计哲学如下:

 - 向 system heap 要求空间  - 考虑多线程 (multi-threads)状态 (一下内容皆不考虑)  - 考虑内存不足时的应变措施  - 考虑过多“小型区块”可能造成的内存碎片(fragment)问题

接下来的代码都是根据这几点设计的

2016.5.4 有时间继续补吧

你可能感兴趣的:(STL)