C++STL之list(用法超详解)
C++STL之list
- 1.list基本介绍
- 2.构造函数
- 3.迭代器
- 4.空间成员函数
- 5.元素获取成员函数
- 6.元素操作成员函数
- 7.迭代器失效问题
- 8.list和vector的对比
🌟🌟hello,各位读者大大们你们好呀🌟🌟
🚀🚀系列专栏:【C++的学习】
📝📝本篇内容:list基本介绍;构造函数;迭代器;空间成员函数;元素获取成员函数;元素操作成员函数;迭代器失效问题;list和vector的对比
⬆⬆⬆⬆上一篇:C++模拟实现string(图解+超详版)
💖💖作者简介:轩情吖,请多多指教(> •̀֊•́ ) ̖́-
1.list基本介绍
在STL中也有对list的实现,它是一个带头双向循环链表,底层是一个双向迭代器。forward_list是单链表,它底层只是一个单向迭代器。与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。但是他也有缺点,与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息。
2.构造函数
list | 构造函数 |
---|---|
list | 默认构造函数 |
list(size_type n, const value_type& val = value_type()) | 构造n个值为val的元素 |
list (const list& x) | 拷贝构造 |
list (InputIterator first, InputIterator last) | 使用迭代器来构造 |
#include <list>
#include <iostream>
using namespace std;
int main()
{
//默认构造
list<int> lt;
//拷贝构造
list<int> lt1(lt);
//5个为20
list<int> lt2(5, 20);
for (auto& e : lt2)
{
cout << e << " ";
}
cout << endl;
//使用迭代器构造
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
list<int> lt3(arr, arr + 10);
for (auto& e : lt3)
{
cout << e << " ";
}
cout << endl;
return 0;
}
3.迭代器
list | iterator/reverse_iterator |
---|---|
begin()+end() | 双向迭代器 |
rbegin()+rend() | 反向迭代器 |
#include <list>
#include <iostream>
using namespace std;
int main()
{
list<int> lt(5, 20);
//迭代器
list<int>::iterator it=lt.begin();
while (it != lt.end())
{
cout << *it << " ";
it++;
}
return 0;
}
#include <list>
#include <iostream>
using namespace std;
int main()
{
list<int> lt{ 1,2,3,3,4,5,6,7,8,9,10 };
//反向迭代器
list<int>::reverse_iterator rit = lt.rbegin();
while (rit != lt.rend())
{
cout << *rit << " ";
rit++;
}
return 0;
}
begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
因为是双向迭代器,因此还可以使用- -
#include <list>
#include <iostream>
using namespace std;
int main()
{
list<int> lt={1,2,3,4,5,6,7,8,9,10};
list<int>::iterator it = lt.end();
while (it != lt.begin())
{
--it;//这边得先--再使用,因为end()指向的是最后一个元素的下一个
cout <<*it << " ";
}
return 0;
}
4.空间成员函数
list | 空间函数 |
---|---|
size() | 链表大小 |
empty() | 链表是否为空 |
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt{1, 2, 3, 4, 5, 6, 7};
//大小
cout << lt.size() << endl;
//是否为空
cout << lt.empty() << endl;
return 0;
}
5.元素获取成员函数
list | 元素获取函数 |
---|---|
back() | 获取最后一个元素的值 |
front() | 获取第一个元素的值 |
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt{1, 2, 3, 4, 5, 6, 7};
cout << "The first element:";
cout << lt.front() << endl;
cout << "The end elemeent:";
cout << lt.back() << endl;
return 0;
}
6.元素操作成员函数
list | 元素操作函数 |
---|---|
push_back( (const value_type& val)) | 尾插 |
push_front( (const value_type& val)) | 头插 |
pop_front() | 头删 |
pop_back() | 尾删 |
iterator insert (iterator position, const value_type& val) | 在positon位置插入val |
void insert (iterator position, size_type n, const value_type& val) | 在position位置插入n个val |
iterator erase (iterator position) | 删除position位置的元素 |
iterator erase (iterator first, iterator last) | 以迭代器的方式删除[first,last)的元素 |
clear() | 清空链表 |
swap(list& lt) | 交换两个链表 |
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt;
//尾插
lt.push_back(10);
lt.push_back(11);
lt.push_back(12);
for (auto& e : lt)
{
cout << e << " ";
}
cout << endl;
//头插
lt.push_front(9);
lt.push_front(8);
lt.push_front(7);
for (auto& e : lt)
{
cout << e << " ";
}
cout << endl;
//头删
lt.pop_front();
for (auto& e : lt)
{
cout << e << " ";
}
cout << endl;
//尾删
lt.pop_back();
for (auto& e : lt)
{
cout << e << " ";
}
cout << endl;
return 0;
}
再来看看插入和删除
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
int main()
{
list<int> lt;
lt.push_back(10);
lt.push_back(11);
lt.push_back(12);
lt.push_back(13);
cout << "初始元素:";
for (auto& e : lt)
{
cout << e << " ";
}
cout << endl;
//insert
list<int>::iterator it=find(lt.begin(), lt.end(), 11);
lt.insert(it, 7);
cout << "insert之后:";
for (auto& e : lt)
{
cout << e << " ";
}
cout << endl;
//erase(iterator position)
auto it1 = find(lt.begin(), lt.end(), 12);
lt.erase(it1);
cout << "erase之后:";
for (auto& e : lt)
{
cout << e << " ";
}
cout << endl;
//erase(iterator first,iterator last)
lt.erase(lt.begin(), lt.end());
cout << "insert之后:";
for (auto& e : lt)
{
cout << e << " ";
}
cout << endl;
return 0;
}
swap()和clear()
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
cout << "初始元素:";
for (auto& e : lt)
{
cout << e<<" ";
}
cout << endl;
//交换swap
list<int> lt1;
lt.swap(lt1);
cout << "lt和默认构造lt1交换后的元素:";
for (auto& e : lt)//没有元素
{
cout << e<<" ";
}
cout << endl;
cout << "lt1的元素:";
for (auto& e : lt1)
{
cout << e << " ";
}
cout << endl;
//清空lt1
lt1.clear();
cout << "lt1清空元素后:";
for (auto& e : lt1)//没有元素
{
cout << e << " ";
}
cout << endl;
return 0;
}
7.迭代器失效问题
在前面咱们讲的vector时,我们也提到过迭代器失效问题,我们的list也会有这个问题,但是有区别。我们的list因为是一个个的结点,因此你进行插入的话并不会造成原来的迭代器失效,但是如果是删除呢?这就会有问题,因为当你删除一个结点时,肯定会释放空间,一旦释放空间,那么这个结点中的内容就已经无效了。当再次使用迭代器进行++时,就类似于访问野指针一样,导致崩溃。删除时的迭代器失效仅仅是被删除的迭代器,其他迭代器不会受任何影响。具体看我下一篇博客的list模拟实现中的迭代器实现可以更好的理解,list的迭代器是经过封装的,但本质还是结点
再来看一下下面的一个比较巧妙的用法
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
auto it = lt.begin();
//一个个进行删除
while (it!=lt.end())
{
//这边如果不用这种写法,就得接收返回值
lt.erase(it++);//后置++是先使用后+1,因此在实现中,返回的其实是没有+1前的值,it迭代器已经+1了
//it=lt.erase(it);
}
if (lt.empty())
{
cout << "list is empty"<<endl;
}
return 0;
}
8.list和vector的对比
🌸🌸C++STL之list的知识大概就讲到这里啦,博主后续会继续更新更多C++的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