💓博主CSDN主页::Am心若依旧💓
⏩专栏分类c++从入门到精通⏪
🚚代码仓库:青酒余成🚚🌹关注我🫵带你学习更多c++
🔝🔝
vector的介绍
1.vector表示的是可变序列大小的容器
2、vector就像数组一样,也采用的连续空间来存储元素,这也意味着可以采用下标对vector的元素进行访问。
3、vector与普通数组不同的是,vector的大小是可以动态改变的。
4、当vector需要重新分配大小时,其做法是,分配一个新的数组,然后将全部元素移到这个数组当中,并释放原来的数组空间。---这就表明原来指向该位置的迭代器已经失效了,所以在插入和删除数据的时候要返回一个合法的迭代器的位置。
5、vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因此存储空间比实际需要的存储空间一般更大。不同的库采用不同的策略权衡空间的使用和重新分配,以至于在末尾插入一个元素的时候是在常数的时间复杂度完成的。
6、由于vector采用连续的空间来存储元素,与其他动态序列容器相比,vector在访问元素的时候更加高效,在其末尾添加和删除元素相对高效,而对于不在其末尾进行的删除和插入操作效率则相对较低。--这是因为不在其末尾进行删除和插入操作时,要整体移动数据,效率较低
vector的使用
vector的定义方式
方式一:构造一个空容器--刚开始v1数据内部实际有效的大小是0
vector<int> v1; //构造int类型的空容器
方式二:构造一段含有n个val的容器
vector<int> v2(5,2); //构造含有5个2的容器
方式三:用拷贝构造来复制一个一模一样的容器
vector<int> v3(v2);//v3把v2给复制了一份
方式四:用迭代器区间来进行构造
vector <int> v4(v2.begin(),v2.end());//用v2里面的值来构造v4
此外,不仅仅只是可以使用int容器,也可以使用其他数据类型
string s("hello world");
vector<char> v5(s.begin(), s.begin()+5); //拷贝构造string对象的某一段内容
vector的空间增长问题
在空间增长这一块:主要有resize,reserver,capacity, size,empty等几个重要的函数
size和capacity函数
通过size函数获取当前容器中的有效元素个数,通过capacity函数获取当前容器的最大容量。
vector <int> v(5, 2);
v.push_back(1);
cout << v.size() << endl;//6
cout << v.capacity() << endl;//7
empty函数
主要是判断当前对象中是否还有元素,有元素就返回假,没有元素就返回真
vector <int> v(5, 2);
v.push_back(1);
cout << v.size() << endl;//6
cout << v.capacity() << endl;//7
cout << v.empty() << endl;//0
返回0的含义是当前v对象里面是存在函数的,所以返回的是假,也就是0
resize和reserve函数
reserve函数的使用规则:
1、当所给值大于容器当前的capacity时,将capacity扩大到该值。---扩容的本质就是新开辟一块空间,然后把原空间的数据拷贝过来,然后再释放原空间---这个地方也是导致迭代器失效的本质
2、当所给值小于容器当前的capacity时,什么也不做。
resize函数的使用规则:
1、当所给值大于容器当前的size时,将size扩大到该值,扩大的元素为第二个所给值,若未给出,则默认为0。
2、当所给值小于容器当前的size时,将size缩小到该值
vector <int> v(5, 2);
v.reserve(4);//比当前的空间要小,所以不会发生扩容
cout << v.capacity() << endl;//输出依然是7
v.reserve(10);//比当前的capacity大,所以会发生扩容
cout << v.capacity() << endl;//发生了扩容,输出变为了10
v.resize(3);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;//打印出 2 2 2
v.resize(6);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;//打印出 2 2 2 0 0 0
v.resize(10,1);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;//打印出 2 2 2 0 0 0 1 1 1 1
vector的迭代器使用
begin和end
通过begin函数可以得到容器中第一个元素的正向迭代器,通过end函数可以得到容器中最后一个元素的后一个位置的正向迭代器。
正向迭代器遍历容器:
auto it = v.begin();
while (it != v.end())
{
cout << *it << ' ';
it++;
}
cout << endl;
rbegin和rend
rbegin就指向最后一个元素,rend就指向第一个元素的前一个位置
反向迭代器遍历容器:
auto it = v.begin();
while (it != v.end())
{
cout << *it << ' ';
it++;
}
cout << endl;//打印2 2 2 0 0 0 0 1 1 1 1
auto it1 = v.rbegin();
while (it1 != v.rend())
{
cout << *it1 << ' ';
it1++;
}
cout << endl;//打印 1 1 1 1 0 0 0 2 2 2
vector的增删查改
insert和erase
通过insert函数可以在所给迭代器位置插入一个或多个元素,通过erase函数可以删除所给迭代器位置的元素,或删除所给迭代器区间内的所有元素(左闭右开)。
#include <iostream>
#include <vector>
using namespace std;
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(), 0); //在容器开头插入0
v.insert(v.begin(), 5, -1); //在容器开头插入5个-1
v.erase(v.begin()); //删除容器中的第一个元素
v.erase(v.begin(), v.begin() + 5); //删除在该迭代器区间内的元素(左闭右开)
return 0;
}
pop_back和push_back函数
这就是一个普通的尾插和尾删的函数,就不做过多的解释了。不懂的可以查看下面链接:
vector::push_back - C++ Reference
vector::pop_back - C++ Reference
由于上面的插入和删除都是在某个迭代器的位置进行插入和删除,所以你的先有一个迭代器的位置,而我们目前所拥有的迭代器位置就只有两个 begin()和end(),其中end还是无效的。
所以为了满足在某一特定的值前面进行插入,c++还给我们内置了一个find函数
find函数:
find函数共三个参数,前两个参数确定一个迭代器区间(左闭右开),第三个参数确定所要寻找的值。
find函数在所给迭代器区间寻找第一个匹配的元素,并返回它的迭代器,若未找到,则返回所给的第二个参数。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
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); //在2的位置插入10
pos = find(v.begin(), v.end(), 3); //获取值为3的元素的迭代器
v.erase(pos); //删除3
return 0;
}
ps:这个find函数实在algorithm里面的,并不是STL中vector内部的
阅读到这里大部分的vector中的函数接口都已经学完了,如果有兴趣学习更多的参考如下:
vector - C++ Reference
总结与拓展
vector迭代器失效问题
迭代器的主要作用就是让我们在使用各个容器时不用关心其底层的数据结构,而vector的迭代器在底层实际上就是一个指针。迭代器失效就是指迭代器底层对应指针所指向的空间被销毁了,而指向的是一块已经被释放的空间,如果继续使用已经失效的迭代器,程序可能会崩溃。
那么在什么情况下有可能发生迭代器失效呢? ---在使用insert和erase函数时,最容易发生迭代器失效的问题,因为在插入的过程中,你在某个迭代器位置进行插入的同时进行了扩容,由于扩容的机制,会导致原来的空间进行释放,所以说这就容易导致迭代器失效。
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
//v: 1 2 3 4 5
vector<int>::iterator pos = find(v.begin(), v.end(), 2); //获取值为2的元素的迭代器
v.insert(pos, 10); //在值为2的元素的位置插入10
//v: 1 10 2 3 4 5
v.erase(pos); //删除元素2 ???error(迭代器失效)
//v: 1 2 3 4 5
return 0;
}
这里就是在扩容的时候,导致的迭代器失效的问题。
解决办法就是:使用迭代器时,永远记住一句话:每次使用前,对迭代器进行重新赋值。就能够完美的解决这个问题了
还有一个就是在删除时,当你删除了该位置的元素,你在对该位置去进行解引用,那么这样就导致你访问了不属于你的资源,那么就会发生程序报错。
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
std::vector<int>::iterator it = vec.begin() + 1; // 指向第二个元素
vec.erase(it); // 删除第二个元素
std::cout << *it; // 试图访问已删除元素的位置,将导致未定义行为
return 0;
}
解决办法:每次删除一个元素之后,都给他返回一个合法的迭代器位置,即返回删除位置的下一个位置的迭代器。
下期预告:STL之vector的模拟实现