创作初心:在加深个人对知识系统理解的同时希望可以帮助到更多需要的同学

😄柯一梦的专栏系列

🚀柯一梦的Gitee主页

🛠️柯一梦主页详情

座右铭:心向深耕,不问阶序;汗沃其根,花自满枝。


目录

1.initializer_list

编辑

2.emplace_back:

3.使用for循环打印多参数的自定义类型对象

3.1迭代器遍历v1

3.2for循环遍历

4.leetcode训练题,写一个杨辉三角

4.1使用C语言

4.2使用C++

5.迭代器失效


在今天vector的学习之前我们先讲解几个知识:

1.initializer_list

我们可以看到 c++11 里面vector的构造函数的一个参数是initializer_list,这个参数的作用是什么呢?

initializer_list是一个包装了“ 编译器 临时数组”的轻量级只读对象(这个对象里面有一些成员函数begin,end之类的,并且是用const修饰过了之后的),既然是“ 数组 ”,我们在初始化一些容器的时候就可以用{},传入多个值对容器进行初始化。它的底层原理是:在栈上开辟一个只读的空间,然后vector再主动的去读取initializer_list里面的内容,并且拷贝到自己的容器里。(注意:所以我们在使用{}初始化一个vector对象的时候,本质上就是直接初始化(这里又有小伙伴疑惑了,为什么这里不是隐式类型转换呢?其实如果我们的赋值方式是v{1,2,3},这里编译器会直接把{1,2,3}当成initializer去构造,但是如果是v={1,2,3},这里就涉及到了隐式类型转换,也就是先构造一个函数,然后再进行拷贝构造))

1
2
3
4
5
6
7
8
9
int main()
{
vector<int> v1{ 1,2,3,4,5,6,7,8,9,10 };
for (const auto& e : v1)
{
cout << e << " ";
}
return 0;
}

2.emplace_back:

emplace_back和push_back有什么区别呢?我们来看下面这段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
int main()
{
struct A//我们先定义一个类
{
A(int a1,int a2)
:_a1(a1)
,_a2(a2)
{

}
A(const A& a)
{

}
int _a1;
int _a2;
};
vector<A> v1;
//我们先使用push_back实现以下三个东西
//1.传入构造好的对象
A a(1,2);
v1.push_back(a);
//2.传入匿名对象
v1.push_back(A(1, 2));
//3.多参数构造函数的隐式类型转换需要用{}括起来
v1.push_back({ 1,2 });
//我们再使用emplace_back实现一下:
//1.传入构造好的对象
A a(1, 2);
v1.emplace_back(a);
//2.传入匿名对象
v1.emplace_back(A(1, 2));
//3.不需要使用{}进行隐式类型转换
v1.emplace_back(1,2);//因为他是模版的可变参数,所以可以直接传过去
return 0;
}


3. 使用 for循环打印多参数的自定义类型对象

3.1迭代器遍历v1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
int main()
{
struct A//我们先定义一个类
{
A(int a1, int a2)
:_a1(a1)
, _a2(a2)
{

}
A(const A& a)
:_a1(a._a1)
, _a2(a._a2)
{

}
int _a1;
int _a2;
};
vector<A> v1;
//我们先使用push_back实现以下三个东西
//1.传入构造好的对象
A a1(1, 2);
v1.push_back(a1);
//2.传入匿名对象
v1.push_back(A(1, 2));
//3.多参数构造函数的隐式类型转换需要用{}括起来
v1.push_back({ 1,2 });
//我们再使用emplace_back实现一下:
//1.传入构造好的对象
A a2(1, 2);
v1.emplace_back(a2);
//2.传入匿名对象
v1.emplace_back(A(1, 2));
//3.不需要使用{}进行隐式类型转换
v1.emplace_back(1, 2);//因为他是模版的可变参数,所以可以直接传过去


//1.iterator访问v1
vector<A>::iterator it1 = v1.begin();
while(it1!=v1.end())
{
cout << (*it1)._a1 << " " << (*it1)._a2 << endl;
++it1;
}
return 0;
}

我刚才在写迭代器遍历v1的内容的时候出现了一些问题,我刚才写的是一个空的 构造函数 ,vector的push_back插入的是拷贝对象而不是对象本身,所以我们子啊使用push_back的时候要把自定义类型的拷贝构造先写好。

3.2for循环遍历

1
2
3
4
5
6
7
8
9
10
11
//C++13的遍历方式
for (const auto& aa : v1)
{
cout << aa._a1 << " " << aa._a2 << endl;//我们这里的本来就是对象,所以不能使用->
}
//C++17的遍历方式,结构化绑定
for (auto [x, y] : v1)
{
cout << x << " " << y << endl;
}
return 0;


4.leetcode训练题,写一个杨辉三角

4.1使用C语言

先讲一下做题思路:我们要先构造出来一个二维数组,然后再去构造一维数组,紧接着就是给一维数组里面的各个地址 赋值 。在赋值的时候我们要先将每个一维数组的头尾进行赋值,然后再让每一行的元素=上一行该位置与前一个位置元素的和

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int** generate(int numRows, int* returnSize, int** returnColumnSizes) {
int** aa = (int**)malloc(sizeof(int*)*numRows);
*returnColumnSizes = (int*)malloc(sizeof(int)*numRows);
for(int i = 0;i<numRows;i++)
{
(*returnColumnSizes)[i] = i+1;
aa[i] = (int*)malloc(sizeof(int)*(i+1));
}
for(int i = 0;i<numRows;i++)
{
aa[i][0] = 1;
aa[i][i] = 1;
for(int j = 1;j<i;j++)
{
aa[i][j] = aa[i-1][j]+aa[i-1][j-1];
}
}
*returnSize = numRows;
return aa;
}

4.2使用C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<vector<int>> aa;
aa.resize(numRows,vector<int>());
for(size_t num = 0;num<numRows;++num)
{
aa[num].resize(num+1,1);
}
for(size_t i = 2;i<aa.size();++i)
{
for(size_t j = 1;j<aa[i].size()-1;++j)
{
aa[i][j] = (aa[i-1][j])+(aa[i-1][j-1]);
}
}
return aa;
}
};

5.迭代器失效

vector的 insert 和erase的使用都会牵扯到迭代器,这里面有一个隐形的炸弹就是迭代器失效

实例1:

insert:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include<initializer_list>
#include<iostream>
#include<vector>//包含vector
#include<algorithm>//包含find
void test2()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
//在2的位置插入一个10
vector<int>::iterator pos = find(v.begin(), v.end(), 2);
v.insert(pos,10);
v.erase(pos);
for (auto& e : v)
{
cout << e << " ";
}
}

int main()
{
test2();
return 0;
}

这块代码会直接报错,因为vs2022编译器会直接终止这种违法行为。有的同学问为什么会违法呢?其实我们在insert的时候,v的空间可能会涉及到扩容的问题,一旦涉及扩容,先前指向的空间就会被释放,pos也就变成了野指针。

实例2

erase:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include<initializer_list>
#include<iostream>
#include<vector>//包含vector
#include<algorithm>//包含find
void test2()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
v.push_back(7);
v.push_back(8);
v.push_back(9);
v.push_back(10);
//在2的位置插入一个10
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if ((*it) / 2 == 0)
{
v.erase(it);
++it;
}
}
}

类似于insert,这个erase使用过的it迭代器依旧报错了

解决方法:我们再使用erase或者insert的时候,我们都要对迭代器进行重新赋值(erase和insert函数都有返回值,返回值就是迭代器)!!!