来聊聊STL标准库(一)---allocators

写在前面的话


C++的STL库很早以前就已经是C++的标准库了,大量的c++应用都在使用,STL是一个代码写得非常精美的库,侯捷老师有一本<<STL源码剖析>>对此有非常详细的介绍,感兴趣的可以去看一下,认真看完看懂以后,对你编程的思想还是有些影响的,毕竟读完以后看到了那么多大师的写法,怎么说脑袋还是会受到一些影响的。

看标准库的源代码,并不是要更加熟悉的使用库,而是强化自己的编程思想,像STL这样的标准库,基本上代码已经非常精简没有什么多余的了(精简的同义词实际上是非常晦涩难懂),阅读这样的代码,可以大大提供自己的编程思想,注意,是思想,不是能力,我觉得,编程能力还是得靠练习实际项目才能有提高的,当然,一些牛人除外,他们的思想和算法我们是跟不上的,比如雷神之锤中的那个浮点数开方函数中的0x5f3759df,像这玩意,我等凡人就不要去深究了。。。

还有,虽然是c++,但是STL并不是按照面向对象的方式进行编程的,所以你要是想增加自己的面向对象的功力,还是不要看这个了,并且用了比较多的模板和c++一些特别语法,读起来会非常非常晦涩,但是理解了以后就一片光明了。

我是对照侯老师的书去看的源代码,我觉得没必要非常非常深入的读,只需要理解他们的编程思想就行了,太深入的话,一是花费太多时间,二是这种精简的代码往往有很多编程技巧在里面,理解困难并且实际项目中使用得也比较少,因为在实际项目中更多的还是要跟人合作,你写的代码总得让别人看得懂吧,使用过多的编程技巧实际上是一种费力不讨好的事情。

STL的组件


STL的组件分成6大部分,分别是:

  • allocators 这是一个最底层的部分,负责空间的配置,管理,申请,释放等等
  • containers 容器,这个是我们用的最多的,像vector,map,set等等都在这里面
  • iterators 迭代器,这个也是我们用得最多的,一般和容易一起使用
  • algorithms 算法,这个用得也不少,排序,搜索,替换之类的都在这里
  • functors 这个是算法的策略
  • adapters 适配器,知道设计模式的就应该比较容易理解,你把它理解成接口可能更容易一些

上面六大组件就是STL的核心,我们一个一个来说一下。

allocators --- 空间配置器


简单的来说,空间配置器主要完成空间的申请和释放,在c++中,我们知道一个对象的整个生命周期包括:

  • 申请类的内存空间
  • 调用构造函数初始化类中的相关信息
  • 类操作
  • 调用析构函数释放类中间的相关信息
  • 释放类的内存空间

第一和第二个操作是由new关键字来实现的,第四和第五个操作是由delete来实现的,在STL中,内存的申请和释放,都是通过stl_alloc.h来实现的,对于类的初始化和析构,都是通过stl_constuct.h来实现的,这样把内存的释放和类的内部变量释放分开了,这只是一种设计方法,没什么特别的地方,只是为了更直观好看一点。

我们分别来看看这两个文件吧,首先是stl_constuct.h 完整的文件在这里(stl_constuct.h)

首先,最基本的部分:

//带参数的构造函数
template <class _T1, class _T2>
inline void _Construct(_T1* __p, const _T2& __value) {
    new ((void*) __p) _T1(__value);
}

//无参数的构造函数
template <class _T1>
inline void _Construct(_T1* __p) {
    new ((void*) __p) _T1();
}

//析构函数
template <class _Tp>
inline void _Destroy(_Tp* __pointer) {
    __pointer->~_Tp();
}

这个很简单,就是调用传入的T的构造和析构函数而已,当然,除了这些还有些特别的,我们在使用STL的时候不一直都使用迭代器么,析构的时候也可以使用迭代器,构造的时候为什么没有用迭代器,呵呵,你对象都还没有哪来的对象数组可迭代啊。

