当前位置: 首页 > news >正文

突破编程_C++_STL教程( queue 的基础知识)

1 std::queue 概述

std::queue 是 C++ 标准模板库(STL)中的一种容器适配器,它提供了队列(Queue)这种数据结构的功能。队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。因此,队列具有先进先出(FIFO)的特性。

1.1 std::queue 的内部实现

std::queue 的内部实现通常基于其他容器,如 std::deque(双端队列)或 std::list。这种实现方式使得 std::queue 能够提供队列(Queue)这种数据结构的功能,即只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,从而实现先进先出(FIFO)的特性。

(1)基于 std::deque 的实现:

当 std::queue 基于 std::deque 实现时,其内部存储机制利用了 deque 的双向操作特性。deque 允许在其头部和尾部都进行高效的插入和删除操作,这使得它非常适合作为队列的底层容器。
在这种实现中,std::queue 的 push 操作会将元素添加到 deque 的尾部,而 pop 操作会从 deque 的头部删除元素。front 和 back 操作则分别返回 deque 的头部和尾部元素。
由于 deque 提供了直接访问其头部和尾部的接口,因此这种实现方式通常具有较高的性能。

(2)基于 std::list 的实现:

当 std::queue 基于 std::list 实现时,其内部存储机制利用了 list 的双向链表结构。虽然 list 在任意位置进行插入和删除操作都较为高效,但作为队列使用时,主要的操作还是集中在头部和尾部。
在这种实现中,std::queue 的 push 操作会将元素添加到 list 的尾部,而 pop 操作会从 list 的头部删除元素。与基于 deque 的实现类似,front 和 back 操作分别返回 list 的头部和尾部元素。
然而,需要注意的是,虽然 list 允许在任意位置进行插入和删除操作,但在作为队列使用时,其性能可能不如基于 deque 的实现,因为 deque 在头部和尾部的操作通常更加高效。

注意:没有明确指定 std::queue 的底层容器类型时,它默认使用 std::deque

1.2 std::queue 的性能特点

std::queue 的性能特点主要源于其内部使用的容器类型,通常是 std::deque 或 std::list。

(1)基于std::deque的性能特点:

当 std::queue 基于 std::deque 实现时,它通常能够展现出非常优秀的性能。这是因为 std::deque 是一个双向队列,它允许在两端进行高效的插入和删除操作。因此,在 std::queue 中,使用 push 在尾部插入元素和使用 pop 在头部删除元素的操作通常都非常快。

此外,std::deque 通常是以固定大小的块来存储元素,这种存储方式减少了内存分配和释放的次数,从而提高了性能。这也使得 std::queue 在处理大量元素时能够保持稳定的性能。

(2)基于std::list的性能特点:

虽然 std::queue 也可以基于 std::list 实现,但相比基于 std::deque 的实现,其性能可能稍逊一筹。std::list 是一个双向链表,虽然它在任意位置插入和删除元素都比较高效,但相对于 std::deque,它在头部和尾部进行操作的性能可能稍差。

具体来说,std::list 中的元素是分散在内存中的,这可能导致缓存不命中(cache misses)的问题,从而降低性能。此外,由于链表需要维护指针或迭代器来跟踪元素的位置,这也可能增加一些额外的开销。

(3)时间复杂度:

对于 std::queue 的基本操作,如 push、pop、front 和 back,其时间复杂度通常都是常数时间 O(1)。这意味着无论队列中有多少元素,这些操作所需的时间都是固定的。

然而,需要注意的是,虽然这些基本操作的时间复杂度是常数,但在实际应用中,性能还可能受到其他因素的影响,如内存分配、缓存行为以及并发访问等。

(4)实际应用中的考虑:

在选择使用 std::queue 时,通常不需要过多关心其底层实现和性能特点。因为标准库已经提供了优化过的实现,并且在大多数情况下都能够满足性能需求。然而,在一些对性能要求非常高的场景下,可能需要考虑使用更底层的容器或自定义数据结构来替代 std::queue。

2 std::queue 的基本使用

2.1 std::queue 的声明与初始化

声明

首先,需要包含<queue>头文件以使用 std::queue:

#include <queue>  
#include <string>  // 声明一个整数类型的 queue  
std::queue<int> vals;// 声明一个字符串类型 queue  
std::queue<std::string> strs;// 声明一个自定义类型的 queue  
struct MyStruct
{int id;std::string name;
};std::queue<MyStruct> myStructs;

