【STL基础】序列式容器之vector
参照自文档http://www.cplusplus.com/reference/array/,教程http://c.biancheng.net/view/6688.html,和书籍《STL源码剖析》(侯捷)
定义:
template < class T, class Alloc = allocator<T> > class vector;
vector与array非常类似,但是其对于空间的操作更为灵活,vector使用动态空间。
动态增加空间并不是直接在原空间后接续新的空间,而是以原空间大小的两倍配置一块较大的空间,然后进行原数据的拷贝。
使用需要引入头文件
(一)初始化
方式一:创建了一个空的 vector 容器,并未分配空间。
std::vector<double> values;
为空容器分配20个容量的内存。注意,如果 vector 的容量在执行此语句之前,已经大于或等于 20 个元素,那么这条语句什么也不做。
values.reserve(20);
方式二:创建并初始化。
std::vector<double> values {2, 3, 5, 7, 11};
方式三:创建并指定元素个数,此时这二十个元素都被初始化为0.0。
std::vector<double> values(20);
方式四:创建并指定元素个数和初始化值,此时这二十个元素都被初始化为1.0。
std::vector<double> values(20, 1.0);
方式五:直接拷贝其他的vector。
std::vector<char> value1(5, 'c');
std::vector<char> value2(value1);
也可以选择性复制一部分元素(利用迭代器函数)。
std::vector<int> value1 {1, 2, 3, 4, 5};
std::vector<int> value2(std::begin(value1), std::begin(value1 + 3);//将保存1,2,3
(二)迭代器函数
- begin():返回指向容器中第一个元素的随机访问迭代器。
- end():返回指向容器最后一个元素后一个位置的随机访问迭代器。
- rbegin():返回指向最后一个元素的随机访问迭代器
- rend():返回指向第一个元素前一个位置的随机访问迭代器
- cbegin():和 begin() 功能相同,只不过在其基础上增加了 const 属性,不能用于修改元素。
- cend()、crbegin()、crend():依此类推
前缀带c的返回的迭代器是const类型,因此只能通过它读取而不能修改元素。另外,空的vector不能使用迭代器。如果vector申请了更多内存,比如使用reserve()函数,可能会导致之前的迭代器失效,因为其地址可能改变了。每当 vector 容器的容量发生变化时,我们都要对之前创建的迭代器重新初始化一遍。
(三) 容量函数
- size():返回容器中当前元素的数量,其值始终等于初始化 array 类的第二个模板参数 N。
- max_size():返回元素个数的最大值。这通常是一个很大的值,一般是 2^32-1,所以我们很少会用到这个函数。
- resize():改变实际元素的个数。
- capacity():返回当前容量。
- empty():判断容器是否为空。
- reserve():增加容器的容量。
- shrink_to_fit():将内存减少到等于当前元素实际所使用的大小。
(四)元素访问函数
- at(n):使用经过边界检查的索引访问元素,获取指定位置的元素引用。当传给 at() 的索引会造成越界时,会抛出std::out_of_range异常。
- front():返回容器中第一个元素的直接引用,可以用于修改元素,但是也只能修改第一个元素(因为它是引用。。。)。
- back():返回容器中最后一个元素的直接引用,可以用于修改元素。
- data():返回一个指向容器首个元素的指针,可以用于修改元素。
(五)修改器函数
- assign():用新元素替换原有内容。
- push_back():在序列的尾部添加一个元素。
- pop_back():移出序列尾部的元素。该容器的大小(size)会减 1,但容量(capacity)不会发生改变。
- insert():在指定的位置插入一个或多个元素,具体用法如下:
- iterator insert(pos, elem):在迭代器 pos 指定的位置之前插入一个新元素elem,并返回表示新插入元素位置的迭代器。
- iterator insert(pos, n, elem): 在迭代器 pos 指定的位置之前插入 n 个元素 elem,并返回表示第一个新插入元素位置的迭代器。
- iterator insert(pos, first, last):在迭代器 pos 指定的位置之前,插入其他容器(不仅限于vector)中位于 [first,last) 区域的所有元素,并返回表示第一个新插入元素位置的迭代器。
- iterator insert(pos, initlist):在迭代器 pos 指定的位置之前,插入初始化列表(用大括号{}括起来的多个元素,中间有逗号隔开)中所有的元素,并返回表示第一个新插入元素位置的迭代器。
//insert()源码
//从position开始,插入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){
//剩余空间大于n时
T x_copy = x;
//计算插入后现有元素的个数
const szie_type elems_after = finish - position;
iterator old_finish = finish;
if(elems_after > n){
//插入点之后的现有元素个数大于新增元素个数
uninitialized_copy(finish - n, finish, finish);//先后移原vector中插入点之后的元素
finish += n; //将vector尾端标记后移
copy_backward(position, old_finish - n, old_finish);
fill(position, position + n, x_copy);//从插入点开始填入新值
}
else{
//插入点之后的现有元素个数小于等于新增元素个数
uninitialized_fill_n(finish, n - elems_after, x_copy);//先在原vector的末尾填几个新值
finish += n - elems_after;
uninitialized_copy(position, old_finish, finish);//然后将插入点后的元素拷贝到上面插入新值之后的位置
finish += elems_after;
fill(position, old_finish, x_copy);//最后将前面空出来的n - elems_after个空位用新值填满
}
}
else{
//剩余空间小于n,必须配置额外的内存
//首先决定长度,旧长度的两倍,或者旧长度+新增元素的个数(取最大值)
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);
//将新增元素复制到新空间
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
//清除并释放旧的vector
destroy(start, finish);
deallocate();
//调整水位标记
start = new_start;
finish = new_finish;
end_of_storage = new_start + len;
}
}
}
//使用示例
std::vector<int> demo{1,2};
//第一种格式用法
demo.insert(demo.begin() + 1, 3);//{1,3,2}
//第二种格式用法
demo.insert(demo.end(), 2, 5);//{1,3,2,5,5}
//第三种格式用法
std::array<int,3> test{7,8,9};
demo.insert(demo.end(), test.begin(), test.end());//{1,3,2,5,5,7,8,9}
//第四种格式用法
demo.insert(demo.end(), {10,11});//{1,3,2,5,5,7,8,9,10,11}
for (int i = 0; i < demo.size(); i++) {
cout << demo[i] << " ";
}
- erase():删除 vector 容器中 pos 迭代器指定位置处的元素,并返回指向被删除元素下一个位置元素的迭代器。该容器的大小(size)会减 1,但容量(capacity)不会发生改变。
//erase()源码
//1.清除[first, last)间的所有元素
iterator erase(iterator first, iterator last){
iterator it = copy(last, finish, first);//copy是全局函数
destroy(it, finish);//destroy是全局函数
finish = finish - (last - first);
return first;
}
//2.清除position位置的元素
iterator erase(iterator position){
if(position + 1 != end())
copy(position + 1, finish, position);//copy是全局函数
--finish;
destroy(finish);//destroy是全局函数
return position;
}
//使用示例
vector<int>demo1{ 1,2,3,4,5 };
auto iter = demo1.erase(demo1.begin() + 1);//删除元素 2
cout << endl << *iter << endl;//iter迭代器指向元素 3
vector<int>demo2{ 1,2,3,4,5 };
iter = demo2.erase(demo2.begin()+1, demo2.begin()+3);//删除元素2和3
- swap():交换两个容器的所有元素。
- clear():移除所有的元素,容器大小变为 0。
//clear()源码
void clear(){
erase(begin(), end());
}
- emplace():在指定位置之前生成一个新的元素。(与insert()区分开来)
- emplace_back():在序列尾部生成一个元素。
emplace_back()和push_back()的区别,在于底层实现的机制不同。push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。
push_back() 的底层实现过程比 emplace_back() 更繁琐,换句话说,emplace_back() 的执行效率比 push_back() 高。因此,在实际使用时,建议大家优先选用 emplace_back()。
(六)重载运算符
- vector可以通过运算符[]以下标的形式访问元素。
(七)示例
#include <iostream>
#include <vector>
using namespace std;
int main(){
//初始化一个空的vector
vector<char> value;
//在尾部添加若干元素
value.push_back('S');
value.push_back('T');
value.push_back('L');
//打印出vector的尺寸
printf("元素个数为:%d\n", value.size());
//遍历vector(使用迭代器)
for(auto it = value.begin(); it < value.end(); ++it){
cout << *it << " ";
}
cout << endl;
//在首部插入一个元素
value.insert(value.begin(), 'C');
cout << value.at(0) << endl;
vector<int> values{1,2,3,4,5};
//输出容器中第 3 个元素的值
cout << *(values.data() + 2) << endl;
//修改容器中第 2 个元素的值
*(values.data() + 1) = 10;
cout << *(values.data() + 1) << endl;
return 0;
}