SGISTL源码阅读八 Vector容器上

SGISTL源码阅读八 Vector容器上

Vector概述

array是静态的数组,一经定义大小不可改变,这对于未知数组大小的情况来说是非常不利的,如果没有vector我们只能猜测一个较大的值来初始化数组。这无疑对空间造成了很大程度上的浪费,而且如果初始值过小,就得必须自己重新定义一个更大的数组,并且将原来的数据拷贝一份,再释放掉原来的空间。
vector被称为动态数组。随着元素的加入,它的内部机制会自行扩充空间以容纳新元素,它相对于array来说,更加的灵活。


深入Vector源码

Vector的迭代器及其数据结构
//缺省使用SGISTL空间配置器
template <class T, class Alloc = alloc>
class vector {
public:
  //vector的嵌套型别定义
  typedef T value_type;
  typedef value_type* pointer;
  typedef const value_type* const_pointer;
  typedef value_type* iterator;			//它的迭代器就是普通指针
  typedef const value_type* const_iterator;
  typedef value_type& reference;
  typedef const value_type& const_reference;
  typedef size_t size_type;
  typedef ptrdiff_t difference_type;

我们知道vector所维护的是一个连续的线性空间,普通指针就可以作为vector的迭代器。
(在iterator_traits的学习中,我们知道指针的迭代器类型是random_access_iterator。)

protected:
  //...  
  
  iterator start;			//表示目前使用空间的头
  iterator finish;			//表示目前使用空间的尾
  iterator end_of_storage;	//表示目前可用空间的尾
  
  //...
  
public:
  //由于vector内部的迭代器是保护类型的,所以需要一个对外的接口
  iterator begin() { return start; }
  const_iterator begin() const { return start; }
  iterator end() { return finish; }
  const_iterator end() const { return finish; }
  
  //...
  //返回vector的已使用空间
  size_type size() const { return size_type(end() - begin()); }
  size_type max_size() const { return size_type(-1) / sizeof(T); }
  //返回vector的容量
  size_type capacity() const { return size_type(end_of_storage - begin()); }
  bool empty() const { return begin() == end(); }

这里我们可以看到vector的结构是由三个迭代器来维护的,分别指向了vector使用空间的头部,尾部,以及可用空间的尾部。
如图:

在这里插入图片描述

vector的构造与内存分配
vector的构造函数
	//默认构造函数,不申请空间
    vector() : start(0), finish(0), end_of_storage(0) {}
    //接受两个参数,第一个参数 指定vector初始化大小,第二个参数为初始化值(所有空间都会被初始化成value)
    //以下三个是针对不同情况的重载版本,他们都调用了fill_initialize函数
    vector(size_type n, const T& value) { fill_initialize(n, value); }
    vector(int n, const T& value) { fill_initialize(n, value); }
    vector(long n, const T& value) { fill_initialize(n, value); }
    
    //接受一个参数,指定vector的初始化大小。
    //详述
    explicit vector(size_type n) { fill_initialize(n, T()); }
	
    //拷贝构造函数
    vector(const vector<T, Alloc>& x) {
    //调用了allocate_and_copy函数
    start = allocate_and_copy(x.end() - x.begin(), x.begin(), x.end());
    finish = start + (x.end() - x.begin());
    end_of_storage = finish;
    }
    
    //以下两个构造函数是根据first和last两个迭代器的范围来进行初始化
#ifdef __STL_MEMBER_TEMPLATES
    template <class InputIterator>
    vector(InputIterator first, InputIterator last) :
    start(0), finish(0), end_of_storage(0)
    {
    	//调用了range_initialize
    	range_initialize(first, last, iterator_category(first));
    }
#else /* __STL_MEMBER_TEMPLATES */
    vector(const_iterator first, const_iterator last) {
        size_type n = 0;
        distance(first, last, n);
        start = allocate_and_copy(n, first, last);
        finish = start + n;
        end_of_storage = finish;
    }

explicit vector(size_type n) { fill_initialize(n, T()); }
这个构造函数接受一个参数,指定了vector的初始化大小。explicit关键字是用来防止当只有一个传入参数时发生隐式转换的。
例如vector<int> v = 10;,vector<int> v = 'a'
如果没有explicit关键字,以上两种写法是正确的。因为在C++中, 如果的构造函数只有一个参数时, 那么在编译的时候就会有一个缺省的转换操作:将该构造函数对应数据类型的数据转换为该类对象. 也就是说 vector<int> v = 10;这段代码, 编译器自动将整型转换为vector类对象, 实际上等同于下面的操作:

vector<int> temp(10);
vector<int>v = temp;
vector的内存分配

vector的构造函数分析中,我们可以看到它们调用了fill_initializeallocate_and_copyrange_initialize函数,下面我们就来看一下这些函数的实现。

  • fill_initialize

fill_initializ

//维护了vector的三个迭代器,调用了allocate_and_fill函数
void fill_initialize(size_type n, const T& value) {
    start = allocate_and_fill(n, value);
    finish = start + n;
    end_of_storage = finish;
}

allocate_and_fill


protected:
	//typedef simple_alloc<value_type, Alloc> data_allocator;
    //vector给SGISTL的空间配置器设置了一个别名
    iterator allocate_and_fill(size_type n, const T& x) {
        iterator result = data_allocator::allocate(n);
        
        /* __STL_TRY...__STL_UNWIND类似异常处理的try...catch语句块
		 * 这段代码的大意就是,初始化allocate分配的未初始化空间
		 * 如果失败了,则将分配的内存回收,防止内存泄露
=		 */
        __STL_TRY {
        	//调用初始化uninitialized_fill_n未初始化的空间
            uninitialized_fill_n(result, n, x);
            return result;
        }
        __STL_UNWIND(data_allocator::deallocate(result, n));
    }
  • allocate_and_copy
//allocate_and_copy整体和allocate_and_fill类似,只是他们初始化未初始化空间调用的函数不同
#ifdef __STL_MEMBER_TEMPLATES
  template <class ForwardIterator>
  iterator allocate_and_copy(size_type n,
                             ForwardIterator first, ForwardIterator last) {
    iterator result = data_allocator::allocate(n);
    __STL_TRY {
      uninitialized_copy(first, last, result);
      return result;
    }
    __STL_UNWIND(data_allocator::deallocate(result, n));
  }
#else /* __STL_MEMBER_TEMPLATES */
  iterator allocate_and_copy(size_type n,
                             const_iterator first, const_iterator last) {
    iterator result = data_allocator::allocate(n);
    __STL_TRY {
      uninitialized_copy(first, last, result);
      return result;
    }
    __STL_UNWIND(data_allocator::deallocate(result, n));
  }
  • range_initialize
//根据迭代器的不同版本,重载了不同版本
 template <class InputIterator>
  void range_initialize(InputIterator first, InputIterator last,
                        input_iterator_tag) {
    for ( ; first != last; ++first)
      push_back(*first);
  }

  // This function is only called by the constructor.  We have to worry
  //  about resource leaks, but not about maintaining invariants.
  template <class ForwardIterator>
  void range_initialize(ForwardIterator first, ForwardIterator last,
                        forward_iterator_tag) {
    size_type n = 0;
    distance(first, last, n);
    start = allocate_and_copy(n, first, last);
    finish = start + n;
    end_of_storage = finish;
  }
总结

我们介绍了vector的数据结构,构造及内存分配的相关内容。
之后我们将继续介绍vector的相关操作。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值