C++11 容器(1) forward_list

C++11 forward_list详解
本文详细介绍了C++11中新增的forward_list容器,包括其作为单链表的特性、迭代器使用、以及特有的操作函数如insert_after、emplace_after、erase_after和splice_after的用法与注意事项。

一 简介

1. C++11开始,STL增加了 forward_list 容器。它的实现是 singly linked list(单链表), 支持 foward iterator (前向迭代器)。

2. 单聊表进行元素的插入和删除是比较快速的,改动指针的指向即可,但前提是知道待处理节点的前驱节点。

3. 由于性能原因,forward_List 并没有提供 size 函数,也不能直接处理最末元素。size 可通过如下进行计算:

forward_list c;
auto size = std::distance(c.begin(), c.end());

4. forward_list 插入和移除元素,不会导致指向其他元素的pointer、reference 和 iterator 失效。

5. forward_list 由于自身特性,提供了一些特殊的迭代器和元素操作成员函数,下面分别展开。

二 迭代器

before_begincbefore_begin

返回指向第一个元素之前迭代器。

前面说过,例如插入需要知道前驱节点,那如果在开头插入,所以需要知道开头(begin)的前驱,也就是before_begin。

三 操作函数

其特殊的元素操作函数,是一批 xxx_after 函数。它们的处理范围也比较特殊。

1. insert_after

在容器中的指定位置后插入元素。

iterator insert_after( const_iterator pos, const T& value ); (1) (C++11 起)
iterator insert_after( const_iterator pos, T&& value ); (2) (C++11 起)
iterator insert_after( const_iterator pos, size_type count, const T& value ); (3) (C++11 起)
template< class InputIt >
iterator insert_after( const_iterator pos, InputIt first, InputIt last ); (4) (C++11 起)
iterator insert_after( const_iterator pos, std::initializer_list<T> ilist ); (5) (C++11 起)

例子1: 

// 为不影响阅读逻辑,打印数据均进行注释,下同
std::forward_list<int> c1 {1,2,3,4,5,6,7};
// print("original c1:", c1);

c1.insert_after(c1.before_begin(), 10);
// print("1 insert_after c1:", c1);
c1.insert_after(c1.begin(), 3, 30);
// print("2 insert_after c1:", c1);

std::forward_list<int> temp_c {100,200,300};
c1.insert_after(c1.before_begin(), temp_c.begin(), temp_c.end());
// print("3 insert_after c1:", c1);

结果:

original c1:
1 2 3 4 5 6 7

1 insert_after c1:
10 1 2 3 4 5 6 7

2 insert_after c1:
10 30 30 30 1 2 3 4 5 6 7

3 insert_after c1:
100 200 300 10 30 30 30 1 2 3 4 5 6 7

注意:

(1)pos 必须是有效迭代器(指向有效元素) 或 before_begin。在 pos 的下一位置插入。

(2) insert_after(4) 处理的区间为 [begin, end)

2. emplace_after

在容器中的指定位置后插入新元素。原位构造元素,即不进行复制或移动操作。

template< class... Args > 
iterator emplace_after( const_iterator pos, Args&&... args ); (C++11 起)

 例子2:

class A 
{
 public:
  explicit A(int a = 0) {}
  explicit A(int a, int b) {}
};

……
std::forward_list<A> temp_c;
A a1;
temp_c.insert_after(temp_c.before_begin(), a1);
temp_c.emplace_after(temp_c.before_begin(), 1);
temp_c.emplace_front(2,2);

// std::cout << "temp_c size:" << std::distance(temp_c.begin(), temp_c.end()) << std::endl;

结果:

temp_c size:3

3. erase_after

从容器移除指定元素。

iterator erase_after( const_iterator pos ); (1) (C++11 起)
iterator erase_after( const_iterator first, const_iterator last ); (2) (C++11 起)

例子3: 

// 代码接例子1
// print("c1", c1);
auto it = std::next(c1.begin(), 9);
c1.erase_after(c1.begin(), it);
// print("1 erase_after c1:", c1);
c1.erase_after(it);
// print("2 erase_after c1:", c1);

结果:

c1:
100 200 300 10 30 30 30 1 2 3 4 5 6 7

1 erase_after c1:
100 3 4 5 6 7

2 erase_after c1:
100 3 5 6 7

注意:

(1)erase_after(1) 移除的是 pos 的下一位置元素。(it 指向元素3, 第一次 erase_after 后仍有效;第二次 erase_after 移除的是其后的元素4)

(2)erase_after(2) 处理的区间为 (begin, end) 开区间。

4. splice_after

从另一 forward_list 移动元素到 *this 。不复制元素。

void splice_after( const_iterator pos, forward_list& other ); (1) (C++11 起)
void splice_after( const_iterator pos, forward_list&& other ); (1) (C++11 起)
void splice_after( const_iterator pos, forward_list& other, 
                   const_iterator it ); (2) (C++11 起)
void splice_after( const_iterator pos, forward_list&& other,
                   const_iterator it ); (2)	(C++11 起)
void splice_after( const_iterator pos, forward_list& other, 
                   const_iterator first, const_iterator last ); (3)	(C++11 起)
void splice_after( const_iterator pos, forward_list&& other, 
                   const_iterator first, const_iterator last ); (3)	(C++11 起)

例子4:

// 代码接例子3
// print("c1:", c1);
std::forward_list<int> temp_c {1000,2000,3000};
// print("original temp_c:", temp_c);
c1.splice_after(c1.before_begin(), temp_c);
// print("splice_after temp_c:", temp_c);
// print("splice_after c1:", c1);

