目录
秃头侠们好呀,今天来说 vector
vector简介
- vector表示可变大小数组序列的容器。
- 像数组一样,vector也采用的连续存储空间来存储元素。意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
- vector使用动态分配数组来存储它的元素。当新元素插入时候,如果空间不够,将会开辟一个新数组,新数组的空间大小一般是原来的2倍左右,然后把元素都移到新数组再把你插入的数组插进来。
- vector支持随机访问,所以访问元素比较高效,尾插尾删也很高效,但是如果在头插头删或者其他位置的插入删除,效率就很低,因为要移动大量数据。当然如果经常需要插入删除我们会使用像list这样的容器。
vector的使用
vector的定义
(constructor)构造函数声明 | 接口说明 |
---|---|
vector()(重点) | 无参构造 |
vector(size_type n, const value_type& val = value_type()) | 构造并初始化n个val |
vector (const vector& x); (重点) | 拷贝构造 |
vector (InputIterator first, InputIterator last); | 使用迭代器进行初始化构造 |
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> v1;//无参构造
vector<int> v2(3, 10);//用3个10构造
vector<int> v3(v2.begin(), v2.end());//用一段迭代器区间初始化
vector<int> v4(v3);//拷贝构造
int arr[] = { 1,2,3,4,5,6,7,8,9 };
vector<int> v5(arr, arr+sizeof(arr) / sizeof(arr[0]));//也相当于一段迭代器区间
vector<int>::iterator it = v5.begin();
for (; it < v5.end(); it++)
{
cout << *it << " ";
}
cout << endl;
for (auto e : v5)
{
cout << e << " ";
}
cout << endl;
for (int i = 0; i < v5.size(); i++)
{
v5[i] += 1;
cout << v5[i] << " ";
}
cout << endl;
return 0;
}
vector iterator 的使用
正向迭代器:
begin()
获取第一个数据位置
end()
获取最后一个数据的下一个位置
反向迭代器:
rbegin()
获取最后一个数据位置
rend()
获取第一个数据的前一个位置
迭代器都是左闭右开!
int main()
{
// 使用push_back插入4个数据
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
// 使用迭代器进行遍历打印
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
// 使用迭代器进行修改
it = v.begin();
while (it != v.end())
{
*it *= 1;
++it;
}
// 使用反向迭代器进行遍历再打印
vector<int>::reverse_iterator rit = v.rbegin();
while (rit != v.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
return 0;
}
void PrintVector(const vector<int>& v)
{
// const对象使用const迭代器进行遍历打印
//只可打印不能修改
vector<int>::const_iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
vector 空间扩容问题
size | 获取数据个数 |
capacity | 获取容量大小 |
empty | 判断是否为空 |
resize(重点) | 改变vector的size |
reserve (重点) | 改变vector的capacity |
注意:
- capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。不要以为都是2倍增长的(vs是PJ版本STL,g++是SGI版本STL)
- reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问
题。 - resize在开空间的同时还会进行初始化,如果开辟的比原来小则会删除后面的数据,影响size。
- reserve只会增大capacity如果再减少,capacity不会改变,这样是防止一会儿你又要扩容。
vector 增删查改
push_back(重点) | 尾插 |
pop_back (重点) | 尾删 |
find | 查找(注意这个是算法模块实现,不是vector的成员接口) |
insert | 在pos之前插入val |
erase | 删除pos位置的数据 |
swap | 交换两个vector的数据空间 |
operator[] (重点) | 像数组一样访问 |
at()函数和operator[]一样,都可以得到相应的下标的值,区别是at()会对边界做检查(如果有错会抛异常),而[]不会。
int main()
{
int a[] = { 1, 2, 3, 4 };
vector<int> v(a, a + sizeof(a) / sizeof(int));
// 使用find查找3所在位置的iterator
vector<int>::iterator pos = find(v.begin(), v.end(), 3);
// 在pos位置之前插入30
v.insert(pos, 30);
vector<int>::iterator it = v.begin();
while (it != v.end()) {
cout << *it << " ";
++it;
}
cout << endl;
pos = find(v.begin(), v.end(), 3);
// 删除pos位置的数据
v.erase(pos);
it = v.begin();
while (it != v.end()) {
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
vector 迭代器失效问题
迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T*。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,变成野指针了,如果继续使用已经失效的迭代器,程序可能会崩溃。
有可能引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、push_back等。
那么像insert的操作为什么有可能会导致迭代器失效?
因为如果插入需要扩容,那么会新开一片空间,之前指向原数组pos位置的空间被释放,此时我们再对此位置解引用不就是对野指针解引用非法访问了嘛!
(如果空间足够,没有扩容那没事)
那么像erase的操作为什么有可能会导致迭代器失效?
其中一个原因是因为可能有的编译器会缩容,再开一片空间,那么就和上面的情况一样了(但一般不会缩容)。
另一种就是如果pos是最后一个元素,删除后,pos就是end的位置了,而end位置是没有元素的,那pos不就失效了。
还有一种情况就是,erase删除元素后,后面的元素会前移,pos的意义就不再是指向你删除的元素了,现在是指向下一个元素了。
所以迭代器失效解决办法:⭐在使用前,对迭代器重新赋值
vector模拟实现
使用memcpy拷贝问题
int main()
{
vector<string> v;
v.push_back("1111");
v.push_back("2222");
v.push_back("3333");
return 0;
}
这里需要扩容,出现问题了!
当模拟实现vector中的reserve接口中,使用memcpy进行拷贝会有问题。
1、memcpy是浅拷贝(内存的二进制格式),将一段内存空间中内容原封不动的拷贝到另外一段内存空间中。
2、 如果拷贝的是内置类型的元素,memcpy即高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。
结论: 如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。
动态二维数组
跟我们在C语言学的二维数组一样,vector也可以表示二维数组
vector<vector<int>>
vector<vector<double>>
vector<vector<string>>
⭐感谢阅读,我们下期再见
如有错 欢迎提出一起交流