突破编程_C++_C++11新特性(forward_list)

1 std::forward_list 的概述

1.1 什么是 std::forward_list?

std::forward_list 是 C++ 标准模板库(STL)中的一个容器,它表示一个单向链表。相比于 std::list,std::forward_list 在存储和操作上更加简洁,从而减小了空间开销。单向链表意味着它只支持从前往后的遍历,即只有前向迭代器,而不支持从后往前的遍历。

std::forward_list 的基本特性如下:

  • 单向性:forward_list 中的迭代器只支持向前移动,不支持向后移动,因此不能从尾部向前遍历链表。
  • 插入和删除操作:由于forward_list 是链表结构,因此在链表头部插入和删除元素的时间复杂度是常数。但需要注意的是,由于它只支持前向迭代,所以插入和删除操作在链表的其他位置可能会相对复杂一些。
  • 空间效率:相比于 std::list(双向链表),std::forward_list 节省了用于存储指向下一个和上一个节点的指针的空间,因此其空间开销较小。

使用 std::forward_list 时,需要包含头文件<forward_list>。下面是一个简单的使用示例:

#include <iostream>  
#include <forward_list>  int main() 
{  // 创建一个空的forward_list  std::forward_list<int> flst;  // 向forward_list的头部插入元素  flst.push_front(1);  flst.push_front(2);  flst.push_front(3);  // 移除forward_list的一个元素  flst.pop_front(); // 遍历forward_list并打印元素  for (auto it = flst.begin(); it != flst.end(); ++it) {  std::cout << *it << " ";  }  std::cout << std::endl;  return 0;  
}

上面代码的输出为:

2 1

在访问 forward_list 的第一个元素时,也可以使用 before_begin() 方法,这个方法返回的是第一个元素的前一个元素(也就是一个虚设的元素),不能直接解引用。但是,可以通过加 1 操作( std::advance(it, 1) )来访问第一个元素。

总体而言,std::forward_list 是一个单向链表容器,适用于那些只需要单向遍历数据结构的场景,它可以有效地节省空间,并提供常数时间的头部插入和删除操作。

1.2 std::forward_list 的使用场景

以下是std::forward_list的主要使用场景:

(1)内存空间有限: 当内存使用非常紧张,且每个字节都显得宝贵时,std::forward_list 是一个很好的选择。由于它只存储单个指针,相比双向链表(如 std::list)或动态数组(如 std::vector),它占用的空间更少。因此,在需要节省内存的场景中,std::forward_list 是理想的容器。

(2)频繁在头部插入或删除元素: 如果你的应用场景需要频繁在列表的头部插入或删除元素,std::forward_list 是一个很好的选择。由于单向链表的结构特性,它在头部插入或删除元素的时间复杂度几乎是常数时间,不依赖于容器的大小。因此,对于需要频繁进行此类操作的场景,std::forward_list 比其他类型的容器更加高效。

(3)不需要双向遍历: 如果应用场景不需要双向遍历元素,那么 std::forward_list 比 std::list 更加高效。单向链表只支持从前往后的遍历,这意味着它不需要存储额外的指针来实现双向遍历,从而节省了空间。在只需要单向遍历的场景中,使用 std::forward_list 可以避免不必要的空间开销。

2 声明与初始化

2.1 声明

首先,需要包含 <forward_list> 头文件,然后声明一个 std::forward_list 变量,并指定类型。

#include <forward_list>  std::forward_list<Type> listName;

其中 Type 是元素的类型,listName 是 unordered_set 变量的名称。

2.2 初始化

std::forward_list 可以通过多种方式初始化。以下是一些常见的初始化方法:

(1)默认初始化

创建一个空的 forward_list:

std::forward_list<int> emptyList;

(2)使用列表初始化
使用花括号 {} 初始化 forward_list,其中可以包含多个元素:

std::forward_list<int> list = {1, 2, 3, 4, 5};

(3)使用迭代器范围初始化

如果有一个已存在的容器或数组,并希望使用它的元素来初始化 forward_list,可以使用迭代器范围:

std::vector<int> vec = {1, 2, 3, 4, 5};  
std::forward_list<int> list(vec.begin(), vec.end());

(4)使用 assign 方法初始化

可以先创建一个空的 forward_list,然后使用 assign 方法分配一组值给它:

std::forward_list<int> list;  
list.assign({10, 20, 30, 40, 50}); // 分配一组值给 list

(5)使用拷贝构造函数或赋值运算符

可以从一个已存在的 forward_list 创建另一个 forward_list,或者使用赋值运算符将一个 forward_list 的内容复制到另一个:

std::forward_list<int> list1 = {1, 2, 3, 4, 5}; 
std::forward_list<int> list2(list1);   // 使用拷贝构造函数 
std::forward_list<int> list3 = list1; // 使用赋值运算符

3 添加元素

