vector底层本质就是一个顺序表,它是一个可变长的数组,采用连续存储的空间来存储数据,它的元素类型也可以是任意的内置类型或者自定义类型。
第一种方式:定义一个任意类型的空vector
vector<int> v1;
vector<double> v2;
vector<string> v3;
第二种方式:定义一个任意类型的vector,并用n个val来初始化vector
vector<int> v4(10, 5);// 用10个5来初始化vector
第三种方式:定义一个任意类型的vector,并用迭代器区间来初始化vector
vector<int> v5(v4.begin(), v4.end());// 用v4的迭代器区间来初始化v5
string s("hello world");
vector<char> v6(s.begin(), s.end());// 用s的迭代器区间来初始化v6
第一种方式:下标+[]循环遍历
#include
#include
#include
using namespace std;
int main()
{
vector<int> v(10, 10);
for (size_t i = 0; i < v.size(); i++)
{
v[i] += i;
cout << v[i] << " ";
}
cout << endl;
return 0;
}
第二种方式:迭代器循环遍历
#include
#include
#include
using namespace std;
int main()
{
vector<int> v(10, 10);
vector<int>::iterator it = v.begin();
while (it != v.end())
{
(*it)++;
cout << *it << " ";
}
cout << endl;
return 0;
}
第三种方式:范围for遍历
#include
#include
#include
using namespace std;
int main()
{
vector<int> v(10, 10);
for (auto i : v)
{
i++;
cout << i << " ";
}
cout << endl;
return 0;
}
我们可以通过一个例子来了解如何通过vector实现二维数组,假设我们定义一个5*10的二维空数组,循环向二维数组写入值,最后循环打印这个二维矩阵。
#include
#include
#include
using namespace std;
int main()
{
vector<vector<int>> v;
v.resize(5);// 开辟5行空间
for (size_t i = 0; i < 5; i++)
{
v[i].resize(10);// 开辟10列空间
}
// 至此,5行10列的二维数组初始化完毕
// 向二维数组写入
for (size_t i = 0; i < v.size(); i++)
{
for (size_t j = 0; j < v[i].size(); j++)
{
v[i][j] = i*j;
}
}
// 打印二维数组
for (size_t i = 0; i < v.size(); i++)
{
for (size_t j = 0; j < v[i].size(); j++)
{
cout << v[i][j] << " ";
}
cout << endl;
}
return 0;
}
我们可以通过实验测试一下在Linux下vector是怎么扩容的,一次扩容扩大多少倍?我们定义一个vector,利用nowCapacity来记录vector的容量大小。循环向vector尾插100次数据,当vector的容量发生变化时,打印出来观察前后容量的变化情况。
#include
#include
#include
using namespace std;
int main()
{
vector<int> v;
size_t nowCapacity = v.capacity();
cout << "nowCapacity:" << nowCapacity << endl;
for (size_t i = 0; i < 100; i++)
{
v.push_back(i);
if (nowCapacity != v.capacity())
{
nowCapacity = v.capacity();
cout << "nowCapacity:" << nowCapacity << endl;
}
}
return 0;
}
在Linux下的运行结果如下图所示,我们可以看到它是呈现2倍增长的。
相同的代码在VS编译器下实验最后的结果是呈1.5倍增长。其实vector一次扩容多少倍并没有确定的数值,一般就是1.5倍增长到2倍增长这样一个范围,因为如果单次增长较多那么增长的次数就会更少,效率也更高,但是如果单次增长过多容易造成空间浪费;如果单次增长较少那么增长的次数就会更多,效率也更低,但是不容易造成空间的浪费。
vector的insert函数不支持下标的方式去插入,只提供了借助迭代器完成插入的接口。
第一种方式:在指定迭代器的位置插入一个值
vector<int> v(10, 10);
v.insert(v.begin() + 3, 11);// 在第三个位置插入11
第二种方式:在指定迭代器的位置插入n个val值
vector<int> v(10, 10);
v.insert(v.begin() + 2, 5, 2);// 在第二个位置往后连续插入5个2
vector的erase函数也不支持下标的方式去删除,只提供了借助迭代器完成删除的接口。
第一种方式:删除指定迭代器位置的值
vector<int> v(10, 10);
v.erase(v.begin());// 删除第3个位置的值
第二种方式:删除迭代器区间内的所有值
vector<int> v(10, 10);
v.erase(v.begin() + 2, v.begin() + 6);// 删除第2个位置到第6个位置的所有值
示例一:
#include
#include
using namespace std;
int main()
{
vector<int> v;
// 循环插入1,2,3,4,5,6到v中
for (int i = 1; i <= 6; i++)
{
v.push_back(i);
}
// 在所有的偶数前面插入10
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if ((*it) % 2 == 0)
{
v.insert(it, 10);
}
it++;
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
return 0;
}
上面的代码示例中,我们循环向数组中所有的偶数前插入10,会出现两种情况的迭代器失效:
第一种: 发生扩容时出现野指针问题
第二种:空间足够时不会出现扩容,但会出现无限插入的死循环问题
这种情况虽然没有出现扩容造成的野指针问题,但迭代器的指向意义已经改变了,这也是迭代器失效问题。
其实在标准库中vector的insert函数的实现是有返回值的,insert函数的返回值是新插入值位置的迭代器。
所以示例一的正确写法应该是下面这样的:每一次insert插入的时候都更新一下it的值,这样就可以避免扩容造成的野指针问题。然后每一次insert插入之后都需要对it进行加一操作,这样就可以避免死循环插入的问题。
#include
#include
using namespace std;
int main()
{
vector<int> v;
// 循环插入1,2,3,4,5,6到v中
for (int i = 1; i <= 6; i++)
{
v.push_back(i);
}
// 在所有的偶数前面插入10
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if ((*it) % 2 == 0)
{
it = v.insert(it, 10);// v:1,10,2,3,4,5,6
// 此时it指向的是10的位置
it++;
}
it++;
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
return 0;
}
示例二:
#include
#include
using namespace std;
int main()
{
vector<int> v;
// 循环插入1,2,3,4,5,6到v中
for (int i = 1; i <= 6; i++)
{
v.push_back(i);
}
// 在所有的偶数前面插入10
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if ((*it) % 2 == 0)
{
v.erase(it);
}
it++;
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
return 0;
}
上面的代码也是迭代器失效的问题,它同样存在两种问题:
第一种:因为最后一个数也是偶数,当最后一个数被删除以后,it加一去到了v.end()的后一个位置,所以就会陷入死循环
第二种:数字3和5是没有被检测是否属于偶数的,因为删除了一个偶数之后,下一个数挪动到被删除的位置,然后执行it++操作,跳过了新数据的检测。
同样的,在标准库种vector的erase函数的实现是有返回值的,erase函数的返回值是被删除数据的位置。
所以示例二的正确写法应该是下面这样的:如果it当前指向的位置是偶数的话,直接erase函数删除该偶数,然后将返回值给it,it此时指向的就是被删除偶数的下一个数据,所以不需要再进行加一操作。如果it当前指向的位置是奇数的话,则进行加一操作。
#include
#include
using namespace std;
#include
#include
using namespace std;
int main()
{
vector<int> v;
// 循环插入1,2,3,4,5,6到v中
for (int i = 1; i <= 6; i++)
{
v.push_back(i);
}
// 在所有的偶数前面插入10
vector<int>::iterator it = v.begin();
while (it != v.end())
{
// 如果是偶数,直接删除,将返回值给it
// 此时it指向的就是被删除数据的下一个数据,所以不需要++
if ((*it) % 2 == 0)
{
it = v.erase(it);
}
// 如果是奇数,再进行++
else
{
it++;
}
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
return 0;
}
所以,vector的迭代器失效问题是很容易发生的,我们在使用的时候一定要非常非常小心。一定要画图动态地模拟它的变化,再针对不同的迭代器失效问题使用对应的解决方法。