初始化

可以使用多种方法来初始化 std::queue。

(1)默认初始化:

如果不提供任何参数,std::queue 会使用默认构造函数进行初始化。这意味着它会使用其底层容器(默认为 std::deque)的默认构造函数。

std::queue<int> q;

(2)使用 std::deque 进行初始化:

虽然 std::deque 不支持初始化列表,但可以使用以初始化列表初始化的 std::deque<int> 来进行初始化。

std::queue<int> q(std::deque<int>{1, 2, 3, 4, 5});  // 使用 std::deque<int> 初始化队列 q

(3)复制另一个队列:

可以使用另一个 std::queue 的副本来初始化一个新的队列。

std::queue<int> q1(std::deque<int>{1, 2, 3, 4, 5});  
std::queue<int> q2(q1);  // 使用q1的内容初始化q2

(4)移动另一个队列:

C++11 及更高版本还支持移动语义,这意味着可以转移另一个队列的内容来初始化新的队列,而不需要复制元素。

std::queue<int> q1 = {1, 2, 3, 4, 5};  
std::queue<int> q2(std::move(q1));  // 使用 q1 的内容(通过移动)初始化 q2,q1 现在为空

(5)指定底层容器:

虽然不常见,但可以通过指定底层容器来初始化 std::queue。这要求提供一个容器对象,该对象将用作队列的底层存储。

std::list<int> l = {1, 2, 3, 4, 5};  
std::queue<int, std::list<int>> q(l);  // 使用 list l 作为底层容器初始化队列 q

2.2 std::queue 的大小与容量

(1)大小(size)

std::queue 的大小是指队列中当前存储的元素数量。可以使用 std::queue 的 size 成员函数来获取队列的大小。例如:

std::queue<int> q;  
q.push(1);  
q.push(2);  
q.push(3);  std::size_t size = q.size(); // size 现在是 3,因为队列中有 3 个元素

这个例子向队列中添加了三个元素,并使用 size 成员函数获取队列的大小。

(2)容量(capacity)

与 std::vector 或 std::deque 不同,std::queue 没有直接提供获取其“容量”的成员函数。容量通常指的是容器在不进行内存重新分配的情况下可以容纳的元素数量。由于 std::queue 的设计是为了提供队列操作的接口,并且隐藏了其底层容器的实现细节,因此它并不直接暴露底层容器的容量信息。

如果需要了解底层容器的容量信息,可能需要直接操作底层容器,但这通常不是使用 std::queue 的推荐做法,因为它违反了队列的抽象和封装原则。

2.3 std::queue 的构造函数与析构函数

(1)构造函数

std::queue 提供了多个构造函数,以便在不同的情况下灵活地初始化队列。以下是一些主要的构造函数:

默认构造函数:

std::queue<Type> q;

此构造函数创建一个空的队列,其底层容器使用默认构造函数进行初始化。这里的 Type 是队列中元素的类型。

拷贝构造函数:

std::queue<Type> q1(q2);

此构造函数使用另一个队列 q2 的内容来初始化新的队列 q1。它复制 q2 中的所有元素到 q1 中。

移动构造函数(C++11 及更高版本):

std::queue<Type> q1(std::move(q2));

此构造函数通过移动另一个队列 q2 的内容来初始化新的队列 q1。这意味着 q2 在移动操作后不再包含其原始元素,这些元素的所有权现在属于 q1。使用移动构造函数通常比使用拷贝构造函数更高效,因为它可以避免不必要的元素复制。

(2)析构函数

当 std::queue 对象的生命周期结束时,其析构函数会被自动调用。析构函数负责清理队列所占用的资源,包括释放底层容器的内存。注意不需要显式地调用析构函数,因为 C++ 的自动存储期管理会处理这些细节。

例如:

{  std::queue<int> q;  // ... 在这里使用队列 ...  
} // 在这里,当 q 离开其作用域时,其析构函数会自动被调用

在上面的代码中,当 q 离开其作用域时,其析构函数会被自动调用,从而释放队列所占用的资源。

3 std::queue 的元素操作

3.1 入队列操作(push)

入队列操作使用 push 成员函数,它接受一个参数,即要添加到队列顶的元素。例如:

std::queue<int> q;  
q.push(1); // 将整数 1 压入队列中  
q.push(2); // 将整数 2 压入队列中

这个例子创建了一个 int 类型的队列 q,并使用 push 函数将两个整数依次压入队列中。

