STL之空间配置器(Allocator)


本文以gcc_9.3.0为例

一、介绍

配置器的基本目的是为给定类型提供内存资源以及提供在内存不再需要时将其归还的地方。标准库的内存管理被封装在一个类模板中——allocator。

1.1、设计

C++标准对于该领域只是给出了一些指示,实现这些要求的最简单的方式是调用operator newoperator delete运算符。相比于有缓存的分配和可以重复利用之前分配的内存,这种方法可能会比较慢。但是这种方法可以在多种硬件和操作系统中正确地工作。__gnu_cxx::new_allocator实现了简单的operator newoperator delete语义,而__gun_cxx::malloc_allocator使用C语言的函数std::mallocstd::free实现了相同的功能。

另一种方法是在配置器类中使用智能来缓存分配。 这种额外的机制可以采用多种形式:位图索引,以2的指数级增长的存储桶的索引,或者更简单的固定大小缓存池。缓存可以在程序中的容器间共享。而且,也不总是调用operator new和operator delete来传递内存,这可以带来速度上的优势。使用这些技术的配置器是__gnu_cxx::bitmap_allocator__gun_cxx::mt_alloc,和__gun_cxx::pool_allocator1

1.2、使用

STL默认使用std::allocator配置器,而该配置器继承自__gnu_cxx::new_allocator

vector< int > int_vec;

我们可以指定配置器:

vector<int,__gnu_cxx::malloc_allocator<int>> malloc_intvec;

1.3、扩展的配置器

  • 1、new_allocator:简单地包装::operator new 和::operator delete
  • 2、malloc_allocator:简单地包装malloc和free
  • 3、debug_allocator:在一个任意的配置器A外增加了一层包装。它将大小略有增加的请求传递给A,并使用额外的内存来存储尺寸大小信息。当将指针传递给deallocate()时,将检查存储的大小,并使用assert()确保它们匹配。
  • 4、throw_allocator:包含内存跟踪和标记功能
  • 5、__pool_alloc:高性能的单池配置器。可重用内存在类型相同实例之间共享。当列表(free list)被用完时,它将通过::operator new 获取新内存。如果客户容器请求的块超过某个阈值,则将绕过缓冲池,将分配请求直接传递给::operator new。
  • 6、__mt__alloc:高性能的固定尺寸的配置器,分配呈指数级增长。
  • 7、bitmap_allocator:一种高性能的配置器,使用位图来跟踪已经使用和未使用的内存位置。

二、C++内存操作

2.1、C++ 中的 new 和 delete

当我们使用一条new表达式时:

string *sp = new string("a value");
string *arr = new string[10];

实际执行了三步操作:

  • 第一步,new表达式调用operator new(或者operator new[ ])标准库函数。该函数分配一块足够大的、原始的、未命名的内存空间以便存储特定类型的对象(或者对象的数组)。
  • 第二步,编译器运行相应的构造函数以构造这些对象,并为其传入初始值。
  • 第三步,对象被分配了空间并构造完成,返回一个指向该对象的指针。

当我们使用一条delete表达式删除一个动态分配的对象时:

delete sp;
delete [] arr;

实际执行了两步操作:

  • 第一步,对sp所指向的对象或者arr所指的数组中的元素执行对应的析构函数。
  • 第二步,编译器调用名为operator delete(或者operator delete[ ])的标准库函数释放内存空间。2

2.2、STL内存配置

STL将new和delete的实现均分为两个阶段:

  • 内存配置由allocator::allocate()负责,内存释放由allocator::deallocate()负责
  • 对象构造由_Construct()负责,对象析构由_Destroy()负责

allocate和deallocate在不同的配置器中有不同的实现,_Construct和_Destroy则在stl_construct.h中实现。

三、对象构造和析构

3.1、对象构造

调用placement new

/**
* Constructs an object in existing memory by invoking an allocated
* object's constructor with an initializer.
*/
#if __cplusplus >= 201103L
template <typename _T1, typename... _Args>
inline void
_Construct(_T1 *__p, _Args &&... __args)
{
   
    ::new (static_cast<void *>(__p)) _T1(std::forward<_Args>(__args)...);
}
#else
template <typename _T1, typename _T2>
inline void
_Construct(_T1 *__p, const _T2 &__value)
{
   
    // _GLIBCXX_RESOLVE_LIB_DEFECTS
    // 402. wrong new expression in [some_]allocator::construct
    ::new (static_cast<void *>(__p)) _T1(__value);
}
#endif

3.2、对象析构

3.2.1 析构单个对象
/**
* Destroy the object pointed to by a pointer type.
*/
template <typename _Tp>
inline void
_Destroy(_Tp *__pointer)
{
   
    __pointer->~_Tp();
}
3.2.2 析构指定范围内的对象
3.2.2.1、_Destroy(_ForwardIterator __first, _ForwardIterator __last)
template <bool>
struct _Destroy_aux
{
   
    template <typename _ForwardIterator>
    static void
    __destroy(_ForwardIterator __first, _ForwardIterator __last)
    {
   
        for (; __first != __last; ++__first)
            std::_Destroy(std::__addressof(*__first));
    }
};

template <>
struct _Destroy_aux<true>
{
   
    template <typename _ForwardIterator>
    static void
        __destroy(_ForwardIterator, _ForwardIterator) {
   }
};

/**
* Destroy a range of objects.  If the value_type of the object has
* a trivial destructor, the compiler should optimize all of this
* away, otherwise the objects' destructors must be invoked.
*/
template <typename _ForwardIterator>
inline void
_Destroy(_ForwardIterator __first, _ForwardIterator __last)
{
   
    typedef typename iterator_traits<_ForwardIterator>::value_type
        _Value_type;
#if __cplusplus >= 201103L
    // A deleted destructor is trivial, this ensures we reject such types:
    static_assert(is_destructible<_Value_type>::value,
                  "value type is destructible");
#endif
    std::_Destroy_aux<__has_trivial_destructor(_Value_type)>::
        __destroy(__first, __last);
}
3.2.2.2、_Destroy_n(_ForwardIterator __first, _Size __count)
template <bool>
struct _Destroy_n_aux
{
   
    template <typename _ForwardIterator, typename _Size>
    static _ForwardIterator
    __destroy_n(_ForwardIterator __first, _Size __count)
    {
   
        for (; __count > 0; (void)++__first, --__count)
            std::_Destroy(std::__addressof(*__first));
        return __first;
    }
};

template <>
struct _Destroy_n_aux<true>
{
   
    template <typename _ForwardIterator, typename _Size>
    static _ForwardIterator
    __destroy_n(_ForwardIterator __first, _Size __count)
    {
   
        std::advance(__first, __count);
        return __first;
    }
};

/**
   * Destroy a range of objects.  If the value_type of the object has
   * a trivial destructor, the compiler should optimize all of this
   * away, otherwise the objects' destructors must be invoked.
   */
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值