您的位置:首页 > 其它

STL学习笔记--4、序列式容器之vector

2016-05-28 17:53 453 查看
常见的数据结构:array数组,list链表,tree树,stack栈,queue队列,hash table散列表,set集合,map映射……

根据数据在容器中的排列分为:序列式sequence和关联式associative。

序列式容器之vector

1、vector VS array:

array是静态空间,一旦配置则无法改变;

vector是动态空间,随着元素的加入,内部机制会自动扩充新的空间来容纳新的元素。

实现技术:对大小的控制和重新配置时的数据移动效率。

扩充空间:配置新空间、数据移动、释放旧空间。

2、定义摘要:

在SGI中,
#include<stl_vector.h>


在STl标准中只需要
#include<vector>


template <class T, class Alloc = alloc>
class vector {
public:
//嵌套型别定义
typedef T value_type;
typedef value_type* pointer;
//迭代器类型为随机存取类型
//RandomAccessIterator
typedef value_type* iterator;
typedef value_type& reference;
typedef size_t size_type;

protect:
//方便以元素为单位配置大小
typedef simple_alloc<value_type, Alloc> data_allocator;

//表示目前使用的空间头
iterator start;
//表示目前使用的空间尾
iterator finish;
//表示目前可用的空间尾
iterator end_of_storage;

//插入元素。有备用空间直接插入,无备用空间则进行空间分配
void insert_aux(iterator position, const T& x);

//调用全局deallocate来完成
void deallocate() {
if (start)
data_allocator::deallocate(start,end_of_storage - start);
}

void fill_initialize(size_type n, const T& value)
{
start = allocate_and_fill(n, value);
finish = start + n;
end_of_storage = finish;
}
public:
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() const { return begin() == end(); }
reference operator[](size_type n) { return *(begin() + n); }

//构造函数
//默认构造函数
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() {
destroy(start, finish);
deallocate();
}

//第一个元素引用
reference front() { return *begin(); }
//最后一个元素引用
reference back() { return *(end() - 1); }

//push_back,调用了insert_aux
void push_back(const T& x) {
if (finish != end_of_storage) {//还有备用空间
construct(finish, x);
++finish;
}
else//以无备用空间
insert_aux(end(), x);
}

//pop_back()
void pop_back() {
--finish;
destroy(finish);
}

//erase版本一:接受一个迭代器
iterator erase(iterator position) {
if (position + 1 != end())
copy(position + 1, finish, position);
--finish;
destroy(finish);
return position;
}
//erase版本二:接受二个迭代器。清除迭代器范围的元素
iterator erase(iterator first, iterator last) {
iterator i = copy(last, finish, first);
destroy(i, finish);
finish = finish - (last - first);
return first;
}

//resize():为容器重新定义长度。
//若new_size小于size则,丢弃多余部分
//若new_size大于size则,空出的部分进行T类型的值初始化
void resize(size_type new_size, const T& x) {
if (new_size < size())
erase(begin() + new_size, end());
else
insert(end(), new_size - size(), x);
}

void resize(size_type new_size){resize(new_size, T());}

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

protected:
//配置容器并填满内容,填n个x
iterator allocate_and_fill(size_type n, const T& x) {
iterator result = data_allocator::allocate(n);
__STL_TRY {
uninitialized_fill_n(result, n, x);
return result;
}
__STL_UNWIND(data_allocator::deallocate(result, n));
}


3、vector迭代器

vector维护的是连续线性空间。支持随机存取,迭代器类型为RandomAccessIterator。

4、vector数据结构

//表示目前使用的空间头
iterator start;
//表示目前使用的空间尾
iterator finish;
//表示目前可用的空间尾
iterator end_of_storage;


vector实际配置的大小总是大于等于客户端需求的大小,以备将来的扩充。

增加新元素时,如果超出当时容量,则容量进行两倍扩充;若两倍容量不足时,则扩充足够大的容量。

容量的扩充需要进行:重新配置、元素移动、释放旧空间

5、构造和内存管理:

vector缺省使用alloc作为空间配置器;

uninitialized_fill_n()
根据第一个参数的型别特性type traits决定算法使用(
__true_type
)fill_n()或是反复调用(
__false_type
)construct()来完成。

//push_back,调用了insert_aux
void push_back(const T& x) {
if (finish != end_of_storage) {//还有备用空间
construct(finish, x);
++finish;
}
else//以无备用空间
insert_aux(end(), x);
}


算法实现:insert_aux(end(), x);

template <class T, class Alloc>
void vector<T, Alloc>::insert_aux(iterator position, const T& x) {
if (finish != end_of_storage) {//还有备用空间
//在备用空间起始处构造元素,以finish - 1位置处的值作为初值
construct(finish, *(finish - 1));
++finish;
T x_copy = x;
//将[position,finish-2)处的值拷贝到[(finish - 1)-((finish - 2)-(position)),finish - 1)
copy_backward(position, finish - 2, finish - 1);
*position = x_copy;
}
else {//无备用空间
const size_type old_size = size();
//确定新构造的空间的长度
//若旧空间为0,则构造1个
//若旧空间非零,则构造旧空间的两倍
const size_type len = old_size != 0 ? 2 * old_size : 1;

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

__STL_TRY {
//旧空间的元素拷贝到新空间
new_finish = uninitialized_copy(start, position, new_start);
//新元素指定为x
construct(new_finish, x);
//调整使用空间大小
++new_finish;
//新元素装入
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(begin(), end());
deallocate();

//调整迭代器,指向新空间
start = new_start;
finish = new_finish;
end_of_storage = new_start + len;
}
}


6、元素操作:erase()

两个参数版本,释放局部区间的清除操作示意图:



7、元素操作:insert()

insert(iterator position, size_type n, const T& x)

1、若备用空间大于等于新增元素个数
(end_of_storage-finish) >= n

计算插入点之后现有的元素个数
elems_after=finish-position;

情况一:1)、若**插入点之后的现有元素个数**大于**新增元素个数**
elems_after>n
{
uninitialized_copy(finish - n, finish, finish);
finish += n;
copy_backward(position, old_finish - n, old_finish);
fill(position, position + n, x_copy);
}

情况二:2)、若**插入点之后的现有元素个数**小于等于**新增元素个数**
elems_after<=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);
}
2、若备用空间小于新增元素个数
(end_of_storage-finish) < n