//当第三个参数为`__false_type`时挨个调用析构方法
template <class _ForwardIterator>
void
__destroy_aux(_ForwardIterator __first, _ForwardIterator __last, __false_type)
{
    for ( ; __first != __last; ++__first)
        destroy(&*__first);
}

//判断传入的类是否有析构方法
template <class _ForwardIterator, class _Tp>
inline void 
__destroy(_ForwardIterator __first, _ForwardIterator __last, _Tp*)
{
    typedef typename __type_traits<_Tp>::has_trivial_destructor
      _Trivial_destructor;
    __destroy_aux(__first, __last, _Trivial_destructor());
}

//总入口,传入的是迭代器的区间,通过__VALUE_TYPE判断输入类型
template <class _ForwardIterator>
inline void _Destroy(_ForwardIterator __first, _ForwardIterator __last) {
    __destroy(__first, __last, __VALUE_TYPE(__first));
}

_Trivial_destructor用来查询是否是一个无关紧要的析构函数,如果是,那么析构的时候就啥也不干,提高效率。__VALUE_TYPE用来判断传入的迭代器中元素的类型。

通过这两层封装,就实现了类的解析和析构,并且和迭代器结合起来了。当然,源代码中还有一些其他内容,感兴趣的可以去看看,比如对某些基本类型的特别优化,说是优化,其实就是啥也不干。

恩,构造和析构说完了,我们接下来看看stl_alloc.h,完整的程序在这里stl_alloc.h

stl_alloc.h主要用来进行内存操作,也就是内存的申请和释放,也就是我们熟悉的new,deletemalloc,free这些个函数,你可以把stl_alloc.h看成是这些函数的封装,其实就是封装了malloc,free,但是封装的时候考虑了更多的问题。

这个文件比较大,首先,我们要找到总的接口,就是其他组件使用内存的时候调用的接口,是里面的simple_class类,这个类描述如下:

template<class _Tp, class _Alloc>
class simple_alloc {

public:
    //申请内存,指定大小
    static _Tp* allocate(size_t __n)
    { return 0 == __n ? 0 : (_Tp*) _Alloc::allocate(__n * sizeof (_Tp)); }
    //申请类型为_Tp的内存空间
    static _Tp* allocate(void)
    { return (_Tp*) _Alloc::allocate(sizeof (_Tp)); }
    //释放内存,指定个数
    static void deallocate(_Tp* __p, size_t __n)
    { if (0 != __n) _Alloc::deallocate(__p, __n * sizeof (_Tp)); }
    //直接释放内存
    static void deallocate(_Tp* __p)
    { _Alloc::deallocate(__p, sizeof (_Tp)); }
};

我们会发现,不管是哪个方法,调用的都是_Alloc这个类型的方法,至于这个类型是什么,由于是使用的模板方式编写的程序,那只有传进来的时候才知道

在stl_alloc.h中,实际上使用了两层内存分配策略,第一层的策略实际上直接就是malloc和free的封装,第二层的稍微复杂一点,维护了几个链表,小型的数据申请和释放更加迅速,在代码里面分别是__malloc_alloc_template__default_alloc_template

对于__malloc_alloc_template比较简单,就是封装了标准c语言的内存申请释放函数,大家可以看看源代码。

而对于__default_alloc_template,实际上实现方式是,当申请的内存大于128字节的时候,交给__malloc_alloc_template来处理,当小于128的时候自己处理,并且维护了一组链表,申请和释放128以下的内存直接从链表中获取和消除,省去了调用malloc和free的时间,速度更快,其实这种方法,我们自己也用得很多,这其实就是内存池了。

这些个代码我就不贴出来了,大家感兴趣可以自己研究研究。

上面就是第一个组件allocators,这个组件实际上并没有对外开放,一般都是在STL内部使用,但是这个是STL的基石,因为不管是上面数据结构还是算法,最后都离不开内存这玩意。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值