3.2 出队列操作(pop)

出队列操作使用 pop 成员函数,它移除队列顶的元素,但不返回该元素的值。例如:

std::queue<int> q;  
q.push(1);  
q.push(2);  
q.pop(); // 移除队列顶元素 1,但不返回它

这个例子创建了一个队列 q 并压入两个整数。然后,使用 pop 函数移除了队列头部的元素 1。

3.3 查看队列头部元素(front)

查看队列头部元素使用 front 成员函数,它返回队列头部元素的引用,但不移除该元素。例如:

std::queue<int> q;  
q.push(1);  
q.push(2);  
int frontElement = q.front(); // 获取队列头部元素,此时 frontElement 的值为 1

这个例子创建了一个队列 q 并压入两个整数。然后,使用 front 函数获取了队列头部的元素,并将其值存储在 frontElement 变量中。

3.4 查看队列尾部元素(back)

查看队列尾部元素使用 back 成员函数,它返回队列尾部元素的引用,但不移除该元素。例如:

std::queue<int> q;  
q.push(1);  
q.push(2);  
int backElement = q.back(); // 获取队列尾部元素,此时 backElement 的值为 2

这个例子创建了一个队列 q 并压入两个整数。然后,使用 back 函数获取了队列尾部的元素,并将其值存储在 backElement 变量中。

3.5 检查队列是否为空(empty)

检查队列是否为空使用 empty 成员函数,如果队列为空,则返回 true;否则返回 false。例如:

std::queue<int> q;  
bool isEmpty = q.empty(); // isEmpty 的值为 true,因为队列是空的  
q.push(1);  
isEmpty = q.empty(); // isEmpty 的值为 false,因为队列不再为空

这个例子首先创建了一个空的队列 q,并使用 empty 函数检查其是否为空。然后,压入一个整数并再次检查队列是否为空。

3.6 队列的交换(swap)

可以使用 swap 成员函数来交换两个队列的内容。例如:

std::queue<int> q1, q2;  
q1.push(1);  
q1.push(2);  
q2.push(3);  
q2.push(4);  q1.swap(q2); // 交换 q1 和 q2 的内容

在这个例子中,q1 原本包含元素 1 和 2,q2 包含元素 3 和 4。调用 swap 后,q1 将包含元素 3 和 4,而 q2 将包含元素 1 和 2。

3.6 底层容器的访问

虽然直接访问 std::queue 的底层容器通常是不推荐的(因为它破坏了队列的封装性),但 STL 仍然提供了某种程度的访问能力。可以使用 _Get_container 成员函数来获取底层容器的引用。注意:应该非常小心地使用这个功能,并只在确实需要时才使用它。

std::queue<int> q;  
auto& underlyingDeque = q._Get_container(); // 获取底层 deque 的引用(注意:这通常不是好的做法)

4 std::queue 的删除操作

std::queue 是一个后进先出(FIFO)的数据结构,其设计初衷是提供基本的队列操作,如 push(压入元素)、pop(弹出元素)、top(查看队列顶元素)等。然而,std::queue 并没有直接提供删除队列中特定元素的操作,这是因为它保持了队列的简单性和一致性。

如果需要删除队列中的特定元素,那么可能需要考虑其他的数据结构,如 std::deque 或 std::list,它们提供了更多的元素操作功能。但如果仍然想要使用 std::queue 并删除其中的元素,那么可以通过以下方式间接实现:

(1)弹出元素直到找到并删除目标元素:

可以通过连续调用 pop 函数,直到找到并删除目标元素。但是,这种方法会破坏队列的结构,因为它会移除队列顶的所有元素,直到找到目标元素为止。这通常不是推荐的做法,因为它违反了队列的 FIFO 原则。