3.1 使用 push_front 方法

push_front 是 std::forward_list 中最常用的添加元素的方法,它将元素添加到链表的头部:

std::forward_list<int> myList;  
myList.push_front(1);  
myList.push_front(2);  
myList.push_front(3);// myList : 3 2 1

3.2 使用 emplace_front 方法

emplace_front 类似于 push_front,但它直接在容器中构造元素,避免了拷贝或移动操作,提高了效率:

std::forward_list<std::string> myStringList;  
myStringList.emplace_front("Hello");  
myStringList.emplace_front("World");// myList : World Hello

3.3 使用 insert_after 方法

虽然 std::forward_list 主要用于在头部添加元素,但 insert_after 方法允许在指定的迭代器位置之后插入新元素。这通常用于在链表的特定位置插入元素:

std::forward_list<int> myList = {1, 2, 4};  
auto it = myList.before_begin(); // 获取头部之前的迭代器  
std::advance(it, 1); // 前进到第二个元素之前  
myList.insert_after(it, 3); // 在第二个元素之后插入3// myList : 1 3 2 4 

3.4 使用 emplace_after 方法

与 insert_after 类似,但 emplace_after 直接在容器中构造元素(避免了拷贝或移动操作,效率更高):

std::forward_list<std::string> myStringList = {"apple", "banana"};  
auto it = myStringList.before_begin();  
std::advance(it, 1);  
myStringList.emplace_after(it, "orange"); // 在"banana"之后构造并插入"orange"// myList : apple orange banana

4 访问元素

4.1 迭代器遍历

迭代器是访问 std::forward_list 中元素的主要方式。由于 forward_list 是单向的,所以只能使用前向迭代器从头部开始向前遍历整个链表。

(1)基本迭代

std::forward_list<int> myList = {1, 2, 3, 4, 5};  
for (auto it = myList.begin(); it != myList.end(); ++it) {  std::cout << *it << ' '; // 访问当前元素的值  
}

(2)常量迭代器

如果只需要读取元素而不需要修改它们时,可以使用常量迭代器。

for (auto it = myList.cbegin(); it != myList.cend(); ++it) {  std::cout << *it << ' '; // 读取当前元素的值  
}

(3)基于的for循环

C++11 引入了范围基于的for循环,它使得遍历容器更加简洁。

for (const auto& element : myList) {  std::cout << element << ' '; // 读取当前元素的值  
}

4.2 访问第一个元素

由于 forward_list 是单向的,所以可以直接访问其第一个元素,但不能直接访问其他位置的元素。

if (!myList.empty()) {  std::cout << "First element: " << myList.front() << std::endl;  
}

4.3 使用算法库中的算法

C++ 标准库中的算法可以与容器一起使用,以执行各种操作,包括访问元素。

(1)使用 std::for_each

std::for_each(myList.begin(), myList.end(), [](int value) {  std::cout << value << ' '; // 访问并打印当前元素的值  
});

(2)使用 std::copy 将元素复制到另一个容器或输出流

std::vector<int> myList = { 1, 2, 3, 4, 5 };
std::vector<int> vec;  
std::copy(myList.begin(), myList.end(), std::back_inserter(vec));  
// 现在 vec 包含了 myList 的所有元素

5 删除元素

5.1 使用 erase_after 方法删除元素

std::forward_list 提供了 erase_after 方法来删除元素。该方法删除传入迭代器的下一个元素。

(1)删除单个元素

#include <iostream>  
#include <forward_list>  int main() {std::forward_list<int> myList = { 1, 2, 3, 4, 5 };auto it = std::find(myList.begin(), myList.end(), 3); // 查找值为3的元素  if (it != myList.end()) {it = myList.erase_after(it); // 删除下一个元素(4)并返回下下个元素(5)的迭代器  }return 0;
}

注意:在调用 erase_after 后,返回的迭代器指向被删除元素之后的元素。如果这是链表的最后一个元素,返回的迭代器将是 end()。

(2)删除一系列元素

虽然 forward_list 是单向的,但可以通过传入两个迭代器来删除一系列元素。

#include <iostream>  
#include <forward_list>  int main() {std::forward_list<int> myList = { 1, 2, 3, 4, 5 };auto it = std::find(myList.begin(), myList.end(), 2); // 查找起始删除位置  if (it != myList.end()) {auto endIt = std::find(it, myList.end(), 5); // 查找结束删除位置(不包括此元素)  if (endIt != myList.end()) {it = myList.erase_after(it, endIt); // 删除一系列元素:3 4}}return 0;
}

5.2 使用 remove 和 remove_if 方法删除元素

除了 erase,forward_list 还提供了 remove 和 remove_if 方法来删除满足特定条件的元素。

(1)使用 remove 删除特定值的元素

