STL库——list(类函数学习)
ʕ • ᴥ • ʔ
づ♡ど
🎉 欢迎点赞支持🎉
个人主页:励志不掉头发的内向程序员;
专栏主页:C++语言;
文章目录
前言
一、标准库中的list类
二、list的成员函数
2.1、构造函数
2.2、析构函数
2.3、赋值重载
2.4、运算符重载
2.5、插入和删除函数
2.5.1、push_front/pop_front函数
2.5.2、push_back/pop_back函数
2.5.3、insert/erase函数
2.6、其他成员函数
2.6.1、迭代器
2.6.2、merge函数
2.6.3、unique函数
2.6.4、remove函数
2.6.5、splice函数
三、流输入/输出
总结
前言
在学习完vector过后我们就会发现它和string相比成员函数简化了非常多,学起来也不想string那样困难,这一章节我们学习的list类时我们就会发现它的成员函数和vector相比十分相像,没有多少不同,这就是STL库的魅力所在,我们一起来看看吧
一、标准库中的list类
我们观看STL库就会发现我们vector有的成员函数我们list基本上也有,这些不同容器之间的区别在于底层的不同,但是STL库封装起来我们的上层基本上没有什么区别。
我们可以看到vector的成员函数和list是十分相像的。
二、list的成员函数
我们list类都保存在我们list的头文件中
#include <list>
2.1、构造函数
list构造也分为4种,无参构造、带参构造、迭代器区间构造以及拷贝构造
int main()
{// 无参构造list<int> a;// 带参构造,也就是用4(n)个0(val)构造blist<int> b(4, 0);// 迭代器区间构造vector<int> c({ 1, 2, 3, 4, 5, 6, 7, 8, 9 });list<int> d(c.begin() + 1 , c.end() - 4);// 拷贝构造list<int> e(d);return 0;
}
我们可以看到和我们vector的构造函数是一样的。但是与vector不同的是list不支持下标+[]访问我们任意一个元素了,因为list想要支持得从前往后遍历,成本很高。所以想要遍历可以用迭代器和范围for。
2.2、析构函数
系统自动调用,我们不用关心。
2.3、赋值重载
赋值重载也很简单。
int main()
{list<int> a;list<int> b;a.push_back(1);a.push_back(2);a.push_back(3);a.push_back(4);// 赋值重载b = a;cout << "a: ";for (auto x : a){cout << x << " ";}cout << endl;cout << "b: ";for (auto x : b){cout << x << " ";}cout << endl;return 0;
}
2.4、运算符重载
我们的比较运算符大致规则就和vector一样,这里就不过多赘述了。
2.5、插入和删除函数
2.5.1、push_front/pop_front函数
这两个函数就是头插和头删,因为list的特殊结构,导致它的头删和头插都很容易。
int main()
{list<int> a(2, 0);a.push_front(1);a.push_front(2);a.push_front(3);cout << "a: ";for (auto x : a){cout << x << " ";}cout << endl;a.pop_front();a.pop_front();a.pop_front();cout << "a: ";for (auto x : a){cout << x << " ";}cout << endl;return 0;
}
2.5.2、push_back/pop_back函数
尾插和尾删也很容易,就不多说了。
int main()
{list<int> a(2, 0);a.push_back(1);a.push_back(2);cout << "a: ";for (auto x : a){cout << x << " ";}cout << endl;a.pop_back();a.pop_back();cout << "a: ";for (auto x : a){cout << x << " ";}cout << endl;return 0;
}
2.5.3、insert/erase函数
这两个函数和vector一样是依靠我们迭代器去插入和删除的。
但是和vector不同之处在于list的迭代器不能进行+/-,如果想要插入或者删除第3个位置的元素,不能直接begin+3或者end-*,只能用一个循环先去改变迭代器位置再去插入或者删除。
int main()
{list<int> a({ 1, 2, 3, 4, 5, 6, 7, 8, 9 });list<int>::iterator it = a.begin();int k = 3;while (k--){it++;}a.erase(it);cout << "a: ";for (auto x : a){cout << x << " ";}cout << endl;return 0;
}
这使得本来使用率不高的两个函数在list中更低了。
2.6、其他成员函数
2.6.1、迭代器
我们迭代器和原来的用法差不多,比如来个循环遍历list。
int main()
{list<int> a;list<int> b;a.push_back(1);a.push_back(2);a.push_back(3);a.push_back(4);list<int>::iterator it = a.begin();cout << "a: ";while(it != a.end()){cout << *it << " ";it++;}cout << endl;return 0;
}
可以很成功的答应出来,但是这个迭代器和vector还是有区别的,比如它不支持加减。
vector迭代器erase:
int main()
{vector<int> a({ 1, 2, 3, 4, 5, 6, 7, 8, 9 });a.erase(a.begin() + 5);cout << "a: ";for (auto x : a){cout << x << " ";}cout << endl;return 0;
}
很成功的给删掉了。
list迭代器erase:
int main()
{list<int> a({ 1, 2, 3, 4, 5, 6, 7, 8, 9 });a.erase(a.begin() + 5);cout << "a: ";for (auto x : a){cout << x << " ";}cout << endl;return 0;
}
直接就运行失败了。
这是因为我们链表的迭代器不是原生指针了,它底层的物理空间不是连续的。
我们之前从功能上划分把迭代器分为:
- iterator
- const_iterator
- reverse_iterator
- const_reverse_iterator
但是迭代器其实还可以从性质方面进行划分:
- 单向迭代器
- 双向迭代器
- 随机迭代器
我们vector/string/deque就是经典的随机迭代器,这种迭代器特点就是除了支持++/--外还支持+/-。而我们list/map/set就是经典的双向迭代器,这种迭代器只支持++/--,不支持+/-。而单向迭代器有forwad_list/unordered_map/unordered_set,这种比双向迭代器支持的还少,只支持++,连--都不支持。它们的性质是由它们底层来决定的。
2.6.2、merge函数
这个函数的作用就是合并我们的链表。
当然它有一个前提条件就是这两个链表必须是有序的。
int main()
{list<double> a({ 1.1, 2.2, 3.3, 4.4 });list<double> b({ 0.9, 2.1, 3.2, 4.3 });a.merge(b);cout << "a: ";for (auto x : a){cout << x << " ";}cout << endl;cout << "b: ";for (auto x : b){cout << x << " ";}cout << endl;return 0;
}
如果不是有序的就会报错。
2.6.3、unique函数
这个函数的主要作用就是去重,它会把list中相同的元素删掉一个,只保留一个,用法和merge相似,list必须有序,不然程序就会失效。
int main()
{list<int> a({ 1, 2, 2, 3, 4, 4, 5 });cout << "a: ";for (auto x : a){cout << x << " ";}cout << endl;a.unique();cout << "a: ";for (auto x : a){cout << x << " ";}cout << endl;return 0;
}
无序的话:
2.6.4、remove函数
这个函数的作用就是删除一个值,它和erase很像,但是erase是给一个迭代器,而这个是给一个元素,它先是去查找这个元素在不在list中,在就把它删除,不在就不动。
int main()
{list<int> a({ 1, 2, 2, 5, 4, 4, 5 });cout << "a: ";for (auto x : a){cout << x << " ";}cout << endl;a.remove(2);cout << "a: ";for (auto x : a){cout << x << " ";}cout << endl;return 0;
}
2.6.5、splice函数
这个函数的作用是把一个list中的数据转移到另外一个list中去,转移的位置是在position之前。
int main()
{list<int> a({ 1, 2, 3, 4, 5, 6, 7, 8, 9 });list<int> b({ 10, 20, 30, 40 });list<int>::iterator it = a.begin();it++;// 相当于是吧b的数据转移到a的it位置之前a.splice(it, b);cout << "a: ";for (auto x : a){cout << x << " ";}cout << endl;cout << "b: ";for (auto x : b){cout << x << " ";}cout << endl;return 0;
}
其他的像empty、clear、size之类的就不过多说明了,用法和作用vector一样。
三、流输入/输出
list没有对流输入和输出进行重载,原因可能是不方便,而且没有必要,我们就老老实实自己做就好了。
总结
以上便是我们list常用的成员函数啦。对比起string,我们现在应该很容易就能接受这些内容,这就是STL库封装的好处,下节课就是list的模拟实现了,大家下一章节再见。
🎇坚持到这里已经很厉害啦,辛苦啦🎇
ʕ • ᴥ • ʔ
づ♡ど