• STL源码阅读(二)


    STL源码阅读(二) (SGI STL v3.3)

    stl_vector.h (<vector>)

    // vector的内存分配基类
    template <class _Tp, class _Allocator, bool _IsStatic>
    class _Vector_alloc_base {
    public:
        typedef _Alloc_traits<_Tp, _Allocator>::allocator_type allocator_type;
        ...
    protected:
        allocator_type _M_data_allocator;   // 内存分配器
        _Tp* _M_start;  // 指向vector所分配内存的起始处
        _Tp* _M_finish; // 指向vector已使用的内存的末尾
        _Tp* _M_end_of_storage; // 指向vector所分配内存的末尾
        ...
    };
    
    // _Vector_alloc_base的偏特化版本,不需要存储内存分配器
    template <class _Tp, class _Allocator>
    class _Vector_alloc_base<_Tp, _Allocator, true> {
    public:
        typedef _Alloc_traits<_Tp, _Allocator>::allocator_type allocator_type;
        ...
    protected:
        _Tp* _M_start;  // 指向vector所分配内存的起始处
        _Tp* _M_finish; // 指向vector已使用的内存的末尾
        _Tp* _M_end_of_storage; // 指向vector所分配内存的末尾
        ...
    }
    
    template <class _Tp, class _Alloc>
    struct _Vector_base
      : public _Vector_alloc_base<_Tp, _Alloc, _Alloc_traits<_Tp, _Alloc>::_S_instanceless> {
        typedef _Vector_alloc_base<_Tp, _Alloc, 
                                 _Alloc_traits<_Tp, _Alloc>::_S_instanceless> _Base;
      typedef typename _Base::allocator_type allocator_type;
    
      _Vector_base(const allocator_type& __a) : _Base(__a) {}
      _Vector_base(size_t __n, const allocator_type& __a) : _Base(__a) {
        _M_start = _M_allocate(__n);
        _M_finish = _M_start;
        _M_end_of_storage = _M_start + __n;
      }
    
      ~_Vector_base() { _M_deallocate(_M_start, _M_end_of_storage - _M_start); }
    }
    
    template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
    class vector : protected _Vector_base<_Tp, _Alloc>;
    
    vector维护的是一段连续内存区域[_M_start, _M_end_of_storage),已经使用的内存区域
    是[_M_start, _M_finish)。
    vector中的迭代器时直接指向内存元素的指针。
    成员函数at访问指定元素时会进行边界检查,当发生越界访问时,会抛出_stl_throw_range_error("vector")。
    operator[]不会进行边界检查。
    默认构造函数是不会初始化内存的,其它构造函数会调用uninitialized_fill_n, uninitialized_copy初始化
    内存。
    注意,reserve重新分配内存空间大小new_size时,当new_size > capacity()时,会先分配new_size内存,然后
    拷贝以前的元素到新内存中,释放就内存区域,更新_M_start, _M_finish, _M_end_of_storage。
    new_size < capacity()不会进行任何操作。
    注意,pop_back移除vector最后一个元素,但是并没有返回该元素的值(不知道为什么要这样设计)。         
    注意,erase移除元素时,会将所移除元素或区域的后续元素拷贝到前面填充被移除元素的空闲区域。
    注意,sgi vector swap实现交换两个vector对象的内容时,不是拷贝元素交换,而是交换两个vector的指针。所以,
    之前使用它们的迭代器都已经无效了,需要重新更新。和C++11参考手册上关于vector swap的语义不一样哈,参考手册上
    写的是迭代器与应用都依旧是有效的。所以实际使用的时候,还是先测试一下或看一下实现源码比较好。
    void swap(vector<_Tp, _Alloc>& __x) {
      __STD::swap(_M_start, __x._M_start);
      __STD::swap(_M_finish, __x._M_finish);
      __STD::swap(_M_end_of_storage, __x._M_end_of_storage);
    }
    注意,assign的语义是替换,即替换之前的内容替换为现在的内容,它的两个实现版本,如下:
    void assign(size_type __n, const _Tp& __val) {
          if (__n > capacity()) {
        vector<_Tp, _Alloc> __tmp(__n, __val, get_allocator());
        __tmp.swap(*this);      // 使用了swap交换内容
      }
      else if (__n > size()) {
        fill(begin(), end(), __val);
        _M_finish = uninitialized_fill_n(_M_finish, __n - size(), __val);
      }
      else
        erase(fill_n(begin(), __n, __val), end());
    }
    template <class _Tp, class _Alloc> template <class _InputIter>
    void vector<_Tp, _Alloc>::_M_assign_aux(_InputIter __first, _InputIter __last,
                                            input_iterator_tag) {
      iterator __cur = begin();
      for ( ; __first != __last && __cur != end(); ++__cur, ++__first)
        *__cur = *__first;
      if (__first == __last)
        erase(__cur, end());
      else
        insert(end(), __first, __last);
    }
    
    template <class _Tp, class _Alloc> template <class _ForwardIter>
    void vector<_Tp, _Alloc>::_M_assign_aux(_ForwardIter __first, _ForwardIter __last,
                                       forward_iterator_tag) {
      size_type __len = 0;
      distance(__first, __last, __len);
    
      if (__len > capacity()) {
        iterator __tmp = _M_allocate_and_copy(__len, __first, __last);
        destroy(_M_start, _M_finish);
        _M_deallocate(_M_start, _M_end_of_storage - _M_start);
        _M_start = __tmp;
        _M_end_of_storage = _M_finish = _M_start + __len;
      }
      else if (size() >= __len) {
        iterator __new_finish = copy(__first, __last, _M_start);
        destroy(__new_finish, _M_finish);
        _M_finish = __new_finish;
      }
      else {
        _ForwardIter __mid = __first;
        advance(__mid, size());
        copy(__first, __mid, _M_start);
        _M_finish = uninitialized_copy(__mid, __last, _M_finish);
      }
    }
    
    其它的insert, operator=, erase,resize操作都会拷贝移动元素。另外,注意resize与reserve的区别,
    当new_size < size()会调用erase移除多余的元素;否则,会调用insert以初始值初始化插入元素。
    另外insert,push_back添加新元素,内存不足时,会以原来内存大小的二倍重新分配内存,并将原来的元素拷贝
    到新内存中。
    另外,像一些函数如__M_fill_assign不应该暴露给用户的,却没有隐藏起来,不知道为什么。

    参考资料

    1. sgi STL
    2. cppreference.com
  • 相关阅读:
    Ellipse4SL 的安装包和环境配置要求
    什么是SOHO人
    D2上的Silverlight
    银光团队项目正式启动,欢迎您的参与!
    Silverlight开发团队招募SOHO团员若干
    SOHO新的生活方式
    [银客北京开发团队]承接各种Silverlight和WPF外包项目和合作项目
    微软Silverlight 3.0 概观
    grers
    翻译电文
  • 原文地址:https://www.cnblogs.com/corfox/p/6063307.html
Copyright © 2020-2023  润新知