一篇搞懂C++ STL 单向链表std::forward_list
@TOC
前言
C++标准模板库(STL)提供了多种容器类来处理不同的数据结构,其中std::forward_list
是用于实现单向链表(Singly Linked List)的容器。与其他容器如std::list
、std::vector
相比,std::forward_list
更为轻量,专为需要快速插入和删除操作、以及内存效率的应用场景而设计。本篇文章将详细介绍std::forward_list
的特性、与std::list
的区别、所有构造函数和操作函数,并通过示例代码帮助你全面理解这一容器的使用方法。
什么是std::forward_list
std::forward_list
是C++11引入的单向链表容器。它的设计非常简单,仅包含指向下一个节点的指针,因此与双向链表(std::list
)相比,它占用的内存更少。由于其单向链表的性质,std::forward_list
只能高效地在头部进行插入和删除操作,遍历时只能从头到尾单向访问。
std::forward_list
与std::list
的区别
std::list
是双向链表容器,具有指向前后两个节点的指针,因此可以在头部和尾部进行高效的插入和删除操作,并且支持双向遍历。而std::forward_list
则仅有一个指向下一个节点的指针,无法进行反向遍历,也无法在尾部高效地插入或删除。
通过一个简单的字符串图可以更清晰地理解这两者的区别:
// std::forward_list(单向链表)
head
v
+---+ +---+ +---+
| 1 | -> | 2 | -> | 3 |
+---+ +---+ +---+
// std::list(双向链表)
head tail
v ^
+---+ +---+ +---+
| 1 | <->| 2 | <->| 3 |
+---+ +---+ +---+
如图所示,std::forward_list
只能从头到尾进行单向遍历,而std::list
可以在任意方向上遍历,并且更灵活,但代价是更高的内存开销。
std::forward_list
的构造函数
-
默认构造函数
创建一个空的forward_list
容器。std::forward_list<int> flist;
-
指定大小的构造函数
创建一个包含n
个默认初始化元素的forward_list
。std::forward_list<int> flist(5); // 创建一个包含5个默认值为0的元素的forward_list
-
指定大小和初始值的构造函数
创建一个包含n
个元素,每个元素的值为val
。std::forward_list<int> flist(5, 10); // 创建一个包含5个值为10的元素的forward_list
-
区间构造函数
创建一个forward_list
,初始化为迭代器范围[first, last)
内的元素。std::vector<int> vec = {1, 2, 3, 4, 5}; std::forward_list<int> flist(vec.begin(), vec.end()); // 用vector的元素初始化forward_list
-
复制构造函数
创建一个forward_list
,它是另一个forward_list
的副本。std::forward_list<int> flist1 = {1, 2, 3, 4, 5}; std::forward_list<int> flist2(flist1); // 复制flist1
-
移动构造函数
创建一个forward_list
,通过移动另一个forward_list
的内容来初始化。std::forward_list<int> flist1 = {1, 2, 3, 4, 5}; std::forward_list<int> flist2(std::move(flist1)); // 移动flist1到flist2
std::forward_list
的操作函数
-
push_front()
在链表头部插入元素。std::forward_list<int> flist = {2, 3, 4}; flist.push_front(1); // flist = {1, 2, 3, 4}
-
pop_front()
删除链表头部的元素。std::forward_list<int> flist = {1, 2, 3, 4}; flist.pop_front(); // flist = {2, 3, 4}
-
insert_after()
在指定位置之后插入元素或元素序列。std::forward_list<int> flist = {1, 3, 4}; auto it = flist.insert_after(flist.begin(), 2); // 在1之后插入2,flist = {1, 2, 3, 4}
-
erase_after()
删除指定位置之后的元素。std::forward_list<int> flist = {1, 2, 3, 4}; flist.erase_after(flist.begin()); // 删除2,flist = {1, 3, 4}
-
front()
返回链表头部的元素。std::forward_list<int> flist = {1, 2, 3, 4}; int frontElem = flist.front(); // frontElem = 1
-
clear()
清空链表中的所有元素。std::forward_list<int> flist = {1, 2, 3, 4}; flist.clear(); // flist = {}
-
empty()
判断链表是否为空。std::forward_list<int> flist; bool isEmpty = flist.empty(); // isEmpty = true
-
size()
由于std::forward_list
的单向链表特性,它没有直接提供size()
函数来获取元素个数。通常你需要手动遍历链表来计算元素的数量:std::forward_list<int> flist = {1, 2, 3, 4}; size_t size = std::distance(flist.begin(), flist.end()); // size = 4
示例代码
#include <iostream>
#include <forward_list>
#include <iterator>
int main() {
// 创建一个单向链表
std::forward_list<int> flist = {3, 4, 5};
// 在链表头部插入元素
flist.push_front(2); // flist = {2, 3, 4, 5}
flist.push_front(1); // flist = {1, 2, 3, 4, 5}
// 插入元素到指定位置后
auto it = flist.insert_after(flist.begin(), 6); // flist = {1, 6, 2, 3, 4, 5}
// 删除指定位置后的元素
flist.erase_after(flist.begin()); // flist = {1, 2, 3, 4, 5}
// 删除头部元素
flist.pop_front(); // flist = {2, 3, 4, 5}
// 获取链表头部的元素
std::cout << "Front element: " << flist.front() << std::endl; // Front element: 2
// 遍历链表并输出每个元素
std::cout << "Elements in forward_list: ";
for (int n : flist) {
std::cout << n << " ";
}
std::cout << std::endl;
// 计算链表中的元素个数
size_t size = std::distance(flist.begin(), flist.end());
std::cout << "Size of forward_list: " << size << std::endl; // Size of forward_list: 4
// 清空链表
flist.clear();
std::cout << "Size after clearing: " << std::distance(flist.begin(), flist.end()) << std::endl; // Size after clearing: 0
return 0;
}
总结
std::forward_list
是一个轻量级的单向链表容器,适用于内存开销敏感的场景,以及需要高效进行
头部插入和删除的场景。尽管它在功能上不如std::list
全面,但它的简单性使得它在特定应用中更具优势。通过理解std::forward_list
的构造函数和操作函数,并结合实际需求,你可以选择合适的容器来优化程序的性能和内存使用。
- 点赞
- 收藏
- 关注作者
评论(0)