候捷老师STL源码剖析视频课程笔记

本文是我在学习候捷《STL标准库和泛型编程》课程时所做的笔记。在此分享给大家

STL源码剖析

命名空间

所有新式header的组件,都封装于“std"命名空间下。

STL六大件

容器(Container)、算法(Algorithm)、分配器(Allocator)、迭代器(Iterator)、适配器(Adapter)、仿函数(Functor)

关系

请添加图片描述

int ia[6] = {2, 52, 365, 37, 587, 23};
vector<int, allocator<int>> vi(ia, ia + 6);
cout << count_if(vi.begin(), vi.end(), not1(bind2nd(less<int>(), 40)));
//寻找范围内所有大于等于40的元素

vector容器。allocator<int>分配器。vi.begin()迭代器。count_if()算法。not1仿函数适配器。less<int>()仿函数。bind2nd(a,b)适配器,绑定第二参数。not1()适配器,否定相应内容。
not1(bind2nd(less<int>(), 40))predicate,判断条件。
x.begin(),x.end()维持的是左闭右开区间,x.end()指向最后一个元素的下一位置

迭代器遍历
Container<T> c;
for(Container<T>::iterator iter = c.begin(); iter!=c.end(); ++iter)

容器分类

红色部分为C++11新特性。
请添加图片描述

顺序型容器
array

只是将数组封装成class。
size()、front()、back()可用
声明

    array<type,SIZE> arr;
    //必须声明类型及大小
    arr.data()    //起始地址
vector

变长数组(尾部可变)

deque

两端可扩。是分段连续的。
每段一个buffer,每个buffer可以存储一定的元素(满,进入下一个buffer),buffer两端可扩展。

list

双向链表。

forward-list

单链表。单链表没有push_back(),而是push_front()

forward_list<int> fl;
    fl.push_front(1);
    fl.pop_front();
容器适配器

由deque封装而来。

queue
stack
关联式容器

key-value。通过key找value。查找更方便。
multikey可重复。unordered迭代器易失效。

set/multiset

底层红黑树。每个节点key即为value。

map/multimap

底层红黑树。除key之外有value。

unordered set/multiset

底层链式防冲突哈希表。每个节点key即为value。

unordered map/multimap

底层链式防冲突哈希表。除key之外有value。

分配器

在定义时候,带有默认值。可以不声明。以vector为例。

template <typename _Tp, typename _Alloc = std::allocator<_Tp>>
class vector : protected_Vector_base<_Tp, _Alloc>{
    ...............
}

OOP vs GP

面向对象编程(OOP)企图将数据(data)和方法(method)关联一起。
泛型编程(GP)企图将数据(data)和方法(method)分开。
GP可以将容器和算法各自分开开发,通过iterator连接即可。(操作符重载显得很重要)

  template<typename _Tp, typename _Compare>
    inline const _Tp&
    max(const _Tp& __a, const _Tp& __b, _Compare __comp)
    {
      //return __comp(__a, __b) ? __b : __a;
      if (__comp(__a, __b))
	return __b;
      return __a;
    }

STL源码基础

运算符重载

:: . .* ?:这四个运算符不能够重载

模板

主要用到的是类模板、函数模板。除此之外,还用到了特化。
特化之前要有template<>

分配器

operator new & malloc

内存分配的底层都是调用operator new 然后调用malloc。通过malloc,调用操作系统的api。
malloc所申请的内存,除所申请的内存外还有一些附加的额外开销。(因为释放时只传入指针,通过这些附加属性可以找到需要释放的地址。)

容器

set里面复合红黑树,非继承。两侧表示相应的sizeof大小,至于容器中存的元素多少,与容器大小无关。
下图中缩进表示复合关系。

请添加图片描述

list

为了使迭代器更好的自增自减,一般均设计为类。

template<typename T>
struct list_node {
    typedef void *void_pointer;
    void_pointer prev;
    void_pointer next;
    T data;
};
template<typename T,class Alloc = alloc>
class list {
protected:
    typedef list_node<T> list_node;
public:
    typedef list_node *link_type;
    typedef list_iterator<T, T &, T *> iterator;
private:
    link_type node;
};
template<class T,class Ref,class Ptr>
struct list_iterator {
    typedef list_iterator<T,Ref,Ptr> self;
    typedef T value_type;
    typedef Ptr pointer;
    typedef Ref reference;
    typedef list_node<T> *link_type;
    //difference_type 和 iterator_category省略。
    link_type node;
    reference operator*() const {
        return (*node).data;
    }
    pointer operator->() const {
        return &(operator*());
    }
}

其中需要特别注意的是,很多运算符已经重载,在阅读过程中要注意其功能及调用次序。
根据运算符的性质,考虑其返回值类型。(前置++传回引用,后置++传值)。

list底层
struct _List_node_base {
  _List_node_base* _M_next;
  _List_node_base* _M_prev;
};

struct _List_node : public _List_node_base {
  _Tp _M_data; 
}

struct _List_iterator_base {
  _List_node_base* _M_node;
};

class _List_base 
{
protected:
  _List_node<_Tp>* _M_node;
};
class list : protected _List_base<_Tp, _Alloc> {
    
}
iterator遵循的原则

trait,萃取。
algorithm要知道iterator的一些属性。 iterator共五种associated type。分别为(后面两种没有被使用过)

  • iterator_category
  • difference_type
  • value_type
  • reference
  • pointer
    以链表为例
template<typename _Tp>
struct _List_iterator
{
    typedef _List_iterator<_Tp>                _Self;
    typedef _List_node<_Tp>                    _Node;

    typedef ptrdiff_t                          difference_type;
    typedef std::bidirectional_iterator_tag    iterator_category;
    typedef _Tp                                value_type;
    typedef _Tp*                               pointer;
    typedef _Tp&                               reference;
};

算法调用时,根据访问相应的类别,获取到相应的类型。

template <typename I>
algorithm(I first,I last) {
    I::iterator_category
    I::difference_type
    I::value_type
    I::pointer
    I::reference
}

如果传入的iterator不是class(比如指针),通过萃取机(中间件)获得相应的类型。

  • 间接询问
  template<typename _Iterator>
    struct iterator_traits
    {
      typedef typename _Iterator::iterator_category iterator_category;
      typedef typename _Iterator::value_type        value_type;
      typedef typename _Iterator::difference_type   difference_type;
      typedef typename _Iterator::pointer           pointer;
      typedef typename _Iterator::reference         reference;
    };
  • 偏特化
  template<typename _Tp>
    struct iterator_traits<_Tp*>
    {
      typedef random_access_iterator_tag iterator_category;
      typedef _Tp                         value_type;
      typedef ptrdiff_t                   difference_type;
      typedef _Tp*                        pointer;
      typedef _Tp&                        reference;
    };
      template<typename _Tp>
    struct iterator_traits<const _Tp*>
    {
      typedef random_access_iterator_tag iterator_category;
      typedef _Tp                         value_type; 
      //value_type主要用来声明变量
      //如果是const,声明一个无法被赋值的变量,没有用。

      typedef ptrdiff_t                   difference_type;
      typedef const _Tp*                  pointer;
      typedef const _Tp&                  reference;
    };
vector

vector 内部封装了三个迭代器start,finish,end_of_storage
当容器填满时,在内存中分配另外一块两倍大小的空间。(造成迭代器失效)

deque

分段连续。内部有startfinish迭代器用来控制map(控制中心)首尾,map中的元素指向相应的bufferbuffer里存放具体的数据。为了维持连续这一假象,当触及到buffer边界时,会扩充。除此之外,内部还有map_size来判断大小。

__deque_buf_size存在默认

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值