C++ STL: vector使用及源码剖析

vector使用

vector定义

语句

作用  

vector a(n);

指定容器大小为n     

vector a(n, x);

指定容器大小为n,并初始化所有元素为x

vector> a(m, vector(n));

m行n列的二维数组,可以直接用a[i][j]访问

访问vector容器中元素的操作

语句

作用

vec.at(index)

返回由index指定的位置上的元素

vec[index]

返回由index指定的位置上的元素

vec.front()

返回第一个元素(不检查容器是否为空)

vec.back()

返回最后一个元素(不检查容器是否为空)

vector容器大小相关的操作

语句

作用

vec.capacity()

返回不重新分配空间可以插入到容器vec中的元素的最大个数

vec.empty()

容器vec为空,返回true;否则,返回false

vec.size()

返回容器vec中当前的元素个数

vec.max_size()

返回可以插入到容器vec中的元素的最大个数

vec.resize(num)

将元素个数改为num。如果size()增加,默认的构造函数负责创建这些新元素

vec.resize(num, elem)

将元素个数改为num。如果size()增加,这些新元素初始化为elem

vec.reserve(num)

保留足够空间以容纳num个元素,避免在达到num之前的插入操作中重新分配空间

元素插入、元素删除、遍历向量容器中的元素

语句

作用

vec.clear()

从容器中删除所有元素

vec.erase(position)

删除由position指定的位置上的元素

vec.erase(beg, end)

删除从beg到end-1之间的所有元素

vec.insert(position, elem)

将elem的一个拷贝插入到由position指定的位置上,并返回新元素的位置

vec.insert(position, n, elem)

将elem的n个拷贝插入到由 position指定的位置上

vec.insert(position, beg, end)

将从beg到end-1之间的所有元素的拷贝插入到vec中由position指定的位置上

vec.push_back(elem)

将elem的一个拷贝插入到vector的末尾

vec.pop_back()

删除最后元素


vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新的元素。vector的实现技术,关键在于对大小的控制以及重新配置时的数据移动效率。

vector的数据结构:

 vector采用线性连续空间,以两个迭代器start和finish分别指向配置得来的连续空间中目前已被使用的范围,并以迭代器end_of_storage指向整块连续空间(含备用空间)的尾端。

templateclass vector{
    ...protected:
    iterator start;                  //表示目前使用的空间的头
    iterator finish;                 //表示目前使用的空间的尾
    iterator end_of_storage;    //表示目前可用的空间的尾...
};

vector的构造函数和析构函数:

emplate 
class vector {
...
protected:
  typedef simple_alloc data_allocator;
  ....

  void fill_initialize(size_type n, const T& value) {
        start = allocate_and_fill(n, value);
        finish = start + n;
        end_of_storage = finish;
  }
   // 分配空间并填满内容
   iterator allocate_and_fill(size_type n, const T& x) {
        iterator result = data_allocator::allocate(n);
        uninitialized_fill_n(result, n, x);
        return result;
    }
...
public:
    vector() : start(0), finish(0), end_of_storage(0) {}
    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);}
    explicit vector(size_type n) {fill_initialize(n, T());}
...
}

 上述中可以看到vector部分构造函数,其中默认构造函数只是把所有的迭代器都初始化为0,但它并没有申请内存空间。

另外4个大同小异,都向堆申请了大小为n的内存空间,只是初始化这些空间的时候进行的操作不一样而已。

这4个构造函数都用同一个函数fill_initialize()来进行堆空间的申请并且初始化。

void fill_initialize(size_type n, const T& value) {
        start = allocate_and_fill(n, value);
        finish = start + n;
        end_of_storage = finish;
}

它接受两个参数n和value,n指明了要申请的堆空间大小,value指明了要初始化这些堆空间的内容,并把它们传给另外一个函数allocate_and_fill() ,该函数才是真正的申请堆空间和初始化。

allocate_and_fill() 函数。
 // 分配空间并填满内容
iterator allocate_and_fill(size_type n, const T& x) {
         iterator result = data_allocator::allocate(n);
         uninitialized_fill_n(result, n, x);
         return result;
 }

