vector
在向vector插入元素时,空间够,正常插入,如果空间不足则调用insert_aux辅助函数(不只被push_back调用,在实现时不仅仅满足vector需求)
insert_aux的实现
保存原来空间的大小,在扩充时以两倍扩充。
8->16;finsh = new_start;
然后将原来的数据拷贝到新vector,原来的vector销毁(涉及到大量的拷贝构造和析构函数,花费大量的开销)
vector iterator
概述
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。
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; }
...
}
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;
}
}
}