vector概述
vector的数据存储以及操作方式和array非常相似。两者唯一的区别在于内存空间的灵活性。
array在初始化完成后就无法改变大小了;如果要扩大内存空间则必须人为申请一块新的足够大的内存,然后将原来的数据拷贝到新的地址中,最后释放原来的内存。
vector的扩容操作由其内部机制完成,当数据满了后,vector内部会申请比原来大一倍(甚至更大)的内存空间,而不是大一个元素的内存,因为“申请新内存->拷贝数据->释放旧内存”一系列操作非常耗时,所以在设计扩容机制时必须考虑减少扩容次数。
vector数据结构
vector在数据结构是一块线性连续内存空间。由两个迭代器start和finish分别指向内存中已添加元素的范围,迭代器end_of_storage指向整块内存的末尾。
下面通过代码和图例来详细介绍vector的数据结构:
vector<int> vecIn;
vecIn.push_back(1);
vecIn.push_back(2);
vecIn.push_back(3);
vecIn.push_back(4);
vecIn.push_back(5);
上面代码对应内存的状态如下:
vector主要接口源码剖析
因为最新版本的vector源码涉及到很多我不懂的新语法,考虑到代码可读性,下面列举的是在《STL源码剖析》一书中选用的C++2.91.57 for Windows版本的源码。
push_back()
void push_back(const T& x) {
if (finish != end_of_storage) { // 表示备用空间够大
construct(finish, x); // 在finish位置上执行T::T(x)操作,相当于在finish位置创建对象
++finish;
}
else
insert_aux(end(), x); // 没有备用空间可用时,需要扩容,此函数详细展开了
}
pop_back()
void pop_back() {
--finish; // 该迭代器指向最后一个元素的起始地址
destroy(finish); // destroy函数的本质是调用了对应类的析构函数来释放对象
}
erase()
iterator erase(iterator first, iterator last) {
/*
iteratpr copy(first, last, result)函数主要是将first到last的所有元素拷贝到result中
返回值是一个迭代器,指向复制后result最后一个元素的末尾
*/
iterator i = copy(last, finish, first);
destroy(i, finish); // 逐个调用析构函数释放对象
finish = finish - (last - first);
return first;
图示详解:
clear()
void clear() {
erase(begin(), end());
}
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) {
if (size_type(end_of_storage - finish) >= n) { // 当备用空间够大时
T x_copy = x;
const size_type elems_after = finish - position; // 在插入位置之后的元素个数
iterator old_finish = finish; // 备份finish迭代器
if (elems_after > n) { // 插入点之后的元素个数 大于 插入元素个数
uninitialized_copy(finish - n, finish, finish);
finish += n;
copy_backward_copy(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);
iterator new_start = data_allocator::allocate(len);
iterator new_finish = new_start;
__STK_TRY {
new_finish = uninitialized_copy(start, position, new_start);
new_finish = uninitialized_fill_n(new_finish, n, x);
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;
}
}
}
插入方式图示详解:
-
插入点之后的元素个数 大于 插入元素个数
-
插入点之后的元素个数 小于等于 插入元素个数