文章目录
一. vector的介绍
- vector是表示可变大小数组的序列容器。
- 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
- 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
- vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
- 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
- 与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好。
二. vector的使用
vector的定义方式
方式一:构造某种类型的空容器
vector<int> v; //构造一个int类型的空容器
方式二:构造一个含有n个val的某类型容器
vector<int> v(n, 1); //构造含有10个1的int类型容器
方式三:拷贝构造生成某类型容器的复制品
vector<int> v2(v1); //拷贝构造
方式四:使用迭代区间构造初始化
template
vector (InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
vector<int> v2(v1.begin(),v1.end()); //使用迭代器构造v2的一段内容
string s("hello world");
vector<char> v3(s.begin(),s.end()); //拷贝构造string对象的某一段内容
vector的空间增长问题
size和capacity
通过size函数获取当前容器中的有效元素个数,通过capacity函数获取当前容器能够存储有效元素个数的最大容量。
#include<iostream>
using namespace std;
#include<vector>
int main()
{
vector<int> v(10, 3);
cout << v.size() << endl; //获取当前容器的有效元素个数
cout << v.capacity() << endl; //获取当前容器的最大容量
return 0;
}
reserve和resize
通过reserse函数改变容器的最大容量,resize函数改变容器中的有效元素个数。
reserve规则
- 当所给值大于容器当前的capacity时,将capacity扩大到该值。
- 当所给值小于容器当前的capacity时,什么也不做。
resize规则
3. 当所给值大于容器当前的size时,将size扩大到该值,扩大的元素为第二个所给值,若未给出,默认给’\0’
4. 当所给值小于容器当前的size时,将size缩小到该值。
#include<iostream>
using namespace std;
#include<vector>
int main()
{
vector<int> v(10, 3);
cout << v.size() << endl; //获取当前容器的有效元素个数
cout << v.capacity() << endl; //获取当前容器的最大容量
v.reserve(15); //size不变,影响容量,capacity增容到15
cout << v.size() << endl; //10
cout << v.capacity() << endl; //15
v.resize(12); //capacity不变 size变为12
cout << v.size() << endl; //12
cout << v.capacity() << endl; //15
return 0;
}
empty
通过empty函数判断当前容器是否为空。
#include<iostream>
using namespace std;
#include<vector>
int main()
{
vector<int> v(10, 2);
cout << v.empty() << endl; //0 不为空
return 0;
}
vector的迭代器使用
begin和end
通过begin函数可以得到容器中第一个元素的正向迭代器,通过end函数可以得到容器中最后一个元素的后一个位置的正向迭代器。
正向迭代器遍历容器
#include<iostream>
using namespace std;
#include<vector>
int main()
{
vector<int> v(10, 2);
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it<<" ";
it++;
}
cout << endl;
return 0;
}
rbegin和rend
通过rbegin函数可以得到容器中最后一个元素的反向迭代器,通过rend函数可以得到容器中第一个元素的前一个位置的反向迭代器。
反向迭代器遍历容器
#include<iostream>
using namespace std;
#include<vector>
int main()
{
vector<int> v(10, 2);
v.push_back(1);
vector<int>::reverse_iterator rit = v.rbegin();
while (rit != v.rend())
{
cout << *rit << " ";
rit++;
}
cout << endl;
return 0;
}
vector的增删查改
push_back和pop_back
通过push_back函数对容器进行尾插,pop_back函数对容器进行尾删。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
v.push_back(1); //尾插元素1
v.push_back(2); //尾插元素2
v.push_back(3); //尾插元素3
v.push_back(4); //尾插元素4
v.pop_back(); //尾删元素
v.pop_back(); //尾删元素
v.pop_back(); //尾删元素
v.pop_back(); //尾删元素
return 0;
}
insert和erase
通过insert函数可以在所给迭代器位置插入一个或多个元素,通过erase函数可以删除所给迭代器位置的元素,或删除所给迭代器区间内的所有元素(左闭右开)。
#include<iostream>
using namespace std;
#include<vector>
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.insert(v.begin(), 5); //头插一个5
v.insert(v.begin(), 5, -1); //头插5个-1
v.erase(v.end() - 1); //删除最后一个元素
v.erase(v.begin(), v.begin() + 3); //删除一段迭代区间
for (auto x : v)
{
cout << x << " ";
}
cout << endl; //-1 -1 5 1 2 3
return 0;
}
以上是按位置进行插入或删除元素的方式,若要按值进行插入或删除(在某一特定值位置进行插入或删除),则需要用到find函数。
find函数
- find函数共三个参数,前两个参数确定一个迭代器区间(左闭右开),第三个参数确定所要寻找的值。
- find函数在所给迭代器区间寻找第一个匹配的元素,并返回它的迭代器,若未找到,则返回所给的第二个参数。
#include<iostream>
using namespace std;
#include<vector>
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
vector<int>::iterator pos1 = find(v.begin(), v.end(), 2);
//在2位置插入10
v.insert(pos1, 10);
//删除3,先获取3这个位置上的迭代器,在erase
vector<int>::iterator pos2 = find(v.begin(), v.end(), 3);
v.erase(pos2);
for (auto& x : v)
{
cout << x << " ";
}
cout << endl;
return 0;
}
注意: find函数是在算法模块这个组件(algorithm)当中实现的,不是vector的成员函数。
swap
通过swap函数可以交换两个容器的数据空间,实现两个容器的交换。
#include<iostream>
using namespace std;
#include<vector>
int main()
{
vector<int> v1(10, 1);
vector<int> v2(10, 2);
v1.swap(v2); //交换v1和v2两个容器的数据
for (auto& x : v1)
{
cout << x << " ";
}
cout << endl;
for (auto& x : v2)
{
cout << x << " ";
}
cout << endl;
return 0;
}
元素访问
方式一: vector当中实现了 [ ] 操作符的重载,因此我们也可以通过“下标+[ ]”的方式对容器当中的元素进行访问。
#include<iostream>
using namespace std;
#include<vector>
int main()
{
vector<int> v(10, 1);
for (int i = 0; i < v.size(); i++)
{
cout << v[i] << " ";
}
cout << endl;
return 0;
}
**方式二:**迭代器的方式遍历
#include<iostream>
using namespace std;
#include<vector>
int main()
{
vector<int> v(10, 2);
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
it++;
}
cout << endl;
return 0;
}
方式三: 我们上面说到vector是支持迭代器的,所以我们还可以用范围for对vector容器进行遍历。(支持迭代器就支持范围for,因为在编译时编译器会自动将范围for傻瓜式的替换成迭代器的形式)
#include<iostream>
using namespace std;
#include<vector>
int main()
{
vector<int> v(10, 2);
for (auto& x : v)
{
cout << x << " ";
}
cout << endl;
return 0;
}
vector迭代器失效问题
迭代器的主要作用就是让我们在使用各个容器时不用关心其底层的数据结构,而vector的迭代器在底层实际上就是一个指针。迭代器失效就是指迭代器底层对应指针所指向的空间被销毁了,而指向的是一块已经被释放的空间,如果继续使用已经失效的迭代器,程序可能会崩溃。
迭代器失效问题举例
案例一:
#include<iostream>
using namespace std;
#include<algorithm>
#include<vector>
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
vector<int>::iterator pos = find(v.begin(), v.end(), 2);
//查找到2位置
v.insert(pos, 10);
//1 10 2 3 4
v.erase(pos); //删除2,结果出差错了
//1 2 3 4
return 0;
}
在该代码中,我们本意是使用元素2的迭代器在原序列中2的位置插入一个10,然后将2删除,但我们实际上获取的是指向2的指针,当我们在2的位置插入10后,该指针就指向了10,所以我们之后删除的实际上是10,而不是2。
案例二:
#include <iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> v;
for (size_t i = 1; i <= 6; i++)
{
v.push_back(i);
}
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0) //删除容器当中的全部偶数
{
v.erase(it); //结果运行崩溃了,原因非法访问了地址
}
it++;
}
return 0;
}
该代码看上去实际上并没有什么错误,但如果你画图仔细分析,你就会发现该代码的问题所在,迭代器访问到了不属于容器的内存空间,导致程序崩溃。
迭代器失效解决方法
使用迭代器时,永远记住一句话:每次使用前,对迭代器进行重新赋值。
解决案例一:
#include<iostream>
using namespace std;
#include<algorithm>
#include<vector>
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
vector<int>::iterator pos = find(v.begin(), v.end(), 2);
//查找到2位置
v.insert(pos, 10);
//1 10 2 3 4
//重新获取2位置的迭代器
pos = find(v.begin(), v.end(), 2);
v.erase(pos);
//1 10 3 4 结果正确
for (auto& x : v)
{
cout << x << " ";
}
cout << endl;
return 0;
}
解决办法就是重新获取2位置的迭代器
解决案例二:
#include <iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> v;
for (size_t i = 1; i <= 6; i++)
{
v.push_back(i);
}
//1 2 3 4 5 6
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0) //删除容器当中的全部偶数
{
it = v.erase(it);
//由于erase函数返回当前位置迭代器
//重新获取该位置迭代器,其实就是指向了下一个元素,无需++
}
else
{
it++; //奇数跳过,偶数删除
}
}
for (auto& x : v)
{
cout << x << " ";
}
cout << endl; //1 3 5 结果正确
return 0;
}
对于实例二,我们可以接收erase函数的返回值(erase函数返回删除元素的后一个元素的新位置),并且控制代码的逻辑:当元素被删除后继续判断该位置的元素(因为该位置的元素已经更新,需要再次判断)。