目录
1.vector
2.vector的使用
2.1vector的定义
2.2vector迭代器的使用
2.2.1begin和end
2.2.2rbegin和rend
2.3增删查改
2.3.1pop_back和push_back
2.3.2inset和erase
2.3.3find函数
2.3.4swap函数
2.3.5元素访问
2.4空间函数
2.4.1size和capacity
2.4.2reserve和resize
2.4.3empty
3.迭代器失效的问题
3.1 会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、push_back等。
3.2删除操作--erase
1.vector
简介:
1.向量(vector)是表示可以改变大小的数组的序列容器。
2.就像数组一样,向量(vector)为其元素使用连续的存储位置,这意味着也可以使用指向其元素的常规指针上的偏移量来访问它们的元素,并且与数组一样高效。
3.但与数组不同,它们的大小可以动态变化,其存储由容器自动处理。
4.当vector需要重新分配大小时,其做法是,分配一个新的数组,然后将全部元素移到这个数组当中,并释放原来的数组空间。
5.vector会分配一些额外的空间以适应可能的增长,因此存储空间比实际需要的存储空间一般更大。不同的库采用不同的策略权衡空间的使用和重新分配,以至于在末尾插入一个元素的时候是在常数的时间复杂度完成的。
2.vector的使用
2.1vector的定义
方式一:(无参构造)
vector<int> v1;
方式二:(构造并初始化n个val )
vector<int> v2(10,1);
方式三:(拷贝构造)
vector<int> v3(v2);
方式四:(迭代器构造)
vector<int> v4(v2.begin(),v2.end());
2.2vector迭代器的使用
2.2.1begin和end
begin可以获得容器中第一个元素的正向迭代器,end可以获得最后一个元素的下一个原素的迭代器。
遍历:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v(5, 1);
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
it++;
}
cout << endl;
return 0;
}
2.2.2rbegin和rend
rbegin可以获得容器中最后一个元素的迭代器,rend可以获得容器中第一个位置前一个位置的迭代器。
遍历:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v(5, 1);
vector<int>::reverse_iterator rit = v.rbegin();
while (rit != v.rend())
{
cout << *rit << " ";
rit++;
}
cout << endl;
return 0;
}
2.3增删查改
2.3.1pop_back和push_back
分别是尾删和尾插
#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.pop_back();
v.pop_back();
v.pop_back();
v.pop_back();
return 0;
}
2.3.2inset和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;
}
以上是按位置进行插入或删除元素的方式,若要按值进行插入或删除(在某一特定值位置进行插入或删除),则需要用到find函数。
2.3.3find函数
find函数共三个参数,前两个参数确定一个迭代器区间(左闭右开),第三个参数确定所要寻找的值。
find函数在所给迭代器区间寻找第一个匹配的元素,并返回它的迭代器,若未找到,则返回所给的第二个参数。
#define _CRT_SECURE_NO_WARNINGS 1
#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);
v.insert(pos, 10);//1 10 2 3 4
pos = find(v.begin(), v.end(), 3);
v.erase(pos);1 10 2 4
return 0;
}
注:find的函数是在algorithm中实现的
2.3.4swap函数
交换两个容器的数据空间
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v1(5, 1);
vector<int> v2(5, 2);
v1.swap(v2); //交换v1,v2的数据空间
return 0;
}
2.3.5元素访问
和数组一样,可以通过“下标+[ ]”的方式对容器当中的元素进行访问。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v(10, 1);
//使用“下标+[]”的方式遍历容器
for (size_t i = 0; i < v.size(); i++)
{
cout << v[i] << " ";
}
cout << endl;
return 0;
}
支持迭代器就支持范围for,因为在编译时编译器会自动将范围for替换为迭代器的形式。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v(10, 1);
//范围for
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
return 0;
}
2.4空间函数
2.4.1size和capacity
size函数获取当前容器中的有效元素个数,capacity函数获取当前容器的最大容量。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v(10, 2);
cout << v.size() << endl;
cout << v.capacity() << endl;
return 0;
}
2.4.2reserve和resize
通过reserse函数改变容器的最大容量,resize函数改变容器中的有效元素个数。
reserve规则:
1、当所给值大于容器当前的capacity时,将capacity扩大到该值。
2、当所给值小于容器当前的capacity时,什么也不做。resize规则:
1、当所给值大于容器当前的size时,将size扩大到该值,扩大的元素为第二个所给值,若未给出,则默认为0。
2、当所给值小于容器当前的size时,将size缩小到该值。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v(10, 2);
cout << v.size() << endl; //10
cout << v.capacity() << endl; //10
v.reserve(20);
cout << v.size() << endl; //10
cout << v.capacity() << endl; //20
v.resize(15);
cout << v.size() << endl; //15
cout << v.capacity() << endl; //20
return 0;
}
2.4.3empty
判空
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v(10, 1);
cout << v.empty() << endl;
return 0;
}
3.迭代器失效的问题
迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对 指针进行了封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器 底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器,程序可能会崩溃)。
对于vector可能会导致其迭代器失效的操作有:
3.1 会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、push_back等。
#include <iostream>
using namespace std;
#include <vector>
int main()
{
vector<int> v{1,2,3,4,5,6};
auto it = v.begin();
// 将有效元素个数增加到100个,多出的位置使用8填充,操作期间底层会扩容
// v.resize(100, 8);
// reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容
量改变
// v.reserve(100);
// 插入元素期间,可能会引起扩容,而导致原空间被释放
// v.insert(v.begin(), 0);
// v.push_back(8);
// 给vector重新赋值,可能会引起底层容量改变
v.assign(100, 8);
while(it != v.end())
{
cout<< *it << " " ;
++it;
}
cout<<endl;
return 0;
}
出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释放掉,而在打印时,it还使用的是释放之前的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的空间,而引起代码运行时崩溃。
解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给it重新赋值即可。
总结:一般进行了底层的操作后要恢复迭代器
3.2删除操作--erase
#include <iostream>
using namespace std;
#include <vector>
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位置的数据,导致pos迭代器失效。
v.erase(pos);
cout << *pos << endl; // 此处会导致非法访问
return 0;
}
解释:erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理 论上讲迭代器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素 时,vs就认为该位置迭代器失效了。
关于删除vector中所有的偶数的两个例子:
例一:
#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>
#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) //删除容器当中的全部偶数
{
it = v.erase(it); //删除后获取下一个元素的迭代器
}
else
{
it++; //是奇数则it++
}
}
return 0;
}
这两个代码那个是对的呢?
例二才是正解。
例一的代码看上去实际上并没有什么错误,但如果你画图仔细分析,你就会发现该代码的问题所在,迭代器访问到了不属于容器的内存空间,导致程序崩溃。
不仅如此,而且在迭代器遍历容器中的元素进行判断时,并没有对1、3、5元素进行判断。