配置器之隐藏的内存分配

配置器之隐藏的内存分配

1 前言

​ 配置器(allocators)是STL提供的溜达组件之一,负责空间配置与管理。从实现的角度来看,配置器是一个实现了动态空间配置、空间管理、空间释放的class template。由于配置器的存在,我们使用的容器的内存管理都被隐藏起来了。

​ 本文主要先简单介绍一下SGI STL然后重点分析标准STL。

2 SGI STL

SGI STL配置器的定义在 defalloc.h 文件中。

源码分析:见注释

// 分配函数实现比较简单,就是对::operator new做一个简单封装
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;
}

// 释放内存函数,对 ::operator delete 作一层封装
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);
    }
    // 释放空间函数
    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))); 
    }
};
// 模板特化
class allocator<void> {
public:
    typedef void* pointer;
};

​ SGI STL定义了一个符合部分标准的allocator配置器,但是SGI并为使用过,也不建议使用,主要因为效率不佳,其只是把C++的::operator new和::operator delete做一层简单地封装而已。

3 STL标准版本

3.1 new 和 delete

new操作符:内存分配和对象构造组合在一起的。

  • 1.先调用::operator new配置内存
  • 2.然后调用构造函数构造对象

delete操作符:

  • 1.先调用析构函数将对象析构
  • 2.再调用::operator delete释放内存

allocator 将内存分配和对象构造进行了分离

3.2 allocator类中关键的接口

allocator a定义了一个名为a的allocator对象,它可以为类型为T的对象分配内存
a.allocate(n)分配一段原始的,未构造的内存,保存n个类型为T的对象
a.deallocate(p,n)释放从T指针p中地址开始的内存,这块内存保存了n个类型为T的对象;p必须是一个先前由allocate返回的指针,且n必须是p创建时所要求的大小。在调用deallocate之前,用户必须对每个在这块内存中创建的对象调用destroy
a.construct(p,args) p必须是一个类型为T
a.construct(p,args)p必须是一个类型为T* 的指针,指向一块原始内存;args被传递给类型为T的构造对象,用来在p指向的内存中构造一个对象
a.destroy§p为T*类型的指针,此算法对p指向的对象执行析构函数

3.3 源码分析

// stl_alloc.h 关键定义
template <class _Tp>
class allocator {
	// typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;
	typedef alloc _Alloc;          // The underlying allocator.
public:
	// 各种类型重定义
	typedef size_t     size_type;
	typedef ptrdiff_t  difference_type;
	typedef _Tp* pointer;
	// ...

	// 内存分配
	_Tp* allocate(size_type __n, const void* = 0) {
		return __n != 0 ? static_cast<_Tp*>(_Alloc::allocate(__n * sizeof(_Tp)))
			: 0;
	}

	// 内存释放
	void deallocate(pointer __p, size_type __n)
	{
		_Alloc::deallocate(__p, __n * sizeof(_Tp));
	}

	// 在原始内存上构造对象
	void construct(pointer __p, const _Tp& __val) { new(__p) _Tp(__val); }
	// 释放内存上的对象,成为原始内存(未初始化)
	void destroy(pointer __p) { __p->~_Tp(); }
};

3.3 allocator算法

分配关键算法介绍
uninitialized_copy(b,e,b2)从迭代器b和e指出的输入范围中拷贝元素到迭代器b2指定的未构造的原始内存中,b2指向的内存必须足够大,能容纳输入序列中元素的拷贝
uninitialized_copy_n(b,n,b2)从迭代器b指向的元素开始,拷贝n个元素到b2开始的内存中
uninitialized_fill(b,e,t)在迭代器b和e指定的原始内存范围中创建对象,对象的值均为t的拷贝
uninitialized_fill_n(b,n,t)从迭代器b指向的内存地址开始创建n个对象。b必须指向足够大的未构造的原始内存,能够容纳给定数量的对象

这些算法主要是实现内存的填充与拷贝。

3.4 memory中关于配置器的重要头文件

memory
stl_construct.h
stl_alloc.h
stl_uninitialized.h
头文件介绍
stl_construct.h这里定义了全局函数construct() 和 destroy(),负责对象的构造和析构,它们也都隶属于STL标准规范。
stl_alloc.h定义了一二级配置器,彼此合作,配置器名为alloc
stl_uninitialized.h定义了一些全局函数,用来填充(fill)或复制(copy)大块内存数据,它们也都隶属于STL标准规范。
// stl_construct.h 关键定义

template <class _T1, class _T2>
inline void _Construct(_T1* __p, const _T2& __value) {
  new ((void*) __p) _T1(__value);
}

// construct 构造对象
template <class _T1, class _T2>
inline void construct(_T1* __p, const _T2& __value) {
  _Construct(__p, __value);
}

template <class _Tp>
inline void _Destroy(_Tp* __pointer) {
  __pointer->~_Tp();
}

// 析构对象
template <class _Tp>
inline void destroy(_Tp* __pointer) {
  _Destroy(__pointer);
}
// stl_uninitialized.h 关键实现

// 拷贝
inline char* uninitialized_copy(const char* __first, const char* __last,
                                char* __result) {
  memmove(__result, __first, __last - __first);
  return __result + (__last - __first);
}

template <class _InputIter, class _Size, class _ForwardIter>
inline pair<_InputIter, _ForwardIter>
uninitialized_copy_n(_InputIter __first, _Size __count,
                     _ForwardIter __result) {
  return __uninitialized_copy_n(__first, __count, __result,
                                __ITERATOR_CATEGORY(__first));
}

// 填充
template <class _ForwardIter, class _Tp>
inline void uninitialized_fill(_ForwardIter __first,
                               _ForwardIter __last, 
                               const _Tp& __x)
{
  __uninitialized_fill(__first, __last, __x, __VALUE_TYPE(__first));
}

template <class _ForwardIter, class _Size, class _Tp>
inline _ForwardIter 
uninitialized_fill_n(_ForwardIter __first, _Size __n, const _Tp& __x)
{
  return __uninitialized_fill_n(__first, __n, __x, __VALUE_TYPE(__first));
}


template <class _ForwardIter, class _Tp, class _Tp1>
inline void __uninitialized_fill(_ForwardIter __first, 
                                 _ForwardIter __last, const _Tp& __x, _Tp1*)
{
  typedef typename __type_traits<_Tp1>::is_POD_type _Is_POD;
  __uninitialized_fill_aux(__first, __last, __x, _Is_POD());
                   
}

template <class _ForwardIter, class _Tp>
void __uninitialized_fill_aux(_ForwardIter __first, _ForwardIter __last, 
                         const _Tp& __x, __false_type)
{
  _ForwardIter __cur = __first;
  __STL_TRY {
    for ( ; __cur != __last; ++__cur)
      _Construct(&*__cur, __x);
  }
  __STL_UNWIND(_Destroy(__first, __cur));
}

4 参考博客

(19条消息) 你不能不知道的内存分配,从全局概览STL的allocator空间配置器_stl中map的allocator_董哥的黑板报的博客-CSDN博客

(19条消息) 内存分配(allocator)_冬风诉的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值