stl 源码阅读之allocator配置器

栏目: 编程工具 · 发布时间: 8年前

内容简介:stl 源码阅读之allocator配置器

作者: tiankonguse | 更新日期:

前段时间一个服务大量使用了hashmap,于是简单看了下hashmap的实现方式,发现需要先看allocator,这里记录一下。

背景

在看vertor, map, set, string源码时, 总会看到有个 _Alloc 相关的模板参数。

然后这些容器都是使用这个 _Alloc 管理内存的。

这里简单的记录下 _Alloc 的设计与源码实现, 为之后的容器记录做准备。

template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
        class vector: protected _Vector_base<_Tp, _Alloc>;

template <typename _Key, typename _Tp, typename _Compare = std::less<_Key>,
        typename _Alloc = std::allocator<std::pair<const _Key, _Tp> > >
        class map;


template<typename _CharT, typename _Traits, typename _Alloc>
    class basic_string;

template<typename _CharT, typename _Traits = char_traits<_CharT>,
           typename _Alloc = allocator<_CharT> >
    class basic_string;

typedef basic_string<char>    string;

注1:我这里都是阅读c++ 4.8版本的stl源码。

注2: 这里不去关系怎么管理内存来提高效率, 这里关系的是申请释放内存时做了哪些事。

面向用户的allocator

根据源码,可以看到, allocator 自身只是定义了一些类型,如指针,引用,值的类型等。

这里唯一不协调的就是 rebind 了,正常情况下我们使用不上这个功能,所以本片文章不做介绍,后续项目涉及到这个了再深入记录一下。

// /usr/include/c++/4.8/bits/allocator.h
template<typename _Tp>
class allocator: public __allocator_base<_Tp> {
public:
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    typedef _Tp* pointer;
    typedef const _Tp* const_pointer;
    typedef _Tp& reference;
    typedef const _Tp& const_reference;
    typedef _Tp value_type;

    template<typename _Tp1> struct rebind { typedef allocator<_Tp1> other;};

    allocator() throw () {}
    allocator(const allocator& __a) throw () : __allocator_base<_Tp>(__a) {}
    template<typename _Tp1> allocator(const allocator<_Tp1>&) throw () {}
    ~allocator() throw () {}
};

中间件 __allocator_base

__allocator_base 只是一个中间符号, 用于指定默认使用那个内存分配器。默认是 new_allocator .

// /usr/include/c++/4.8/bits/c++allocator.h
#if __cplusplus >= 201103L
namespace std
{   
    template<typename _Tp>
    using __allocator_base = __gnu_cxx::new_allocator<_Tp>;
}
#else
// Define new_allocator as the base class to std::allocator.

#define __allocator_base  __gnu_cxx::new_allocator

#endif

一种具体实现new_allocator

new_allocator 是最简单的内存管理实现,其他相关的实现还有 malloc_allocator , __pool_alloc , __mt_alloc , bitmap_allocato 等。

我们看到, new_allocator 里面也定义了那些指针,引用,值的类型, 而且也定义了 rebind , 这里不讨论基类与子类重复申明这些的作用。

真正有用的是分配内存函数 allocatedeallocate , 还有构造和析构函数 constructdestroy .

这四个函数的功能与其名字一样, 一个是申请释放内存的, 一个是构造和析构的。

由于容器一般提前申请内存了, 所以容器就有必要自己调用构造函数和析够函数.

其中的 allocate 函数和 construct 函数延伸出一个知识点: new operator , operator new , placement new .

// /usr/include/c++/4.8/ext/new_allocator.h

template<typename _Tp>
class new_allocator {
public:
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    typedef _Tp* pointer;
    typedef const _Tp* const_pointer;
    typedef _Tp& reference;
    typedef const _Tp& const_reference;
    typedef _Tp value_type;

    template<typename _Tp1> struct rebind { typedef new_allocator<_Tp1> other; };

    new_allocator()  {}
    new_allocator(const new_allocator&)  {}
    template<typename _Tp1> new_allocator(const new_allocator<_Tp1>&)  {}
    ~new_allocator()  {}
    
    pointer address(reference __x) const  { 
        return std::__addressof(__x); 
    }
    const_pointer address(const_reference __x) const  { 
        return std::__addressof(__x); 
    }
    
    pointer allocate(size_type __n, const void* = 0) { 
        return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp))); 
    }
    void deallocate(pointer __p, size_type) { 
        ::operator delete(__p); 
    }
    
    void construct(pointer __p, const _Tp& __val) { 
        ::new ((void *) __p) _Tp(__val); 
    }
    void destroy(pointer __p) { 
        __p->~_Tp(); 
    }
    
    size_type max_size() const { return size_t(-1) / sizeof(_Tp); }
};

new operator, operator new 与 placement new

operator new 是一个函数,我们可以重载,作用是申请指定大小的内存。

new operator 是一个我们熟悉的 new ,不可以重载,作用是调用 operator new 申请内存,并初始化, 一般用户调用。

placement new 是一个全局函数,不可重载,作用是允许我们在一个已分配的内存中主动初始化内存中的对象。

placement new 声明和使用如下:

//声明
void *operator new( size_t, void * p ) throw();

//使用, 返回值依旧是传入的指针。  
void construct(pointer __p, const _Tp& __val) { 
    ::new ((void *) __p) _Tp(__val); 
}

正是由于 placement new 的存在, 我们才可以自己管理内存分配内存,并进行初始化。

欠下的帐

  • 内存管理相关算法
  • rebind作用
  • allocator与基类重复定义的原因
  • stl实现

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Intersectional Internet

The Intersectional Internet

Safiya Umoja Noble、Brendesha M. Tynes / Peter Lang Publishing / 2016

From race, sex, class, and culture, the multidisciplinary field of Internet studies needs theoretical and methodological approaches that allow us to question the organization of social relations that ......一起来看看 《The Intersectional Internet》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具