#include <iostream>  
#include <forward_list>  
#include <algorithm>int main() {std::forward_list<int> myList = { 1, 2, 3, 3, 4, 5 };myList.remove(3); // 删除所有值为3的元素return 0;
}

(2)使用 remove_if 删除满足条件的元素

#include <iostream>  
#include <forward_list>  
#include <algorithm>int main() {std::forward_list<int> myList = { 1, 2, 3, 4, 5, 6 };myList.remove_if([](int n) { return n % 2 == 0; }); // 删除所有偶数return 0;
}

remove_if 方法接受一个谓词(即返回布尔值的函数或可调用对象),并删除所有使谓词返回 true 的元素。

5.3 使用 partition 和 stable_partition 删除元素

std::partition 或 std::stable_partition 可以根据谓词将元素分为两组,但不保证组内元素的相对顺序。之后,就可以使用 erase 方法删除不需要的那一组元素。

#include <iostream>  
#include <forward_list>  
#include <algorithm>int main() {std::forward_list<int> myList = { 1, 2, 3, 4, 5 };auto it = std::partition(myList.begin(), myList.end(), [](int n) { return n < 3; });myList.erase_after(it, myList.end()); // 删除所有不小于3的元素return 0;
}

在这个例子中,std::partition 将所有小于 3 的元素移动到链表的前面,然后就可以使用 erase_after 删除剩余的元素。

5.4 遍历删除

一个安全的方法是使用迭代器遍历 std::forward_list,并在删除元素后更新迭代器。

#include <iostream>  
#include <forward_list>  int main() {std::forward_list<int> myList = { 1, 2, 3, 4, 5 };auto itPre = myList.before_begin();size_t offset = 0;for (auto it = myList.begin(); it != myList.end(); ) {// 检查是否需要删除当前元素  if (*it % 2 == 0) {it = myList.erase_after(itPre);itPre = myList.before_begin();std::advance(itPre, offset);}else {// 否则,继续到下一个元素  ++it;++itPre;++offset;}}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/756759.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

k8s详细教程

Kubernetes详细教程 1. Kubernetes介绍 1.1 应用部署方式演变 在部署应用程序的方式上&#xff0c;主要经历了三个时代&#xff1a; 传统部署&#xff1a;互联网早期&#xff0c;会直接将应用程序部署在物理机上 优点&#xff1a;简单&#xff0c;不需要其它技术的参与 缺点…

JavaScript高级(十八)---进程和线程,宏任务和微任务

进程和线程 进程&#xff08;process&#xff09;&#xff1a;计算机已经运行的程序&#xff0c;是操作系统管理程序的一种方式&#xff0c;我们可以认为&#xff0c;启动一个应用程序&#xff0c;就会默认启动一个进程&#xff08;也可能是多个进程&#xff09;。 线程&…

行业模板|DataEase制造行业大屏模板推荐

DataEase开源数据可视化分析平台于2022年6月发布模板市场&#xff08;https://templates-de.fit2cloud.com&#xff09;&#xff0c;并于2024年1月新增适用于DataEase v2版本的模板分类。模板市场旨在为DataEase用户提供专业、美观、拿来即用的大屏模板&#xff0c;方便用户根据…

智能合约 之 ERC-721

ERC-721&#xff08;Non-Fungible Token&#xff0c;NFT&#xff09;标准 ERC-721是以太坊区块链上的一种代币标准&#xff0c;它定义了一种非同质化代币&#xff08;Non-Fungible Token&#xff0c;NFT&#xff09;的标准。NFT是一种加密数字资产&#xff0c;每个代币都具有独…

【计算机网络_网络层】IP协议

文章目录 1. IP的基本概念1.1 什么是IP协议1.2 为什么要有IP协议 2. IP的协议格式3. 网段划分&#xff08;重要&#xff09;3.1 为什么要进行网段划分3.2 网段划分的规则3.2.1 古老的划分方案3.2.2 现代的划分方案 4. 特殊的IP地址5. 解决IP地址的数量限制问题6. 私有IP和公网I…

深入浅出Reactor和Proactor模式

Reactor模式和Proactor模式是两种常见的设计模式&#xff0c;用于处理事件驱动的并发编程。它们在处理IO操作时有着不同的工作方式和特点。 对于到来的IO事件&#xff08;或是其他的信号/定时事件&#xff09;&#xff0c;又有两种事件处理模式&#xff1a; Reactor模式&…

HarmonyOS NEXT应用开发之元素超出List区域

介绍 本示例介绍在List组件内实现子组件超出容器边缘的布局样式的实现方法。 List组件clip属性默认为true&#xff0c;超出容器边缘的子组件会按照List的布局范围被裁剪。为此&#xff0c;可以在List组件内部添加一个占位的ListItem&#xff0c;以达到预期的布局效果。List占…

每日一题-spring中的设计模式

