STL之vector容器底层实现

vector

在向vector插入元素时,空间够,正常插入,如果空间不足则调用insert_aux辅助函数(不只被push_back调用,在实现时不仅仅满足vector需求)

image-20201119104415549

insert_aux的实现

保存原来空间的大小,在扩充时以两倍扩充。

8->16;finsh = new_start;

然后将原来的数据拷贝到新vector,原来的vector销毁(涉及到大量的拷贝构造和析构函数,花费大量的开销)

image-20201119104959567

vector iterator

image-20201119110804597

概述

vector的数据安排及操作方式,与数组非常相似,但数组是静态空间,配置了就不能改变,vector是动态空间,他的内部机制会自行扩充空间容纳新元素。

vector的实现技术在于其对大小的控制及重新配置时的数据移动。

vector结构

使用原生指针和RandomAccess Iterators随机存取迭代器

// alloc是SGI STL的默认空间配置器
template <class T, class Alloc=alloc>
class vector
{
public:
   typedef T value_type;
   typedef value_type* pointer;
   typedef value_type* iterator;  
   typedef value_type& reference;
   typedef size_t size_type;
   typedef ptrdiff_t difference_type;
 }

vector维护的是一个连续线性空间,所以不论其元素型别为何,普通指针都可以作为vector的迭代器而满足所有必要条件,因为vector迭代器所需要的操作行为,如operator*,operator->,operator++,operator–,operator+,operator-,operator+=,operator-=,普通指针天生就具备。

vector支持随机存取,而普通指针正有着这样的能力。所以,vector提供的是RandomAccess Iterators。

image-20201124212636764

ivite的型别其实就是int* svite的型别其实就是shape*

vector的数据结构

所拥有的变量

template <class T,class Alloc=alloc>
class vector
{
protected:
     iterator start;          // 目前使用的空间头
     iterator finish;         // 目前使用的空间尾
     iterator end_of_storage; // 目前可用的空间尾

}

vector采用的数据结构很简单,就是连续线性空间。以上定义中迭代器start和finish之间是已经被使用的空间范围,end_of_storage是整块连续空间包括备用空间的尾部。end_of_storage存在的原因是为了降低空间配置的成本,vector实际分配空间大小的时候会比客端需求的大一些,以便于未来的可能的扩展。

运用start,finish,end_of_storage三个迭代器,便可轻易地提供首尾标示、大小、容量、空容器判断、注标([])运算子、最前端元素值、最后端元素值…等机能:

所拥有的成员方法

template <class T,class Alloc=alloc>
class vector
{
...
pubic:
     iterator begin() { return start; }
     iterator end() { return finish; }
     size_type size() const { return size_type(end() - begin()); }
     size_type capacity() const { return size_type(end_of_storage - begin()); }
     bool empty() { return begin() = end(); }
     reference front { return *begin(); }
     reference operator[](size_type n) { return *(begin() + n); }
     reference front() { return *begin(); }
     reference back() { return *end() - 1; }     
...
}

image-20201124213148155

vectord的构造与内存管理

vector提供了很多的构造函数,以下例举一些:

template <class T, class Alloc = alloc>  
class vector {
public:
    // 默认构造空的vector
    vector() : start(0), finish(0), end_of_storage(0) {}
    
    // 构造大小为n,初始值为都value的vector
    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); }
    
    // 不允许隐式转换,构造大小为n,初始值为都元素默认初始值的vector
    explicit vector(size_type n) { fill_initialize(n, T()); }
    
    // 复制构造函数
    vector(const vector<T, Alloc>& x) {
        start = allocate_and_copy(x.end() - x.begin(), x.begin(), x.end());
        finish = start + (x.end() - x.begin());
        end_of_storage = finish;
    }

