【STL源码剖析】vector
一、vector 概述
vector 的数据安排以及操作方式,与 array 非常相似。两者的唯一差别在于空间的运用的灵活性。array 是静态空间,一旦配置了就不能改变。 vector 是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素。因此, vector 的运用对于内存的合理利用与运用的灵活性有很大的帮助,我们再也不必因为害怕空间不足而开始就要求一个大块头 array 了,我们可以安心使用 vector,吃多少用多少。
vector 的实现技术,关键在于其对大小的控制以及重新配置时的数据移动效率。一旦 vector 旧有空间满载,如果客户端每新增一个元素, vector 内部只是扩充一个元素的空间,实为不智,因为所谓扩充空间(不论多大),一如稍早所说是 “配置新空间/数据移动/释还旧空间” 的大工程,时间成本很高,应该加人某种未雨绸缪的考虑。稍后我们便可看到 SGI vector 的空间配置策略。
二、vector 定义摘要
以下是 vector 定义的源代码摘录。虽然 STL 规定,欲使用 vector 者必须先包括<vector>
,但 SGI STL 将 vector 实现于更底层的<stl_vector h>
。
以下是 vector 定义的部分源代码摘录:
// alloc是SGI STL的空间配置器,见第二章
template <class T, class Alloc = alloc>
class vector
{
public:
// vector的嵌套类型定义
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;
protected:
// 这个提供STL标准的allocator接口
typedef simple_alloc <value_type, Alloc> data_allocator;
iterator start; // 表示目前使用空间的头
iterator finish; // 表示目前使用空间的尾
iterator end_of_storage; // 表示实际分配内存空间的尾
void insert_aux(iterator position, const T& x);
// 释放分配的内存空间
void deallocate()
{
// 由于使用的是data_allocator进行内存空间的分配,
// 所以需要同样使用data_allocator::deallocate()进行释放
// 如果直接释放, 对于data_allocator内部使用内存池的版本
// 就会发生错误
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 max_size() const { return size_type(-1) / sizeof(T); }
// 返回重新分配内存前最多能存储的对象个数
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不分配内存空间
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(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;
}
~vector()
{
// 析构对象(全局函数,见2.2.3节)
destroy(start, finish);
// 释放内存(这是 vector 的一个 member function)
deallocate();
}
vector<T, Alloc>& operator=(const vector<T, Alloc>& x);
// 提供访问函数
reference front() { return *begin(); }
reference back() { return *(end() - 1); }
void push_back(const T& x)
{
// 内存满足条件则直接追加元素, 否则需要重新分配内存空间
if (finish != end_of_storage)
{
construct(finish, x);
++finish;
}
else
insert_aux(end(), x);
}
iterator insert(iterator position, const T& x)
{
size_type n = position - begin();
if (finish != end_of_storage && position == end())
{
construct(finish, x);
++finish;
}
else
insert_aux(position, x);
return begin() + n;
}
iterator insert(iterator position) { return insert(position, T()); }
void pop_back()
{
--finish;
destroy(finish);
}
iterator erase(iterator position)
{
if (position + 1 != end())
copy(position + 1, finish, position);
--finish;
destroy(finish);
return position;
}
iterator erase(iterator first, iterator last)
{
iterator i = copy(last, finish, first);
// 析构掉需要析构的元素
destroy(i, finish);
finish = finish - (last - first);
return first;
}
// 调整size, 但是并不会重新分配内存空间
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()); }
void clear() { erase(begin(), end()); }
protected:
// 分配空间, 并且复制对象到分配的空间处
iterator allocate_and_fill(size_type n, const T& x)
{
iterator result = data_allocator::allocate(n);
uninitialized_fill_n(result, n, x);
return result;
}
};
三、vector 的迭代器
vector 维护的是一个连续线性空间,所以不论其元素型别为何,普通指针都可以作为 vector 的迭代器而满足所有必要条件,因为 vector 迭代器所需要的操作行为,如operator*
, operator->
, operator++
, operator--
, operator+
, operator operator+=
, operator-=
,普通指针天生就具备。 vector 支持随机存取,而普通指针正有着这样的能力。
template <class T, class Alloc=alloc>
class vector {
public:
typedef T value_type;
typedef value_type* iterator; // vector的迭代器是普通指针
...
};
根据上述定义,如果客户端写出这样的代码:
vector<int>::iterator ivite;
vector<Shape>::iterator svite;
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 实际分配空间大小的时候会比客端需求的大一些,以便于未来的可能的扩展。
运用这三个迭代器成员,就能提供 begin、end、size 等函数:
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; }
...
}
为了降低空间配置时的速度成本, vector 实际配置的大小可能比客户端需求量更大一些,以备将来可能的扩充。这便是容量( capacity)的观念。换句话说,一个 vector 的容量永远大于或等于其大小。一旦容量等于大小,便是满载,下次再有新增元素,整个 vector 就得另觅居所,见下图:
参考:
《STL源码剖析》