zoukankan      html  css  js  c++  java
  • STL浅析——序列式容器vector的构造和内存管理: constructor() 和 push_back()

      咱们先来做一个测试capacity是容器容量,size是大小:

    #include <iostream>
    #include <vector>
    using namespace std;
    
    int main(){
        vector<int> result;
        for (int i = 0; i < 17; i++)
        {
            result.push_back(i);
            printf("element count:  %d	", result.size());
            printf("capacity:  %d	", result.capacity());
            printf("first element's address:  %x
    ", result.begin());
        }
        return 0;
    }

      运行结果:

      

      可以观察到每次容器满了需要扩容的时候,容量总是呈现两倍增长,而且每次扩容,容器第一个元素所在地址都会发生改变,由此我们知道,容器的扩容时实际是另外寻找一片更大的空间,VS的如下:

      

      扩容的倍数不一样VS为1.5倍扩容,最好的扩容倍数是黄金分割比,1.618倍,当然也不可能那么精确。。。

      vector 缺省使用 alloc (实际上是 宏__STL_DEFAULT_ALLOCATOR(_Tp) 的typedef,但是该宏本质还是第二级空间配置器最终封装而成的 alloc) 作为空间配置器,并据此定义了一个 _Base。

    class vector : protected _Vector_base<_Tp, _Alloc> 
    {
    ...
    private:
          typedef _Vector_base<_Tp, _Alloc> _Base;
    ...
    //以前是:
        typedef simple_alloc<value_type, Alloc> data_allocator;
    //最新版本是进行了进一步封装,分开了基本参数和泛型操作函数
    }

      vector 提供了许多constructors,其中一个允许我们指定空间大小与初值:

      
    //就是这个,允许我们指定空间大小和初值
    vector(size_type __n, const _Tp& __value, const allocator_type& __a = allocator_type()) : _Base(__n, __a) { _M_finish = uninitialized_fill_n(_M_start, __n, __value); } //上面 uninitialized_fill_n 这个函数,藏得很深在stl_uninitialized.h里面,实际该函数还是对fill函数的一次封装
    //最终调用的是fill函数,
    在 stl_alogbase.h 这个头文件下面,如下,
    //不过这个文件夹下面有很多fill函数,我想应该是uninitialized_fill_n的作用还包括型别判断,根据型别不同调用不同的fill函数
    void fill(_ForwardIter __first, _ForwardIter __last, const _Tp& __value) {
      __STL_REQUIRES(_ForwardIter, _Mutable_ForwardIterator);
      for ( ; __first != __last; ++__first)
        *__first = __value;
    }

      我们以 push_back() 函数将新元素插入vector尾端时,该函数首先检查是否还有备用空间,如果有则直接在备用空间上构造元素,并调整迭代器finish,使得vector变大。如果没有备用空间就扩充:

      void push_back(const _Tp& __x) {
        if (_M_finish != _M_end_of_storage) {
          construct(_M_finish, __x);  //直接placement构造元素,移动_M_finish指针
          ++_M_finish;
        }
        else
          _M_insert_aux(end(), __x);  //重新构造整个vector并插入元素
      }
     //表示不懂为啥要重载一次push_back()。。。
    void push_back() { if (_M_finish != _M_end_of_storage) { construct(_M_finish); ++_M_finish; } else _M_insert_aux(end()); }

      至于 _M_insert_aux 函数,有点复杂,但是也不难理解,如下:

    vector<_Tp, _Alloc>::_M_insert_aux(iterator __position, const _Tp& __x)
    {
      if (_M_finish != _M_end_of_storage)  //有备用空间 
     {
      //在备用空间起始处构造一个元素,并以vector最后一个元素为其初始值 construct(_M_finish,
    *(_M_finish - 1)); ++_M_finish;  //调整水位 _Tp __x_copy = __x; copy_backward(__position, _M_finish - 2, _M_finish - 1); *__position = __x_copy; } else    //无备用空间
     {
    const size_type __old_size = size(); const size_type __len = __old_size != 0 ? 2 * __old_size : 1; //以上配置原则,如果原大小为0,则配置一个元素大小
    //如果原大小不为零,则配置原来大小的两倍
       //前半段用来存放原始数据,后半段用来存放新数据

       iterator __new_start
    = _M_allocate(__len); iterator __new_finish = __new_start; __STL_TRY
      {
        //原vector内容拷贝到新vector __new_finish
    = uninitialized_copy(_M_start, __position, __new_start); construct(__new_finish, __x);  //为新元素设定初始x ++__new_finish;          //调整水位
        //安插点的原内容也一起拷贝 __new_finish
    = uninitialized_copy(__position, _M_finish, __new_finish); } __STL_UNWIND((destroy(__new_start,__new_finish), _M_deallocate(__new_start,__len)));
      //销毁,释放原vector destroy(begin(), end()); _M_deallocate(_M_start, _M_end_of_storage
    - _M_start);
      //调整迭代器,指向新的vector _M_start
    = __new_start; _M_finish = __new_finish; _M_end_of_storage = __new_start + __len; } }

      因此可以明白,对 vector 的任何操作,一旦引起空间重新配置,则指向原 vector 的所有迭代器都会失效。

    既然选择了远方,便只顾风雨兼程
  • 相关阅读:
    python-day8(正式学习)
    Bug快到碗里来
    python-day7(正式学习)
    python-day6(正式学习)
    python-day5(正式学习)
    python-day4(正式学习)
    Django中间件
    cookie和session
    分页器,form组件的使用
    orm常用字段和数据库优化查询
  • 原文地址:https://www.cnblogs.com/Forever-Road/p/6831950.html
Copyright © 2011-2022 走看看