protected:
    // 使用了simple_alloc,每次配置一个元素大小
    typedef simple_alloc<value_type, 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_copy(size_type n,const_iterator first, const_iterator last) 
{
        iterator result = data_allocator::allocate(n);  // 分配空间
        uninitialized_copy(first, last, result);  // 将迭代器[first,last)之间的内容拷贝到result开始的空间
        return result;
}
    // 配置空间并填满内容
    iterator allocate_and_fill(size_type n, const T& x) 
    {
        iterator result = data_allocator::allocate(n);
        uninitialized_fill_n(result, n, x);//会根据第一参数的特性决定使用fill_n或反复调用construct来完成
        return result;
    }
};

push_back的实现

当使用**push_back()**将元素插入尾部时,会先检查是否有备用空间,如果有就直接在备用空间中构造,并调整迭代器finish。否则,就需要进行扩展空间:

template <class T, class Alloc = alloc>
void vector::push_back(const T& x)
{
    if (finish != end_of_storage) {  
        // 还有备用空间,直接构造
        construct(finish, x);
        ++finish;
    }
    else
        insert_aux(finish, x);  // 见后续代码
}

// 在某个位置插入元素
template <class T, class Alloc = alloc>
void  vector::insert_aux(iterator position, const T& x)
{
    if (finish != end_of_storage) {
        // 还有备用空间
        // 将最后一位元素构造给备用空间中的第一位
        construct(finish, *(finish - 1));
        ++finish;

        // 把[position,finish - 2]的元素后移一位
        T x_copy = x;
        copy_backward(position, finish - 2, finish - 1);  // 见后续代码
        *position = x_copy;
    }
    else {
        // 没有备用空间,需要扩展,扩展方式为抛原有空间重新分配
        // 原则为:原大小为0,则配置1个;否则,配置原有的两倍。为什么是两倍,详见https://blog.csdn.net/yangshiziping/article/details/52550291
        const size_type old_size = size();
        const size_type new_size = old_size == 0 ? 1 : 2 * old_size;

        iterator new_start = data_allocator::allocate(new_size);
        iterator new_finish = new_start;

        try
        {
            // 将原有的position前的内容拷贝到新的空间
            // 赋值position位置为x
            // 将position后的内容拷贝到新的空间
            new_finish = uninitialized_copy(start, position, new_start);
            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, new_size);
            throw;
        }

        // 析构并释放原有空间
        destroy(begin(), end());
        deallocate();

        // 调整迭代器位置指向新的空间
        start = new_start;
        finish = new_finish;
        end_of_storage = new_start + new_size;
    }
}

// 将[first,last]按倒序赋值给[first - (result-last), last - (result-last)]
// (result-last)大于0时,相当于将元素[first,last]后移(result-last)位
// (result-last)小于0时,相当于将元素[first,last]前移(result-last)位
template<class BidirectionalIterator1, class BidirectionalIterator2>
BidirectionalIterator2 copy_backward ( BidirectionalIterator1 first,
                                       BidirectionalIterator1 last,
                                       BidirectionalIterator2 result )
{
    while (last != first) *(--result) = *(--last);
    return result;
}

vector的注意点:空间重新配置后

注意,所谓动态增加大小,并不是在原空间之后接续新空间(因为无法保证原 空间之后尚有可供配置的空间),而是以原大小的两倍另外配置一块较大空间,然 后将原内容拷贝过来,然后才开始在原内容之后构造新元素,并释放原空间。

因此, 对vector的任何操作, 一且引起空间重新配置,指向原vector的所有迭代器就都失效了。 这是程序员易犯的一个错误, 务需小心。

空间扩展的测试代码:

#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    vector<int> vec;
    cout << "size()=" << vec.size() << endl;
    cout <<"capacity()=" << vec.capacity() <<endl;

    vec.push_back(0);
    cout << "size()=" << vec.size() << endl;
    cout <<"capacity()=" << vec.capacity() <<endl;

    vec.push_back(0);
    cout << "size()=" << vec.size() << endl;
    cout <<"capacity()=" << vec.capacity() <<endl;

    vec.push_back(0);
    cout << "size()=" << vec.size() << endl;
    cout <<"capacity()=" << vec.capacity() <<endl;

    vec.push_back(0);
    cout << "size()=" << vec.size() << endl;
    cout <<"capacity()=" << vec.capacity() <<endl;

    vec.push_back(0);
    cout << "size()=" << vec.size() << endl;
    cout <<"capacity()=" << vec.capacity() <<endl;

    vec.pop_back();
    cout << "size()=" << vec.size() << endl;
    cout <<"capacity()=" << vec.capacity() <<endl;

    vec.pop_back();
    cout << "size()=" << vec.size() << endl;
    cout <<"capacity()=" << vec.capacity() <<endl;

    vec.clear();
    cout << "size()=" << vec.size() << endl;
    cout <<"capacity()=" << vec.capacity() <<endl;

    return 0;
}

