SGISTL源码阅读八 Vector容器上
Vector概述
array
是静态的数组,一经定义大小不可改变,这对于未知数组大小的情况来说是非常不利的,如果没有vector
我们只能猜测一个较大的值来初始化数组。这无疑对空间造成了很大程度上的浪费,而且如果初始值过小,就得必须自己重新定义一个更大的数组,并且将原来的数据拷贝一份,再释放掉原来的空间。
vector
被称为动态数组。随着元素的加入,它的内部机制会自行扩充空间以容纳新元素,它相对于array
来说,更加的灵活。
深入Vector源码
Vector的迭代器及其数据结构
//缺省使用SGISTL空间配置器
template <class T, class Alloc = alloc>
class vector {
public:
//vector的嵌套型别定义
typedef T value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type* iterator; //它的迭代器就是普通指针
typedef const value_type* const_iterator;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
我们知道vector
所维护的是一个连续的线性空间,普通指针就可以作为vector的迭代器。
(在iterator_traits
的学习中,我们知道指针的迭代器类型是random_access_iterator
。)
protected:
//...
iterator start; //表示目前使用空间的头
iterator finish; //表示目前使用空间的尾
iterator end_of_storage; //表示目前可用空间的尾
//...
public:
//由于vector内部的迭代器是保护类型的,所以需要一个对外的接口
iterator begin() { return start; }
const_iterator begin() const { return start; }
iterator end() { return finish; }
const_iterator end() const { return finish; }
//...
//返回vector的已使用空间
size_type size() const { return size_type(end() - begin()); }
size_type max_size() const { return size_type(-1) / sizeof(T); }
//返回vector的容量
size_type capacity() const { return size_type(end_of_storage - begin()); }
bool empty() const { return begin() == end(); }
这里我们可以看到vector
的结构是由三个迭代器来维护的,分别指向了vector
使用空间的头部,尾部,以及可用空间的尾部。
如图:
vector的构造与内存分配
vector的构造函数
//默认构造函数,不申请空间
vector() : start(0), finish(0), end_of_storage(0) {}
//接受两个参数,第一个参数 指定vector初始化大小,第二个参数为初始化值(所有空间都会被初始化成value)
//以下三个是针对不同情况的重载版本,他们都调用了fill_initialize函数
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); }
//接受一个参数,指定vector的初始化大小。
//详述
explicit vector(size_type n) { fill_initialize(n, T()); }
//拷贝构造函数
vector(const vector<T, Alloc>& x) {
//调用了allocate_and_copy函数
start = allocate_and_copy(x.end() - x.begin(), x.begin(), x.end());
finish = start + (x.end() - x.begin());
end_of_storage = finish;
}
//以下两个构造函数是根据first和last两个迭代器的范围来进行初始化
#ifdef __STL_MEMBER_TEMPLATES
template <class InputIterator>
vector(InputIterator first, InputIterator last) :
start(0), finish(0), end_of_storage(0)
{
//调用了range_initialize
range_initialize(first, last, iterator_category(first));
}
#else /* __STL_MEMBER_TEMPLATES */
vector(const_iterator first, const_iterator last) {
size_type n = 0;
distance(first, last, n);
start = allocate_and_copy(n, first, last);
finish = start + n;
end_of_storage = finish;
}
explicit vector(size_type n) { fill_initialize(n, T()); }
这个构造函数接受一个参数,指定了vector
的初始化大小。explicit
关键字是用来防止当只有一个传入参数时发生隐式转换的。
例如vector<int> v = 10;
,vector<int> v = 'a'
如果没有explicit
关键字,以上两种写法是正确的。因为在C++中, 如果的构造函数只有一个参数时, 那么在编译的时候就会有一个缺省的转换操作:将该构造函数对应数据类型的数据转换为该类对象. 也就是说 vector<int> v = 10;
这段代码, 编译器自动将整型转换为vector
类对象, 实际上等同于下面的操作:
vector<int> temp(10);
vector<int>v = temp;
vector的内存分配
在vector
的构造函数分析中,我们可以看到它们调用了fill_initialize
,allocate_and_copy
,range_initialize
函数,下面我们就来看一下这些函数的实现。
fill_initialize
fill_initializ
//维护了vector的三个迭代器,调用了allocate_and_fill函数
void fill_initialize(size_type n, const T& value) {
start = allocate_and_fill(n, value);
finish = start + n;
end_of_storage = finish;
}
allocate_and_fill
protected:
//typedef simple_alloc<value_type, Alloc> data_allocator;
//vector给SGISTL的空间配置器设置了一个别名
iterator allocate_and_fill(size_type n, const T& x) {
iterator result = data_allocator::allocate(n);
/* __STL_TRY...__STL_UNWIND类似异常处理的try...catch语句块
* 这段代码的大意就是,初始化allocate分配的未初始化空间
* 如果失败了,则将分配的内存回收,防止内存泄露
= */
__STL_TRY {
//调用初始化uninitialized_fill_n未初始化的空间
uninitialized_fill_n(result, n, x);
return result;
}
__STL_UNWIND(data_allocator::deallocate(result, n));
}
allocate_and_copy
//allocate_and_copy整体和allocate_and_fill类似,只是他们初始化未初始化空间调用的函数不同
#ifdef __STL_MEMBER_TEMPLATES
template <class ForwardIterator>
iterator allocate_and_copy(size_type n,
ForwardIterator first, ForwardIterator last) {
iterator result = data_allocator::allocate(n);
__STL_TRY {
uninitialized_copy(first, last, result);
return result;
}
__STL_UNWIND(data_allocator::deallocate(result, n));
}
#else /* __STL_MEMBER_TEMPLATES */
iterator allocate_and_copy(size_type n,
const_iterator first, const_iterator last) {
iterator result = data_allocator::allocate(n);
__STL_TRY {
uninitialized_copy(first, last, result);
return result;
}
__STL_UNWIND(data_allocator::deallocate(result, n));
}
range_initialize
//根据迭代器的不同版本,重载了不同版本
template <class InputIterator>
void range_initialize(InputIterator first, InputIterator last,
input_iterator_tag) {
for ( ; first != last; ++first)
push_back(*first);
}
// This function is only called by the constructor. We have to worry
// about resource leaks, but not about maintaining invariants.
template <class ForwardIterator>
void range_initialize(ForwardIterator first, ForwardIterator last,
forward_iterator_tag) {
size_type n = 0;
distance(first, last, n);
start = allocate_and_copy(n, first, last);
finish = start + n;
end_of_storage = finish;
}
总结
我们介绍了vector
的数据结构,构造及内存分配的相关内容。
之后我们将继续介绍vector
的相关操作。