1)决定新的长度。旧长度的两倍或旧长度+新增元素个数。
len=old_size+max(old_size,n)

2)配置内存空间
iterator new_start =data_allocator::allocate(len);
iterator new_finish = new_start;

情况三:3)数据元素的移动
{
//旧空间的插入点之前的元素copy到新空间
new_finish = uninitialized_copy(start, position, new_start);
//新增的元素初值为x,填入到新空间
new_finish = uninitialized_fill_n(new_finish, n, x);
//旧空间的插入点之后的元素copy到新空间
new_finish = uninitialized_copy(position, finish, new_finish);
}

3、如果分配新空间出现异常,则销毁新分配的内存,抛出异常
# ifdef  __STL_USE_EXCEPTIONS
catch(...) {
destroy(new_start, new_finish);
data_allocator::deallocate(new_start, len);
throw;
}
#endif /* __STL_USE_EXCEPTIONS */

4、清除并释放旧空间内存
destroy(start, finish);
deallocate();

5、调整迭代器,指向新空间
start = new_start;
finish = new_finish;
end_of_storage = new_start + len;


情况一:备用空间充足,且elems_after>n

n=2; elems_after=2; 备用空间end_of_storage-finish=2;



情况二:备用空间充足,且elems_after<=n

n=4; elems_after=2; 备用空间end_of_storage-finish=3;



情况三:备用空间不足容纳新增元素

n=3; 备用空间end_of_storage-finish=2;

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  stl