函数接受来自fill_initialize()的两个参数然后申请堆空间并初始化。data_allocator实质上就是simple_alloc,simple_alloc是SGI STL的空间配置器。若果是SGI STL第一级配置器那么data_allocator::allocate()实质上就是直接调用c语言中的malloc()来申请堆空间;若是第二级配置器,就先考察申请区块是否大于128bytes,若是大于则转调用第一级配置器,否则就以内存池来管理,目的是为了避免太多小额区块造成内存碎片化 

vector的析构函数:

~vector() {
    destroy(start, finish);
dellocate();
}

析构函数很简单,就调用两个函数:destroy()和dellocate()。destroy()负责对象的析构,dellocate()负责释放申请的堆空间。这里释放的方式又与空间配置器相关。若果是第一级配置器,就直接调用c语言中free()函数,这正如申请时的简便。但若果是第二级配置器,则比较复杂一些上面的内容就是关于vector申请和释放堆空间的大概过程,但仅仅是申请和释放堆空间而已。

// 第一个版本
template 
inline void destroy(T* pointer) {
    pointer->~T();
}

 第一个版本接受一个指针,调用析构函数将该指针所指的对象析构。

// 第二个版本
template
inline void destroy(ForwardIterator first, ForwardIterator last, T*) {
    __destroy(first, last, value_type(first));
}

第二版本接受first和last两个迭代器,将[first, last)范围内的所有对象析构掉。 

 

vector的push_back函数:

void push_back(const T& x) { if (finish != end_of_storage) {construct(finish, x); ++finish;} 
else {insert_aux(end(), x);}
}

 当我们把元素push_back到vector的尾端后,函数首先检查是否还有备用的空间,如果有的话就调用construct()函数,在finish迭代器指定的位置上构建x对象,同时改变finish迭代器,使其自增1。没有备用空间,就需要扩充空间,调用insert_aux()函数。

insert_aux()如下:

template 
    void insert_aux(iterator position, const T& x)
    {
        if (finish != end_of_storage)    // 还有备用空间
        {
            // 在备用空间起始处构造一个元素,并以vector最后一个元素值为其初值
            construct(finish, *(finish - 1));
            ++finish;
            T x_copy = x;
            copy_backward(position, finish - 2, finish - 1);
            *position = x_copy;
        }
        else   // 已无备用空间
        {
            const size_type old_size = size();
            const size_type len = old_size != 0 ? 2 * old_size : 1;
            // 以上配置元素:如果大小为0,则配置1,如果大小不为0,则配置原来大小的两倍
            // 前半段用来放置原数据,后半段准备用来放置新数据
            iterator new_start = data_allocator::allocate(len);  // 实际配置
            iterator new_finish = new_start;
            // 将内存重新配置
            try
            {
                // uninitialized_copy()的第一个参数指向输入端的起始位置
                // 第二个参数指向输入端的结束位置(前闭后开的区间)
                // 第三个参数指向输出端(欲初始化空间)的起始处
                // 将原vector的安插点以前的内容拷贝到新vector
                new_finish = uninitialized_copy(start, position, new_start);
                // 为新元素设定初值 x
                construct(new_finish, x);
                // 调整已使用迭代器的位置
                ++new_finish;
                // 将安插点以后的原内容也拷贝过来
                new_finish = uninitialized_copy(position, finish, new_finish);
            }
            catch(...)
            {
                // 回滚操作
                destroy(new_start, new_finish);
                data_allocator::deallocate(new_start, len);
                throw;
            }
            // 析构并释放原vector
            destroy(begin(), end());
            deallocate();
            // 调整迭代器,指向新vector
            start = new_start;
            finish = new_finish;
            end_of_storage = new_start + len;
        }
}

Vector动态扩容:

当备用空间不足时,vector做了以下的工作:

1重新分配空间:若原来的空间大小为0,则扩充空间为1,否则扩充为原来的两倍。

2移动数据释放原空间更新迭代器当调用默认构造函数构造vector时,其空间大小为0但当我们push_back一个元素到vector尾端时,vector就进行空间扩展,大小为1,以后每当备用空间用完了,就将空间大小扩展为原来的两倍。

C++ STL: vector使用及源码剖析_第1张图片

动态增加大小,并不是在原空间之后接续新空间,(因为无法保证原空间之后上有可供分配的空间),而是以原大小的两倍来另外分配一块较大空间,因此,一旦空间重新分配,指向原vector的所有迭代器就会失。

你可能感兴趣的:(c++,c++,开发语言)