std::forward_list<int> temp1_c {7000,8000,9000};
// print("original temp1_c:", temp1_c);
c1.splice_after(c1.before_begin(), temp1_c, temp1_c.begin());
// print("splice_after temp1_c:", temp1_c);
// print("splice_after c1:", c1);

c1.splice_after(c1.before_begin(), temp1_c, temp1_c.begin(), temp1_c.end());
// print("splice_after temp1_c:", temp1_c);
// print("splice_after c1:", c1);

结果:

c1:
100 3 5 6 7

original temp_c:
1000 2000 3000

splice_after temp_c:

splice_after c1:
1000 2000 3000 100 3 5 6 7

// -----------------------------

original temp1_c:
7000 8000 9000

splice_after temp1_c:
7000 9000

splice_after c1:
8000 1000 2000 3000 100 3 5 6 7

// -----------------------------

splice_after temp1_c:
7000

splice_after c1:
9000 8000 1000 2000 3000 100 3 5 6 7

注意:

(1)pos 必须是有效迭代器(指向有效元素) 或 before_begin。

(2)splice_after 是 move操作。(第一次操作后,temp_c 空了。)

(3)splice_after(2) move 的是 it 指向元素的下一元素。(第二次操作,move 的是 8000 而不是 7000。)

(4)splice_after(3) 处理的区间为 (begin, end) 开区间。(第三次操作,move 的是 9000 而不是 7000、9000。)

四 参考

forward_list

### C++ `forward_list` 使用指南与相关问题解决方案 C++ 标准库中的 `std::forward_list` 是一种单向链表结构,属于序列容器。它支持高效的插入和删除操作,并且在内存使用上比双向链表(如 `std::list`)更节省空间。然而,由于是单向链表,`forward_list` 不支持反向遍历。 #### 基本特性 - **单向性**:只能从前向后遍历。 - **动态大小**:可以在运行时根据需要调整大小。 - **插入/删除效率高**:在已知位置插入或删除元素的时间复杂度为 O(1)。 - **不支持随机访问迭代器**:只能通过前向迭代器访问元素。 ```cpp #include <iostream> #include <forward_list> int main() { std::forward_list<int> myForwardList = {10, 20, 80, 70, 30}; std::cout << "元素列表:"; for (auto &num : myForwardList) { std::cout << num << " "; } std::cout << std::endl; std::cout << "myForwardList.front() = " << myForwardList.front() << std::endl; return 0; } ``` #### 构造函数 `forward_list` 提供了多种构造方式以满足不同需求: - 默认构造一个空的链表。 - 指定数量的默认初始化元素。 - 指定数量并用特定值初始化所有元素。 - 使用初始化列表构造。 - 拷贝另一个 `forward_list` 的内容。 - 从指定范围内的元素构造新链表。 ```cpp std::forward_list<int> fl; // 空链表 std::forward_list<int> fl2(10); // 包含10个0的链表 std::forward_list<int> fl3(5, 30); // 包含5个30的链表 std::forward_list<int> fl4 = {1, 2, 3, 4}; // 初始化列表构造 std::forward_list<int> fl6(fl); // 拷贝构造 std::forward_list<int> fl7(fl.begin(), fl.end()); // 范围构造 ``` #### 迭代器操作 `forward_list` 支持以下迭代器方法来访问元素: - `before_begin()` / `cbefore_begin()` 返回指向第一个元素之前的位置的迭代器。 - `begin()` / `cbegin()` 返回指向容器第一个元素的迭代器。 - `end()` / `cend()` 返回指向容器尾端的迭代器。 这些迭代器可以用于遍历、修改以及执行其他基于范围的操作。 #### 插入与删除操作 `forward_list` 提供了丰富的成员函数来进行插入和删除操作: - `insert_after(pos, value)` 在指定位置之后插入一个元素。 - `emplace_after(pos, args...)` 直接构造一个新的元素于指定位置之后。 - `erase_after(pos)` 删除指定位置之后的一个元素。 - `remove(value)` 移除所有等于给定值的元素。 - `clear()` 清空整个链表。 ```cpp std::forward_list<int> flist = {1, 2, 3}; flist.insert_after(flist.before_begin(), 0); // 插入到最前面 flist.emplace_after(flist.begin(), 5); // 在首元素后插入5 flist.erase_after(flist.begin()); // 删除刚刚插入的5 flist.remove(2); // 移除值为2的元素 flist.clear(); // 清空链表 ``` #### 内存管理与性能优化 相比 `std::list`,`forward_list` 更加轻量级,因为它仅维护每个节点的下一个指针,而不保存前一个指针。这使得其占用的内存更少,适合对内存敏感的应用场景[^3]。 此外,在频繁进行插入和删除操作的情况下,`forward_list` 可能会提供更好的性能表现。但需要注意的是,由于缺乏反向遍历能力,某些算法可能无法直接应用于 `forward_list` 上。 #### 自定义比较逻辑 虽然 `forward_list` 主要用于存储基本类型的数据,但它也支持自定义类型的存储。为了确保正确的行为,用户应该为自定义类型重载必要的运算符或者提供自定义的比较函数对象。 #### 总结 `std::forward_list` 是一种高效的单向链表结构,适用于那些需要高效插入和删除操作且不需要反向遍历的应用场景。理解其特性和限制有助于开发者做出合适的选择,并充分利用其优势。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值