std::queue<int> q;
q.push(1);
q.push(2);
q.push(3);
q.push(4);std::queue<int> qTmp;int target = 3;
bool found = false;
while (!q.empty()) {int front = q.front();q.pop();if (front != target) {qTmp.push(front); // 将非目标元素重新压入队列中  }
}q.swap(qTmp);

这个例子试图删除值为 3 的元素。通过循环不断地从队列顶弹出元素,检查它是否是想要删除的目标,如果不是,则将其重新压入备用队列中。这种方法效率很低,特别是当队列很大且目标元素靠近队列底时。

(2)使用其他数据结构辅助:

另一种方法是使用一个辅助的数据结构(如 std::vector 或 std::deque)来存储队列中的元素,然后在这个辅助数据结构中删除目标元素,最后再将辅助数据结构中的元素重新压入队列中。这种方法同样会破坏队列的结构,并且效率也不高。

(3)避免需要删除操作:

最好的方法是避免在 std::queue 中进行删除操作。在设计程序时,尽量确保你不需要从队列中删除特定的元素。如果确实需要这种功能,那么可能需要考虑使用其他更适合的数据结构。

相关文章:

突破编程_C++_STL教程( queue 的基础知识)

1 std::queue 概述 std::queue 是 C 标准模板库&#xff08;STL&#xff09;中的一种容器适配器&#xff0c;它提供了队列&#xff08;Queue&#xff09;这种数据结构的功能。队列是一种特殊的线性表&#xff0c;它只允许在表的前端&#xff08;front&#xff09;进行删除操作…...

Hystrix 容错机制数据监控可视界面无法加载

在微服务项目中创建一个Hystrix模块配置好pom和yml文件但是在加载可视化界面的时候报如下问题 第一个界面报的错误 第二个界面报错误 熔断监控图形化界面报错Unable to connect to Command Metric Stream 解决方案 在yml文件在加如下代码 代码块 hystrix:dashboard:proxy…...

【Java】POI解析excel

一、相关介绍 POI技术 Apache POI是Apache软件基金会的开放源码函式库&#xff0c;POI提供API给Java程序对Microsoft Office格式档案读和写的功能。 poi-ooxml能解析xls&#xff0c;xlsx。 poi能解析word、ppt、excel、xml等office软件 导入坐标&#xff1a; <depende…...

AI浸入社交领域,泛娱乐APP如何抓住新风口?

2023年是大模型技术蓬勃发展的一年&#xff0c;自ChatGPT以惊艳姿态亮相以来&#xff0c;同年年底多模态大模型技术在国内及全球范围内的全面爆发&#xff0c;即模型能够理解并生成包括文本、图像、视频、音频等多种类型的内容。例如&#xff0c;基于大模型的文本到图像生成工具…...

遥感图像地物分类流程

遥感图像地物分类流程 1. 制作标签 使用arcgis pro或者arcgis或者envi&#xff0c;画标签&#xff0c;保存为tiff格式 2. 处理标签数据 用python gdal库安装 osgdal库&#xff0c;如果安装失败就需要下载 对应库得 .whl去安装&#xff0c;网站具体搞忘了&#xff0c;可以百…...

JS13-事件的绑定和事件对象Event

绑定事件的两种方式/DOM事件的级别 DOM0的写法&#xff1a;onclick element.onclick function () {}举例&#xff1a; <body> <button>点我</button> <script>var btn document.getElementsByTagName("button")[0];//这种事件绑定的方式…...

pycorrector检测OCR错字实践

参考&#xff1a;https://github.com/shibing624/pycorrector/tree/master/examples/macbert stopwords.txt 添加专业停用词&#xff0c;避免错误 设置自定义词典&#xff0c;避免将正确的词错误检测成错误的词 from pycorrector import Corrector m Corrector() m.set_cus…...

RDD算子介绍(三)

1. join 将相同的key的值连接在一起&#xff0c;值的类型可以不同 val rdd1 : RDD[(String, Int)] sc.makeRDD(List(("a", 1), ("b", 2), ("c", 3))) val rdd2 : RDD[(String, Int)] sc.makeRDD(List(("a", 4), ("b", 5…...

Redis的脑裂问题

Redis 脑裂&#xff08;Split-brain&#xff09;问题是指在分布式系统中&#xff0c;特别是基于主从复制和哨兵&#xff08;Sentinel&#xff09;模式的Redis集群中&#xff0c;由于网络分区&#xff08;network partition&#xff09;而导致部分节点组成了独立可用的服务&…...

【算法】雪花算法生成分布式 ID

SueWakeup 个人中心&#xff1a;SueWakeup 系列专栏&#xff1a;学习Java框架 个性签名&#xff1a;人生乏味啊&#xff0c;我欲令之光怪陆离 本文封面由 凯楠&#x1f4f7; 友情赞助播出! 目录 1. 什么是分布式 ID 2. 分布式 ID 基本要求 3. 数据库主键自增 4. UUID 5. S…...

FFplay使用滤镜添加字幕到现有视频显示

1.创建字幕文件4k.srt 4k.srt内容: 1 00:00:01.000 --> 00:00:30.000 日照香炉生紫烟2 00:00:31.000 --> 00:00:60.000 遥看瀑布挂前川3 00:01:01.000 --> 00:01:30.000 飞流直下三千尺4 00:01:31.000 --> 00:02:00.000 疑是银河落九天2.通过使用滤镜显示字幕在视…...

【Python + Django】Django模板语法 + 请求和响应

前言&#xff1a; 现在现在&#xff0c;我们要开始将变量的值展现在页面上面啦&#xff01; 要是只会显示静态页面&#xff0c;我们的页面也太难看和死板了&#xff0c; 并且数据库的数据也没法展现在页面上。 但是呢&#xff0c;模板语法学习之后就可以啦&#xff01;&…...

大数据面试总结 四

1、当hadoop集群中某一个节点挂了&#xff0c;内部数据流程是如何进行的&#xff1f; 每一个datanode都会定期向namenode发送heardbeat消息&#xff0c;当一段时间namenode没有接收到某一个datanode的消息&#xff0c;此时namenode就会将该datanode标记为死亡&#xff0c;并不…...

Spring Boot: 使用MongoOperations操作mongodb

一、添加依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4…...

PyTorch 深度学习(GPT 重译)(六)

十四、端到端结节分析&#xff0c;以及接下来的步骤 本章内容包括 连接分割和分类模型 为新任务微调网络 将直方图和其他指标类型添加到 TensorBoard 从过拟合到泛化 在过去的几章中&#xff0c;我们已经构建了许多对我们的项目至关重要的系统。我们开始加载数据&#xf…...

MyBatis3源码深度解析(十七)MyBatis缓存(一)一级缓存和二级缓存的实现原理

文章目录 前言第六章 MyBatis缓存6.1 MyBatis缓存实现类6.2 MyBatis一级缓存实现原理6.2.1 一级缓存在查询时的使用6.2.2 一级缓存在更新时的清空 6.3 MyBatis二级缓存的实现原理6.3.1 实现的二级缓存的Executor类型6.3.2 二级缓存在查询时使用6.3.3 二级缓存在更新时清空 前言…...

Go --- Go语言垃圾处理

概念 垃圾回收&#xff08;GC-Garbage Collection&#xff09;暂停程序业务逻辑SWT&#xff08;stop the world&#xff09;程序根节点&#xff1a;程序中被直接或间接引用的对象集合&#xff0c;能通过他们找出所有可以被访问到的对象&#xff0c;所以Go程序的根节点通常包括…...

力扣每日一题30:串联所有单词的子串

题目描述 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 例如&#xff0c;如果 words ["ab","cd","ef"]&#xff0c; 那么 &q…...

vim | vim的快捷命令行

快捷进入shell界面 -> :nnoremap <F8> :sh<CR> -> 绑定到了F8 :nnoremap <F8> :sh<CR> 快捷执行 -> :nnoremap <F5> :wa<CR>:!g % -o a.out && ./a.out<CR> -> 绑定到了F5 :nnoremap <F5> :wa<CR>…...

项目管理平台-01-BugClose 入门介绍

拓展阅读 Devops-01-devops 是什么&#xff1f; Devops-02-Jpom 简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件 代码质量管理 SonarQube-01-入门介绍 项目管理平台-01-jira 入门介绍 缺陷跟踪管理系统&#xff0c;为针对缺陷管理、任务追踪和项目管理的商业…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题&#xff1a; 指定音频引擎与设备&#xff1b;播放音频文件 本文所使用的环境&#xff1a; Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

智能AI电话机器人系统的识别能力现状与发展水平

一、引言 随着人工智能技术的飞速发展&#xff0c;AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术&#xff0c;在客户服务、营销推广、信息查询等领域发挥着越来越重要…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

STM32---外部32.768K晶振(LSE)无法起振问题

晶振是否起振主要就检查两个1、晶振与MCU是否兼容&#xff1b;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容&#xff08;CL&#xff09;与匹配电容&#xff08;CL1、CL2&#xff09;的关系 2. 如何选择 CL1 和 CL…...

【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅

目录 前言 操作系统与驱动程序 是什么&#xff0c;为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中&#xff0c;我们在使用电子设备时&#xff0c;我们所输入执行的每一条指令最终大多都会作用到硬件上&#xff0c;比如下载一款软件最终会下载到硬盘上&am…...