// 输出结果
size()=0
capacity()=0
size()=1
capacity()=1
size()=2
capacity()=2  // 翻倍
size()=3
capacity()=4  // 翻倍
size()=4
capacity()=4
size()=5
capacity()=8  // 翻倍
size()=4
capacity()=8
size()=3
capacity()=8
size()=0
capacity()=8

vector的元素操作

vector提供的元素操作函数很多,下面只举例常用的几个。

// 移除最后一个元素
void pop_back()
{
    --finish;
    destroy(finish);
}

// 移除指定迭代器的元素
iterator erase(iterator position)
{
    if (position + 1 != end())
        copy(position + 1, finish, position);

    --finish;
    destroy(finish);
    return position;
}

// 移除[first,last)之间的元素
iterator erase(iterator first, iterator last)
{
    iterator i = copy(last, finish, first);
    // 析构掉需要析构的元素
    destroy(i, finish);
    finish = finish - (last - first);
    return first;
}

// 清空
void clear()
{
    erase(begin(), end());
}

// 在指定位置插入n个x
template <class T, class Alloc>
void insert(iterator position, size_type n, const T& x)
{
    if (n != 0)
    {
        if (size_type(end_of_storage - finish) >= n)
        {      
            // 备用空间大于等于“新增元素个数”
            T x_copy = x;
            // 以下计算插入点后面的现有元素个数
            const size_type elems_after = finish - position;
            iterator old_finish = finish;
            if (elems_after > n)
            {
                // 插入点后面的现有元素个数大于新增元素个数的情况
                uninitialized_copy(finish - n, finish, finish);
                finish += n;    
                // 从position开始向后移n位
                copy_backward(position, old_finish - n, old_finish);
                fill(position, position + n, x_copy); // 从插入点开始填入新值
            }
            else
            {
                // 插入点后面的现有元素个数小于等于新增元素个数的情况
                // 无法和大于的情况一样使用copy_backward(position, old_finish - n, old_finish),position会大于old_finish - n
                uninitialized_fill_n(finish, n - elems_after, x_copy);
                finish += n - elems_after;
                uninitialized_copy(position, old_finish, finish);
                finish += elems_after;
                fill(position, old_finish, x_copy);
            }
        }
        else
        {
            // 备用空间小于“新增元素个数”(那就必须配置额外的内存)
            // 首先决定新长度:旧长度的两倍,或旧长度+新增元素个数
            const size_type old_size = size();
            const size_type len = old_size + max(old_size, n);
            // 以下配置新的vector空间
            iterator new_start = data_allocator::allocate(len);
            iterator new_finish = new_start;
            __STL_TRY
            {
                // 以下首先将旧的vector的插入点之前的元素复制到新空间
                new_finish = uninitialized_copy(start, position, new_start);
                // 以下再将新增元素(初值皆为n)填入新空间
                new_finish = uninitialized_fill_n(new_finish, n, x);
                // 以下再将旧vector的插入点之后的元素复制到新空间
                new_finish = uninitialized_copy(position, finish, new_finish);
            }
#ifdef  __STL_USE_EXCEPTIONS
            catch(...)
            {
                destroy(new_start, new_finish);
                data_allocator::deallocate(new_start, len);
                throw;
            }
#endif /* __STL_USE_EXCEPTIONS */

            destroy(start, finish);
            deallocate();
            start = new_start;
            finish = new_finish;
            end_of_storage = new_start + len;
        }
    }
}

image-20201124215750664

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值