1、 spring中的设计模式 工厂模式&#xff08;Factory Pattern&#xff09;&#xff1a;Spring使用工厂模式来创建和管理对象。通过ApplicationContext&#xff0c;Spring可以作为一个工厂&#xff0c;负责创建、初始化和返回应用程序所需的对象。 单例模式&#xff08;Singlet…

【vscode 常用扩展插件】

vscode 常用扩展插件 常用插件部分插件使用技巧1、eslint 保存自动格式化2、代码片段的使用3、最后是关于引入文件路径提示的 常用插件 记录vscode方便开发的扩展插件&#xff0c;方便换电脑时&#xff0c;快速部署所需环境。 部分插件 1、Auto Close Tag html自动闭合标签插…

React——class组件中setState修改state

class组件中通过state去存储当前组件的数据&#xff0c;那怎么对其进行修改呢&#xff1f;就是方法this.setState({ 要修改的部分数据 }) setState() 作用&#xff1a;1 、修改 state 内容&#xff1b;2 、更新 UI 特别注意&#xff1a;react的核心其实是虚拟dom&#xff08;数…

WebSocket 和SSE的区别以及优缺点

WebSocket 和 Server-Sent Events&#xff08;SSE&#xff09;都是用于实现服务器向客户端推送消息的技术&#xff0c;但它们有一些重要的区别&#xff1a; 1.双向 vs 单向&#xff1a;WebSocket 是全双工的&#xff0c;这意味着服务器和客户端可以同时发送和接收消息。而 SSE …

【项目实践Day06】异步请求与同步请求+Ajax+微信小程序上实现发送异步请求

什么是同步和异步 同步 在主线程上排队执行的任务&#xff0c;只有前一个任务执行完毕&#xff0c;才能继续执行下一个任务。也就是一旦调用开始&#xff0c;就必须等待其返回结果&#xff0c;程序的执行顺序和任务排列顺序一致。客户端必须等待服务器端的响应。在等待的期间客…

HTML静态网页成品作业(HTML+CSS)——宠物狗店网页(1个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有1个页面。 二、作品演示 三、代…

el-date-picker时间禁用问题

// 选择今天以及今天以后的日期 export const disabledDate (time) > {return time.getTime() > Date.now() - 8.64e6; //如果没有后面的-8.64e6就是不可以选择今天的 }设置开始时间小于结束时间&#xff08;不能等于&#xff09; export const disabledDate (date) …

NVIDIA NIM 提供优化的推理微服务以大规模部署 AI 模型

NVIDIA NIM 提供优化的推理微服务以大规模部署 AI 模型 生成式人工智能的采用率显着上升。 在 2022 年 OpenAI ChatGPT 推出的推动下&#xff0c;这项新技术在几个月内就积累了超过 1 亿用户&#xff0c;并推动了几乎所有行业的开发活动激增。 到 2023 年&#xff0c;开发人员…

Covalent Network(CQT)借助最大规模的历史与实时 Web3 数据集,推动人工智能的发展

人工智能在众多领域中增强了区块链的实用性&#xff0c;反之亦然&#xff0c;区块链确保了 AI 模型所使用的数据的来源和质量。人工智能带来的生产力提升&#xff0c;将与区块链系统固有的安全性和透明度融合。 Covalent Network&#xff08;CQT&#xff09;正位于这两项互补技…

设计模式(结构型设计模式——代理模式)

设计模式&#xff08;结构型设计模式——代理模式&#xff09; 代理模式 基本定义 代理模式就是给一个对象提供一个代理&#xff0c;并由代理对象控制对原对象的引用。在代理模式中&#xff0c;“第三者”代理主要是起到一个中介的作用&#xff0c;它连接客户端和目标对象。 …

HarmonyOS NEXT应用开发之Navigation实现多设备适配案例

介绍 在应用开发时&#xff0c;一个应用需要适配多终端的设备&#xff0c;使用Navigation的mode属性来实现一套代码&#xff0c;多终端适配。 效果图预览 使用说明 将程序运行在折叠屏手机或者平板上观看适配效果。 实现思路 本例涉及的关键特性和实现方案如下&#xff1a…

backtrader回测股票:突破20日均线买入,跌破20日均线卖出

数据源&#xff1a;akshare 回测工具&#xff1a;backtrader 策略&#xff1a;突破20日均线买入&#xff0c;跌破20日均线卖出 代码&#xff1a; from datetime import datetime import backtrader as bt #1.9.78.123 import matplotlib.pyplot as plt #3.8.3 import aks…

单片机-点亮LED灯

[2-1] 点亮一个LED_哔哩哔哩_bilibili main()程序执行结束后&#xff0c;单片机会再次执行main()。 不断执行P20xFE;&#xff08;点亮LED灯1&#xff09; #include "reg52.h"void main() {P20xFE; //1111 1110 } 只执行一次P20xFE&#xff1b; #include "r…