chapter4 序列式容器:vector

1 前言

在这里插入图片描述
序列式容器:其中的元素都可序,但未必有序

2 vector

2.1 与array的区别

  1. array是静态空间,一旦配置了就不能改变

  2. vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素

2.2 vector的迭代器

vector维护一个线性连续空间,所以不论其元素型别为何,普通指针都可以作为vector的迭代器而满足所有必要条件。所以vector的迭代器是指向元素型别的普通指针,支持随机存取,其类型为Random Access Iterators。

template <class T, class Alloc = alloc>
class vector {
public:
    // vector 的嵌套型别定义
    typedef T value_type;
    typedef value_type* iterator;       //vector 的迭代器是普通指针
...
};

2.3 vector的数据结构

vector采用的数据结构为线性连续空间

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

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

容量:为降低空间配置的速度成本,vector实际配置的大小要比客户端需求量更大一些,以备扩充。

一个vector的容量永远大于或等于其大小,一旦容量等于大小,便是满载。再有新增元素,则需要将容量扩充为原先的两倍。实际需要经历“重新配置、元素移动、释放原空间”这三步。

在这里插入图片描述

2.4 vector的构造与内存管理

2.4.1 定义data_allocator进行空间配置

vector缺省使用alloc作为空间配置器,并据此定义一个data_allocator,为的是更方便以元素大小为配置单位

template<class T, class Alloc = alloc>
class vector {
protected:
    typedef simple_alloc<value_type, Alloc> data_allocator;
...
};

data_allocator::allocate(n)表示配置n个元素空间

2.4.2 提供允许指定空间大小及初值的constructor

//构造函数,允许指定 vector 大小 n 和初值 value
vector(size_type n, const T& value) { fill_initialize(n, value); }

//填充并予以初始化
iterator 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& value) {
    iterator result = data_allocator::allocate(n);
    uninitialized_fill_n(result, n, value);
    return result;
}

uninitialized_fill_n()会根据result的型别特性(type traits)决定使用算法fill_n()或反复调用construct()来完成任务

2.4.3 push_back()

push_back()将新元素插入vector尾端时,该函数首先检查是否还有备用空间,如果有直接在备用空间上构造元素,并调整迭代器finish,使vector变大,如果没有备用空间,就扩充空间(重新配置、移动数据、释放原空间)。

void push_back(const T& x) {
	if(finish != end_of_storage) {	//还有备用空间
        construct(finish, x);		//全局函数,将初值x设定到指针所指向的空间
        ++finish;					//调整水位
    }
    else 							//已无备用空间
        insert_aux(end(), x);
}

insert_aux()实现细节:

template <class T, class Alloc>
void vector<T, Alloc>::insert_aux(iterator position, const T& x) {
    if(finish != end_of_storage) { //还有备用空间
        //...
    }
    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 {
            //将原 vector 的内容拷贝到新 vector
            new_finish = uninitialized_copy(start, position, new_start);
            //为新元素设定初值 value
            construct(new_finish, value);
            ++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的所有迭代器就都失效了。

2.5 vector的元素操作:pop_back,erase,clear,insert

2.5.1 pop_back

删除尾端元素

void pop_back() {
    --finish;			 //将尾端标记往前移一格,表示将放弃尾端元素
    destory(finish);	 //destory是全局函数
}

2.5.2 erase

  1. 清除[first, last)中的所有元素
iterator erase(iterator first, iterator last) {
    iterator i = copy(last, finish, first);	//copy是全局函数
    destory(i, finish);
    finish = finish - (last - first);
    return first;
}
  1. 清除某个位置上的元素
iterator erase(iterator position) {
    if(position + 1 != end()) copy(position + 1, finish, position);
    --finish;
    destory(finish);
    return position;
}

在这里插入图片描述

2.5.3 clear

void clear() { erase(begin(), end()); }

2.5.4 insert

//从position开始,插入n个元素,元素初值为x
template <class T, class Alloc>
void vector<T, Alloc>::insert(iterator position, size_type n, const T& x) {
    if(n != 0) { //当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;	//将vector尾端标记向后移
                copy_backward(position, old_finish - n, old_finish);
                fill(position, position + n, x_copy); //从插入点开始填入新值
            }
            else {
                //“插入点之后的现有元素个数”小于等于“新增元素个数”
                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(...) {
                //如有异常发生,实现“commit or rollback” semantics
                destory(new_start, new_finish);
                data_allocator::deallocate(new_start, len);
                throw;
            }
            #endif /* __STL_USE_EXCEPTIONS */
            //以下清除并释放旧的vector
            destory(start, finish);
            deallocate();
            //以下调整水位标记
            start = new_start;
            finish = new_finish;
            end_of_storage = new_start + len;
        }   
    }
}

备用空间大于新增元素个数:

  1. 插入点之后的现有元素个数大于新增元素个数
    在这里插入图片描述2. 插入点之后的现有元素个数小于新增元素个数
    在这里插入图片描述备用空间小于新增元素个数:
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值