在SGI STL源码中,很多时候一些模板类提供默认形参的时候,都会选择提供deque.
template <class _Tp, class _Sequence __STL_DEPENDENT_DEFAULT_TMPL(deque<_Tp>) > // stl_stack.h
template <class _Tp, class _Sequence __STL_DEPENDENT_DEFAULT_TMPL(deque<_Tp>) > // stl_queue.h
_STL_DEPENDENT_DEFAULT_TMPL宏就是替换后扩展为"=deque<_Tp>".既然出场的频率那么高,所以我首先选择他.一起去看看deque的源码.
deque的源码很多,一千六百多行的代码. 而且实现也是蛮复杂的.包括deque_iterator,deque_allocator,deque_base,deque.如果你也在阅读deque的源码,
我的建议是先从deque开始阅读源码,了解成员模板函数的功能. 然后再慢慢向上扩展,到迭代器和内存管理.
还有一点,我的建议是慢慢的迭代,纵向发展,而不是横向.除非迫不得已,一般我还是选择纵向发展.
1. 好吧,题外话说了不少了,现在一起阅读源码. 首先先从deque中的功能函数下手,虽然这些函数很简单.对,我们就是从最简单的下手.
iterator begin() { return _M_start; } iterator end() { return _M_finish; } const_iterator begin() const { return _M_start; } const_iterator end() const { return _M_finish; } reverse_iterator rbegin() { return reverse_iterator(_M_finish); } reverse_iterator rend() { return reverse_iterator(_M_start); } const_reverse_iterator rbegin() const { return const_reverse_iterator(_M_finish); } const_reverse_iterator rend() const { return const_reverse_iterator(_M_start); } reference front() { return *_M_start; } reference back() { iterator __tmp = _M_finish; --__tmp; return *__tmp; } const_reference front() const { return *_M_start; } const_reference back() const { const_iterator __tmp = _M_finish; --__tmp; return *__tmp; } size_type size() const { return _M_finish - _M_start; } size_type max_size() const { return size_type(-1); } bool empty() const { return _M_finish == _M_start; }
我想这些简单的函数还是很好明白的.
2. 接下来该从哪里下手呢? 很多时候面对源码不知道该从什么地方开始,这就是为什么有些人阅读源码畅通无阻,有些人则很吃力.是因为没有使用
合适的方法. 为什么不去看看deque的成员变量呢?
成员变量都是从_Deque_base继承过来的,很容易找得到,找到swap函数就找到了.
void swap(deque& __x) { __STD::swap(_M_start, __x._M_start); __STD::swap(_M_finish, __x._M_finish); __STD::swap(_M_map, __x._M_map); __STD::swap(_M_map_size, __x._M_map_size); }
3.接下来我就不再说的那么啰嗦了,也不会再设计到如何阅读源码和详细说明阅读方法的问题了.我们直奔主题吧~
首先来看看deque的设计吧~
deque继承_Deque_base, _Deque_base是骨架,deque派生类只是增加STL提供给我们的封装操作API.
_Deque_base的实现由两个版本,由__STL_USE_STD_ALLOCATORS宏控制.
与_Deque_base相关的是_Deque_Alloc_base.这个模板类也是由__STL_USE_STD_ALLOCATORS控制,而且是和_Deque_base在一起.
deque中的迭代器功能来自于_Deque_iterator.
好吧,现在在源码中出现的类都讲完了. 串一下,deque的功能由上面几个不同的组件拼合而成.
4.看一看STL的内存分配.
由于宏__STL_USE_STD_ALLOCATORS的控制可以使用两个版本的_Deque_alloc_base.所以我们去阅读提供了默认分配策略的那个.
typedef simple_alloc<_Tp, _Alloc> _Node_alloc_type; typedef simple_alloc<_Tp*, _Alloc> _Map_alloc_type; _Tp* _M_allocate_node() { return _Node_alloc_type::allocate(__deque_buf_size(sizeof(_Tp))); } void _M_deallocate_node(_Tp* __p) { _Node_alloc_type::deallocate(__p, __deque_buf_size(sizeof(_Tp))); } _Tp** _M_allocate_map(size_t __n) { return _Map_alloc_type::allocate(__n); } void _M_deallocate_map(_Tp** __p, size_t __n) { _Map_alloc_type::deallocate(__p, __n); }
simple_alloc就是提供的默认分配策略,allocate与deallocate提供内存的管理策略. 再看另外一个。
template <class _Tp, class _Alloc, bool __is_static> class _Deque_alloc_base { public: typedef typename _Alloc_traits<_Tp,_Alloc>::allocator_type allocator_type; allocator_type get_allocator() const { return _M_node_allocator; } _Deque_alloc_base(const allocator_type& __a) : _M_node_allocator(__a), _M_map_allocator(__a), _M_map(0), _M_map_size(0) {} protected: typedef typename _Alloc_traits<_Tp*, _Alloc>::allocator_type _Map_allocator_type; allocator_type _M_node_allocator; _Map_allocator_type _M_map_allocator; _Tp* _M_allocate_node() { return _M_node_allocator.allocate(__deque_buf_size(sizeof(_Tp))); } void _M_deallocate_node(_Tp* __p) { _M_node_allocator.deallocate(__p, __deque_buf_size(sizeof(_Tp))); } _Tp** _M_allocate_map(size_t __n) { return _M_map_allocator.allocate(__n); } void _M_deallocate_map(_Tp** __p, size_t __n) { _M_map_allocator.deallocate(__p, __n); }
看一下_Alloc_traits文件源码:
template <class _Tp, class _Tp1> struct _Alloc_traits<_Tp, allocator<_Tp1> > { static const bool _S_instanceless = true; typedef simple_alloc<_Tp, alloc> _Alloc_type; typedef allocator<_Tp> allocator_type; }; // Versions for the predefined SGI-style allocators. template <class _Tp, int __inst> struct _Alloc_traits<_Tp, __malloc_alloc_template<__inst> > { static const bool _S_instanceless = true; typedef simple_alloc<_Tp, __malloc_alloc_template<__inst> > _Alloc_type; typedef __allocator<_Tp, __malloc_alloc_template<__inst> > allocator_type; }; template <class _Tp, bool __threads, int __inst> struct _Alloc_traits<_Tp, __default_alloc_template<__threads, __inst> > { static const bool _S_instanceless = true; typedef simple_alloc<_Tp, __default_alloc_template<__threads, __inst> > _Alloc_type; typedef __allocator<_Tp, __default_alloc_template<__threads, __inst> > allocator_type; }; template <class _Tp, class _Alloc> struct _Alloc_traits<_Tp, debug_alloc<_Alloc> > { static const bool _S_instanceless = true; typedef simple_alloc<_Tp, debug_alloc<_Alloc> > _Alloc_type; typedef __allocator<_Tp, debug_alloc<_Alloc> > allocator_type; }; // Versions for the __allocator adaptor used with the predefined // SGI-style allocators. template <class _Tp, class _Tp1, int __inst> struct _Alloc_traits<_Tp, __allocator<_Tp1, __malloc_alloc_template<__inst> > > { static const bool _S_instanceless = true; typedef simple_alloc<_Tp, __malloc_alloc_template<__inst> > _Alloc_type; typedef __allocator<_Tp, __malloc_alloc_template<__inst> > allocator_type; }; template <class _Tp, class _Tp1, bool __thr, int __inst> struct _Alloc_traits<_Tp, __allocator<_Tp1, __default_alloc_template<__thr, __inst> > > { static const bool _S_instanceless = true; typedef simple_alloc<_Tp, __default_alloc_template<__thr,__inst> > _Alloc_type; typedef __allocator<_Tp, __default_alloc_template<__thr,__inst> > allocator_type; }; template <class _Tp, class _Tp1, class _Alloc> struct _Alloc_traits<_Tp, __allocator<_Tp1, debug_alloc<_Alloc> > > { static const bool _S_instanceless = true; typedef simple_alloc<_Tp, debug_alloc<_Alloc> > _Alloc_type; typedef __allocator<_Tp, debug_alloc<_Alloc> > allocator_type; };
源码中显示了模板_Alloc_traits以及很多的偏特化版本. 我们发现实际的内存管理功能是 _allocator和simple_alloc提供的.
主要有下面几个模板类:__malloc_alloc_template,
__default_alloc_template,
debug_alloc.
去看看__malloc_alloc_template的实现.
static void* allocate(size_t __n) { void* __result = malloc(__n); if (0 == __result) __result = _S_oom_malloc(__n); return __result; } static void deallocate(void* __p, size_t /* __n */) { free(__p); } static void* reallocate(void* __p, size_t /* old_sz */, size_t __new_sz) { void* __result = realloc(__p, __new_sz); if (0 == __result) __result = _S_oom_realloc(__p, __new_sz); return __result; }
可见底层对于内存的管理是通过malloc和realloc,free等标准库函数实现的.
5. 我们讨论一下_Alloc_traits存在的意义.
昨天我看了一下C++ traits技术,这里有资料链接:
Traits: a new and useful template technique by Nathan C. Myers
http://www.cantrip.org/traits.html
很多人讨论C++ traits的时候也习惯成文C++ 萃取技术. 使用_Alloc_traits使对于allocator的使用变得更加灵活,
Container根本不必知道底层的Allocator到底是什么样子的,而是只需要知道他们模糊的样子_Alloc_traits.因为他们
只需要知道内存申请和释放的接口.其他的不关心,同时也没有必要知道.
6.就刚才上面发现如果内存申请或者是重新申请失败的时候就会调用_S_oom_realloc和_S_oom_malloc方法.不妨去看看SGI STL是怎么处理这种内存
申请失败的情况的.
template <int __inst> void* __malloc_alloc_template<__inst>::_S_oom_malloc(size_t __n) { void (* __my_malloc_handler)(); //函数指针. 1 void* __result; for (;;) { __my_malloc_handler = __malloc_alloc_oom_handler; //handler 2 if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; } (*__my_malloc_handler)(); __result = malloc(__n); if (__result) return(__result); } } template <int __inst> void* __malloc_alloc_template<__inst>::_S_oom_realloc(void* __p, size_t __n) { void (* __my_malloc_handler)(); void* __result; for (;;) { __my_malloc_handler = __malloc_alloc_oom_handler; if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; } (*__my_malloc_handler)(); __result = realloc(__p, __n); if (__result) return(__result); } }
看到上面的源码,是通过在for循环中通过回调实现的.而在__malloc_alloc_template也暴露出一个设置Handler的方法.
static void (* __set_malloc_handler(void (*__f)()))() // 这种方式看上去好别扭. { void (* __old)() = __malloc_alloc_oom_handler; __malloc_alloc_oom_handler = __f; return(__old); }
我在上面注明了,这种方式看上去好别扭,暴露出的接口为什么也要声明成为函数指针的形式.
而本身对Handler的限制是很简单的,甚至都没有参数限制.
static void (* __malloc_alloc_oom_handler)();
我只是理清思路,做到在源码中畅游无阻. 剩下的就是关心那部分的实现,或者是对哪种细节的技术感兴趣.然后针对个人情况去处理.
欢迎对细节提出问题讨论. 技术是讨论出来的~ 另外:欢迎拍砖~