【C++】---STL之vector详解
- 一、vector的介绍:
- 二、vector的成员函数:
- 1、vector类的构造函数
- 2、vector的元素访问符
- 3、vector的迭代器
- 4、vector的模版
- 5、vector的拷贝构造
- 6、vector的容量
- (1)vector的增容机制
- (2)reserve()和resize()
- (3)size()
- (4)empty()
- 7、vector的尾插和尾删
- 8、vector在任意位置插入与删除
- (1)插入(insert)
- (2)删除
- 9、find()
- 10、swap()
- 三、迭代器失效
- (1)什么是迭代器失效?
- (2)为什么会出现:迭代器失效问题?
- (3)如何解决迭代器失效?!
- (4)迭代器失效的样例
一、vector的介绍:
二、vector的成员函数:
vector定义:表示可以动态改变大小的数组序列容器!( vector实际上就是一个顺序表)
vector的特点:
(1)vector像数组一样拥有连续的储存空间来储存元素,因此可以通过下标访问来访问储存的元素。
然而还有一点,它不像数组,那就是它可以动态改变其自身的大小,而数组是静态的,它改变其自身大
小是容器对容量自动处理的。
(2)vector的空间比实际上所需要储存的空间更大一点, vector的尾插尾删的效率更高一点,而在其
他位置的插入删除效率相对较低一点,因为每次的插入删除都会挪动后面的数据。
1、vector类的构造函数
1、参考文档:
2、
(1)
//构造空的vector
explicit vector(const allocator_type& alloc = allocator_type());
//构造一个vector,有n个元素,每个元素值为val
explicit vector(size_type n, const value_type& val = value_type(),
const allocator_type& alloc = allocator_type());
//构造一个vector,值为InputIterator的first到last之间的元素
template <class InputIterator>
vector(InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
//使用x拷贝构造一个vector
vector(const vector& x);
(2)
vector容器的定义元素、构造函数、拷贝构造函数、尾插:
vector<int> v;// 构造一个空的vector容器
vector<int> v1(3, 5);// 构造一个元素个数为3,每个元素的初始化为5的容器
vector<int> v2(v1.begin(),v1.end());// 构造一个从v1.begin()到v1.end()的v2容器
vector<int> v3(v1);// 拷贝构造
v.push_back(1);// 对v1空容器进行尾插。
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
2、vector的元素访问符
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> v;// 构造一个空的vector容器
vector<int> v1(3, 5);// 构造一个元素个数为3,每个元素的初始化为5的容器
vector<int> v2(v1.begin(),v1.end());// 构造一个从v1.begin()到v1.end()的v2容器
vector<int> v3(v1);// 拷贝构造
v.push_back(1);// 对v1空容器进行尾插。
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
// 1、通过访问符[ ]来访问vector容器中的元素:
for (size_t i = 0; i <v.size(); i++)
{
cout << v[i] << " ";
}
cout << endl;
return 0;
}
3、vector的迭代器
1、迭代器是各种容器,无论是连续还是不连续的空间的容器,通用的遍历容器的方式。
(1)可读可写的迭代器:
int main()
{
vector<int> v;// 构造一个空的vector容器
vector<int> v1(3, 5);// 构造一个元素个数为3,每个元素的初始化为5的容器
vector<int> v2(v1.begin(), v1.end());// 构造一个从v1.begin()到v1.end()的v2容器
vector<int> v3(v1);// 拷贝构造
v.push_back(1);// 对v1空容器进行尾插。
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
// 2、迭代器遍历vector
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
it++;
}
cout << endl;
return 0;
}
(2)只读的迭代器:
库里面存在:const迭代器,直接用
// 2、迭代器遍历vector
vector<int>::const_iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
it++;
}
cout << endl;
(3)反向迭代器:
可读可写:
// 2、反向迭代器遍历vector
vector<int>::reverse_iterator it = v.rbegin();
while (it != v.rend())
{
cout << *it << " ";
it++;
}
cout << endl;
(4)反向迭代器:只读:
// 2、反向迭代器遍历vector
vector<int>::const_reverse_iterator it = v.rbegin();
while (it != v.rend())
{
cout << *it << " ";
it++;
}
cout << endl;
4、vector的模版
template<class T>
void PrintVector(const vector<T>& v)
{
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
int main()
{
// 1、定义一个v1的顺序表,里面的数据都是int类型
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
// 调用打印函数:printvector
PrintVector(v1);
// 2、定义一个v2的顺序表,里面的数据都是char类型,并且把s1里面的字符串内容,初始化时赋给v2
string s1 = "hello world";
vector<char> v2(s1.begin(),s1.end());
// 调用打印函数:printvector
PrintVector(v2);
// 3、定义一个v3的顺序表,里面的数据都是double类型
vector<double> v3;
v3.push_back(1.1);
v3.push_back(2.2);
v3.push_back(3.3);
v3.push_back(4.4);
v3.push_back(5.5);
// 调用打印函数:printvector
PrintVector(v3);
return 0;
}
5、vector的拷贝构造
1、拷贝构造:
2、样例展示:
string s2("hello vector");
vector<string> v4;
v4.push_back(string(s2));
PrintVector(v4);
// 此时v4的内容就是:hello vector
// 拷贝构造:
vector<string> v5(v4);// 把v4拷贝构造给v5
PrintVector(v5);
6、vector的容量
(1)vector的增容机制
在VS下执行这段代码:
void test_vector3()
{
size_t sz;
std::vector<int> foo;
sz = foo.capacity();
std::cout << "making foo grow:\n";
for (int i = 0; i < 100; i++)
{
foo.push_back(i);
if (sz != foo.capacity())
{
sz = foo.capacity();
std::cout << "capacity changed:" << sz << endl;
}
}
}
发现增容的过程是大概按照1.5倍增容的:
同样的代码在linux下运行,发现是2倍增容的!
(2)reserve()和resize()
1、reserve():
void reserve (size_type n);//开辟n个元素空间
void resize (size_type n, value_type val = value_type());
//开辟n个元素空间,并将每个元素默认初始化为val
如果加上reserve(),那么会提前知道要开多少空间,就提前开好了,避免后面再开空间
void test_vector3()
{
size_t sz;
std::vector<int> foo;
sz = foo.capacity();
foo.reserve(100);
std::cout << "making foo grow:\n";
for (int i = 0; i < 100; i++)
{
foo.push_back(i);
if (sz != foo.capacity())
{
sz = foo.capacity();
std::cout << "capacity changed:" << sz << endl;
}
}
}
2、resize():
(1)样例演示:
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
PrintVector(v1);// resize()前:
v1.resize(3);// 将v1顺序表的元素个数缩小为3个
PrintVector(v1);// resize()后:
v1.resize(10,0);// 将v1顺序表的元素个数扩大为10个,并且其余多出来的元素初始化为0。
PrintVector(v1);// resize()后:
(3)size()
1、计算顺序表中的·元素个数:
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
cout << v1.size() << endl;
(4)empty()
1、判断顺序表是否为空:
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
cout << v1.empty() << endl;
vector<int> v2;
cout << v2.empty() << endl;
7、vector的尾插和尾删
vector<int> v1;
v1.push_back(1);//尾插
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
v1.pop_back();
v1.pop_back();//尾删2次
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
8、vector在任意位置插入与删除
(1)插入(insert)
1、函数的用法理解:
vector<double> v5;
//插入4个float元素
v5.push_back(1.1);
v5.push_back(2.2);
v5.push_back(3.3);
v5.push_back(4.4);
v5.push_back(5.5);
v5.insert(v5.end(), 6.6);//在v5末尾插入6.6
PrintVector(v5);
v5.insert(v5.begin(), 2,0);//在v5开头插入2个0
PrintVector(v5);
vector<double> v6;
v6.push_back(7.7);
v6.push_back(8.8);
v5.insert(v5.begin(), v6.begin(),v6.end());//在v5开头插入v6
PrintVector(v5);
(2)删除
1、erase的使用:
2、特别要注意:erase可能会导致迭代器失效!!!
iterator erase (iterator position);//删除某一位置元素
iterator erase (iterator first, iterator last);//删除迭代器first和last之间的元素
v5.erase(v5.begin());//删除v5开头元素
PrintVector(v5);
v5.erase(v5.begin(), v5.begin()+2);//从v5开头删除2个元素
PrintVector(v5);
9、find()
1、在迭代器区间内查找元素,find函数实现在algorithm中,可以给所有容器使用,因此要使用find函数,就要include
template <class InputIterator, class T>
InputIterator find (InputIterator first, InputIterator last, const T& val);//在InputIterator迭代器first和last区间内查找val元素的位置
注意:迭代器区间是左闭右开,因此能取到第一个位置,但取不到最后一个位置
vector<double>::iterator pos = find(v5.begin(), v5.begin() + 3, 1.1);//在第一个元素和第四个元素(左闭右开,不包含第四个元素)之间查找值为1.1的元素位置
v5.erase(pos);//删除1.1位置的元素,即删除1.1
PrintVector(v5);
10、swap()
1、样例展示:
vector<int> v1;
v1.push_back(1);//尾插
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
vector<int> v2;
v2.push_back(3);
v2.push_back(3);
v2.push_back(3);
v2.push_back(3);
v2.push_back(3);
v1.swap(v2);
cout << "v1:";
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
cout << "v2:";
for (auto e : v2)
{
cout << e << " ";
}
cout << endl;
三、迭代器失效
(1)什么是迭代器失效?
1、首先明白什么是迭代器?
1.迭代器的主要作用:就是让算法能够不用关心底层的数据结构,它的底层实际上就是一个指针或者是对指针进行了封装
什么是迭代器失效?迭代器变成了“野指针”!
(2)为什么会出现:迭代器失效问题?
2、为什么会存在迭代器失效?
(1)迭代器失效的原因:是因为迭代器指针所指向的空间被销毁了,而使用一块已经被释放的空间。造成的后果就是程序崩溃。
(2)注意:会引起其底层空间操改变的操作都有可能使迭代器失效,比如resize reserve Insert erase push_back等等。
(3)如何解决迭代器失效?!
如何解决迭代器失效?!
在使用前,对迭代器进行重新赋值即可。
1、样例展示:
#include <iostream>
using namespace std;
#include <vector>
int main()
{
vector<int> v{ 1, 2, 3, 4 };
auto it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
v.erase(it);
++it;// It是一个迭代器,对一块已经释放了空间的迭代器指针进行操作必然会引起程序崩溃。要想程序不崩溃,必须要对迭代器进行重新赋值!
}
return 0;
}
int main()
{
vector<int> v{ 1, 2, 3, 4 };
auto it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
it = v.erase(it);// 在迭代器失效前,对迭代器进行重新赋值,即可保证迭代器不会失效。程序不会崩溃。
else
++it;
}
return 0;
}
要特别注意,所有容器如果对它底层的空间数据进行改变的话,就有可能引起迭代器失效,常见的有insert和erase。
(4)迭代器失效的样例
1、
#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);
/*
出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释放掉,
而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的
空间,而引起代码运行时崩溃。
解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给it重新
赋值即可。
*/
while(it != v.end())
{
cout<< *it << " " ;
++it;
}
cout<<endl;
return 0;
}
2、
#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就认为该位置迭代器失效 了。
3、
#include <string>
void TestString()
{
string s("hello");
auto it = s.begin();
// 放开之后代码会崩溃,因为resize到20会string会进行扩容
// 扩容之后,it指向之前旧空间已经被释放了,该迭代器就失效了
// 后序打印时,再访问it指向的空间程序就会崩溃
//s.resize(20, '!');
while (it != s.end())
{
cout << *it;
++it;
}
cout << endl;
it = s.begin();
while (it != s.end())
{
it = s.erase(it);
// 按照下面方式写,运行时程序会崩溃,因为erase(it)之后
// it位置的迭代器就失效了
// s.erase(it);
++it;
}
好了,今天的分享就到这里了
如果对你有帮助,记得点赞👍+关注哦!
我的主页还有其他文章,欢迎学习指点。关注我,让我们一起学习,一起成长吧!