STL库——vector(类函数学习)
ʕ • ᴥ • ʔ
づ♡ど
🎉 欢迎点赞支持🎉
个人主页:励志不掉头发的内向程序员;
专栏主页:C++语言;
文章目录
前言
一、标准库中的vector类
二、vector的成员函数
2.1、构造函数
2.2、析构函数
2.3、赋值重载
2.4、运算符重载
2.4.1、[]符重载
2.4.2、其他运算符重载
2.5、插入删除函数
2.5.1、push_back函数
2.5.2、pop_back函数
2.5.3、insert函数
2.5.4、erase函数
2.6、其他成员函数
2.6.1、reserve函数
2.6.2、resize函数
2.6.3、size函数
2.6.4、capacity函数
2.6.5、empty函数
2.6.6、clear函数
三、循环遍历顺序表
四、流输入/输出
总结
前言
我们上一节课学习了string类的使用和模拟实现,我们对string类应该有一定的理解了,接下来我们来学习下一个容器vector,这个容器在我们未来是频繁使用的,我们一起来看看怎么使用吧。
一、标准库中的vector类
与string相比,vector才是我们STL库真正意义上的一员,也是第一个基础的东西。它就是数据结构中的顺序表。它的设计与string相比就比较简单了,也没有那么冗余了。
我们可以粗略的看到它比string少了很多的成员函数。
二、vector的成员函数
我们vector类都保存在我们vector的头文件中
#include <vector>
2.1、构造函数
我们在学习string中就知道了我们想要知道一个类是怎么使用的第一步就是去看看它是怎么构造的,也就是构造函数是什么。
可以看到我们的vector的构造函数比string的7个构造函数简单多了,第一个无参的构造,也就是默认构造;第二个用n个value去构造;第三个就是用迭代器区间去构造以及最后一个拷贝构造,它的操作就比较简单了。
由于我们的vector是一个模板类,所以我们调用时就得告诉编译器我们创建的vector是什么类型的vector,以及它使用的空间配置器是什么也就是内存池(目前不用管),如果不写就是默认的空间配置器。
int main()
{// 不同的类型给编译器生成了存储不同类型的vector// 存储int类型vector<int> a();//存储char类型vector<char> b();//存储string类型vector<string> c();//存储bool类型vector<bool> d();//vector的默认构造return 0;
}
我们第二种构造就是提供n个value去构造vector
第三个参数是空间配置器,我们不用去管它,用缺少参数即可。
int main()
{// 用10个1去构造vector<int> avector<int> a(10, 1);// 用5个x去构造vector<char> bvector<char> b(5, 'x');return 0;
}
我们第3种就是使用迭代器区间初始化,也就是一个vector的迭代器区间去初始化另外一个vector。
最后一个参数依然是空间配置器,用缺省值即可。
int main()
{// 用a的迭代器区间去初始化bvector<char> a(10, 'x');vector<char> b(a.begin() + 2, a.end() - 2);return 0;
}
最后一种也就是最常用的拷贝构造
它可以直接拷贝别的vector,也可以隐式类型转换。
int main()
{vector<int> a(10, 1);// 直接拷贝avector<int> b(a);//将数组隐式类型转换成vector后在拷贝给cvector<int> c({ 1, 2, 3, 4 });return 0;
}
2.2、析构函数
不用多管,它会自动为我们去释放空间的。
2.3、赋值重载
赋值重载和string相同。
int main()
{vector<int> a(10, 1);vector<int> b;b = a;for (auto x : b){cout << x << " ";}cout << endl;return 0;
}
2.4、运算符重载
2.4.1、[]符重载
和string一样,vector也有[]符重载,这个重载对vector也很重要,它让vector可以向数组一样访问和修改。
它的实现原理其实和string没有什么差别,甚至比string更为简单,因为顺序表本质就是数组,我们下一章节再做说明。
int main()
{vector<int> a(10, 1);for (int i = 0; i < a.size(); i++){a[i] += i;cout << a[i] << " ";}cout << endl;return 0;
}
这个操作符如果越界了就会报错。
int main()
{vector<int> a(10, 1);cout << a[10] << endl;return 0;
}
2.4.2、其他运算符重载
我们的比较运算符重载的比较方法其实和我们的string比较是很相似的,它会从前向后比较我们每一个元素的大小,如果其中一个大,那它就大,如果相等就会比较下一个元素。比到最后都相等就看谁比较长。一样长那就相等了。
int main()
{// 相同谁长谁大vector<int> a(1, 100);vector<int> b(1, 101);if (a < b) cout << "a < b" << endl;else if (a == b) cout << "a == b" << endl;else cout << "a > b" << endl;return 0;
}
int main()
{// 从前往后比,第一次出现不同的大的那个就大vector<int> a({ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 });vector<int> b({ 9, 8, 5, 6, 7, 4, 3, 2, 1, 0, 0, 0, 0, 0 });if (a < b) cout << "a < b" << endl;else if (a == b) cout << "a == b" << endl;else cout << "a > b" << endl;return 0;
}
int main()
{// 从前往后比,都一样长且每个元素都一样才相等vector<int> a({ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 });vector<int> b({ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 });if (a < b) cout << "a < b" << endl;else if (a == b) cout << "a == b" << endl;else cout << "a > b" << endl;return 0;
}
当然,如果是类型是别的容器,那每个元素就按那个容器的比较规则去比较大小后再按vector的比较规则去比较。
int main()
{// 从前往后比,这个元素是string,所以先用string的比较方式去比较不同元素的大小// 如xxxxx比yyyyy小,比出string的大小后再去比vector大小,就比如a和b不同元素// (xxxxx和yyyyy)b比a大,所以b就比a大vector<string> a;a.push_back("123456789");a.push_back("xxxxx");vector<string> b;b.push_back("123456789");b.push_back("yyyyy");if (a < b) cout << "a < b" << endl;else if (a == b) cout << "a == b" << endl;else cout << "a > b" << endl;return 0;
}
2.5、插入删除函数
2.5.1、push_back函数
此函数的功能就在于尾插我们vector的元素
int main()
{vector<int> a({ 1, 2, 3 });a.push_back(4);a.push_back(5);a.push_back(6);for (auto x : a){cout << x << " ";}cout << endl;return 0;
}
一次只能尾插一个元素。
2.5.2、pop_back函数
次函数的功能就是删除尾部的一个元素。
int main()
{vector<int> a({ 1, 2, 3, 4, 5, 6, 7, 8, 9});a.pop_back();a.pop_back();a.pop_back();for (auto x : a){cout << x << " ";}cout << endl;return 0;
}
2.5.3、insert函数
我们的顺序表没有提供头插和头删,我们如果想要头插的话就得调用这个函数,这个函数的功能就是在任意位置插入元素,但是和string不同的地方在于它现在只能用迭代器来确定位置,而不能是元素下标。
我们第一个的含义是在哪个位置(迭代器)插入元素。
int main()
{vector<int> a({ 1, 2, 3, 4, 5, 6, 7, 8, 9});// 在5的位置插入元素0a.insert(a.begin() + 5, 0);for (auto x : a){cout << x << " ";}cout << endl;return 0;
}
第二个则是第一个的加强版,在哪个位置插入几个相同的元素。
int main()
{vector<int> a({ 1, 2, 3, 4, 5, 6, 7, 8, 9});// 在5的位置插入10个元素0a.insert(a.begin() + 5, 10,0);for (auto x : a){cout << x << " ";}cout << endl;return 0;
}
第三个则是在哪个位置插入一个区间的元素,一般不怎么使用。
int main()
{vector<int> a({ 1, 2, 3, 4, 5, 6, 7, 8, 9});vector<int> b({ 11, 12, 13, 14, 15, 16, 17, 18, 19 });// 在a的5的位置插入b的5到7区间的元素a.insert(a.begin() + 5, b.begin() + 5, b.end() - 2);for (auto x : a){cout << x << " ";}cout << endl;return 0;
}
2.5.4、erase函数
这个函数就是可以进行头删的函数,它的功能就是在任意位置删除。
这个函数的使用就很简单了,第一个是想要删除的元素的迭代器位置,第二个则是想要删除的迭代器区间。
int main()
{vector<int> a({ 1, 2, 3, 4, 5, 6, 7, 8, 9});// 删除第五个位置的元素a.erase(a.begin() + 5);for (auto x : a){cout << x << " ";}cout << endl;return 0;
}
int main()
{vector<int> a({ 1, 2, 3, 4, 5, 6, 7, 8, 9});// 删除第五个到第七个位置的元素a.erase(a.begin() + 5, a.end() - 2);for (auto x : a){cout << x << " ";}cout << endl;return 0;
}
2.6、其他成员函数
2.6.1、reserve函数
我们一起来看看我们的扩容函数是如何扩容的吧。
int main()
{vector<int> a;size_t sz = a.capacity();cout << "making a grow:\n";for (int i = 0; i < 100; i++){a.push_back(i);if (a.capacity() != sz){sz = a.capacity();cout << "capacity changed: " << sz << endl;}}return 0;
}
我们可以发现我们的vector其实也是1.5倍扩容,只是有的时候得到的小数是向上取整,比如1->2就是向上取整的。
当然,如果我们插入100个数据要扩容这么多次耗费性能,不想要扩容这么多次该怎么办呢,其实我们可以提前扩容,提前给它100个空间即可,这就是这个函数的作用。
我们来看看vs中的vector的缩容规则吧,看看它在什么情况下会缩容。
int main()
{vector<int> a({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });a.reserve(20);cout << "size: " << a.size() << " " << "capacity: " << a.capacity() << endl;// i = 5/15/30int i = 30;cout << "reserve: " << i << endl;cout << "capacity: " << a.capacity() << " -> ";a.reserve(i);cout << a.capacity() << endl;return 0;
}
由此可以看出我们的vector在vs的环境下是不会缩容只会扩容的。
2.6.2、resize函数
这个函数的作用就是把vector的size扩容或者缩容到n的
这个resize比reserve函数使用的频率会多很多,它和reserve一样有3种情况。它和reserve一样是不会缩容的,但是它会删除数据。同理也会插入数据。
int main()
{vector<int> a({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });cout << "a: ";for (auto x : a){cout << x << " ";}cout << endl;a.reserve(20);cout << "size: " << a.size() << " " << "capacity: " << a.capacity() << endl;// i = 5/15/25int i = 5;cout << "resize: " << i << endl;cout << "capacity: " << a.capacity() << " -> ";int n = a.size();a.resize(i, 0);cout << a.capacity() << endl;cout << "size: " << n << " -> " << a.size() << endl;;cout << "a: ";for (auto x : a){cout << x << " ";}cout << endl;return 0;
}
如果size比resize大,它就会删除我们的后面数据,当然如果resize大,就会增加我们指定的数据,如果没有指定就是缺省值,如果空间不够就会扩容。
2.6.3、size函数
主要功能就是返回我们vector中有多少元素。
int main()
{vector<int> a({ 1, 2, 3, 4, 5, 6, 7, 8, 9});cout << a.size() << endl;return 0;
}
2.6.4、capacity函数
主要功能就是返回vector类开辟的内存空间。
int main()
{vector<int> a({ 1, 2, 3, 4, 5, 6, 7, 8, 9});cout << a.capacity() << endl;return 0;
}
2.6.5、empty函数
主要功能就是查看vector类中元素是否为空。
int main()
{vector<int> a({ 1, 2, 3, 4, 5, 6, 7, 8, 9});vector<int> b;if (a.empty()) cout << "YES" << endl;else cout << "NO" << endl;if (b.empty()) cout << "YES" << endl;else cout << "NO" << endl;return 0;
}
2.6.6、clear函数
主要功能就是清空vector类中元素。
int main()
{vector<int> a({ 1, 2, 3, 4, 5, 6, 7, 8, 9});if (a.empty()) cout << "YES" << endl;else cout << "NO" << endl;a.clear();if (a.empty()) cout << "YES" << endl;else cout << "NO" << endl;return 0;
}
三、循环遍历顺序表
我们之前学习了string的循环遍历字符串了,那我们的顺序表该怎么循环遍历呢,答案显而易见,那就是和string一样。
我们的顺序表也重载了[],那我们就可以向数组一样去遍历我们的顺序表了
int main()
{vector<int> a({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });for (int i = 0; i < a.size(); i++){cout << a[i] << ' ';}cout << endl;return 0;
}
同时,我们之前讲过,我们的迭代器的循环遍历是适用于我们的所有容器的,那我们的vector也不例外。
int main()
{vector<int> a({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });vector<int>::iterator it = a.begin();while (it != a.end()){cout << *it << ' ';it++;}cout << endl;return 0;
}
当然,既然我们的迭代器可以,那我们的范围for肯定也是支持的。
int main()
{vector<int> a({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });for (auto h : a){cout << h << ' ';}cout << endl;return 0;
}
四、流输入/输出
我们可以发现,我们的vector不像我们的string一样重载了流输入/输出,原因是因为我们的vector想要输入和输出太简单了,没有必要去重载来框死我们的输入和输出方式,所以就没有重载,如果只是想要最基本的输入和输出,那就像数组那样实现即可。
输入10个数据后输出:
int main()
{vector<int> a({ 1, 2, 3, 4, 5, 6, 7, 8, 9});// 清空数据a.clear();for (int i = 0; i < 10; i++){int x;cin >> x;a.push_back(x);}for (int i = 0; i < a.size(); i++){cout << a[i] << " ";}cout << endl;return 0;
}
总结
以上便是vector类经常使用的函数的用法,大家可以仔细学习,下一章节我们会具体去实现我们的vector类。还有部分我们没有讲过的内容现在要么现在学习还为时过早,要么就是不是很重要,大家可以到后面C++学的还不错时再去看看即可。
🎇坚持到这里已经很厉害啦,辛苦啦🎇
ʕ • ᴥ • ʔ
づ♡ど