C++进阶 map和set

news2024/11/19 8:44:30

作者:@小萌新
专栏:@C++进阶
作者简介:大二学生 希望能和大家一起进步!
本篇博客简介:简单介绍C++中map和set容器

map和set

  • 关联式容器
  • 树形结构与哈希结构
  • 键值对
  • set
    • set的介绍
    • set的定义方式
      • 方式一: 构造一个某类型的空容器
      • 方式二: 拷贝构造某类型set容器的复制品
      • 方式三: 使用迭代器拷贝构造某一段内容
      • 方式四: 构造一个某类型的空容器 比较方式指定为大于
    • set的使用
      • 展示去重和范围for遍历
      • 展示直接删除
      • 展示迭代器遍历(正反向遍历)
      • 展示查找计数交换等
      • 展示交换容器
    • multiset
  • map
    • map的介绍
    • map的定义方式
      • 方式一: 指定构造key value类型的空容器
      • 方式二: 拷贝构造某类型容器
      • 方式三: 使用迭代器拷贝构造某一段内容
      • 方式四: 指定key和value的类型构造一个空容器 key比较方式指定为大于
    • map的插入
      • 方式一: 构造匿名对象插入
      • 方式二: 使用模板插入
    • map的查找
    • map的删除
    • map的[ ]运算符重载
    • map的其他成员函数
  • multimap

关联式容器

C++STL包含了序列式容器关联式容器

序列式容器: 序列式容器里面储存的式元素本身 其底层为线性序列的数据结构 比如说:vector list deque等

关联式容器: 关联式容器里面储存的是<key , value>结构的键值对 一般在数据检索时 比序列式容器的效率更高 比如说 set map等

小Tip: C++STL中的queue和stack并不属于容器 而是容器适配器 本质是由容器封装而成的 默认封装容器是deque

树形结构与哈希结构

根据应用场景的不同 C++STL总共实现了两种不同结构的关联式容器: 树形结构和哈希结构

关联式容器容器结构底层实现
set、map、multiset、multimap树型结构平衡搜索树(红黑树)
unordered_set、unordered_map、unordered_multiset、unordered_multimap哈希结构哈希表

其中 树形结构容器中的序列是一个有序的序列

而哈希结构容器中的序列是一个无序的序列

键值对

键值对是用来表示一种具有一一对应关系的一种结构 该结构中一般只包含两个成员变量key和value
key标志键值 value表示key对应的信息

比如说我们现在要建立一个英译汉的词典

那么该词典中的英文单词和其对应的中文意思之间就是一一对应的关系

在SGI版本的STL中 对于键值对的定义如下

template<class T1, class T2>
struct pair
{
	typedef T1 first_type;
	typedef T2 second_type;
	T1 first;
	T2 second;
	pair() : first(T1()) , second(T2())
	{}
	pair(const T1& a, const T2& b) : first(a), second(b)
	{}
};

set

set的介绍

  1. set是按照一定次序来存储数据的容器 因此使用set的迭代器去遍历之 我们就能得到有序的序列
  2. set当中存储的value值是唯一的 不可以重复 因此可以使用set进行去重
  3. 与map不同的是 map的底层存放的是真正的 k v键值对 set当中只存放 value 但是其实set的底层存放的是 v v键值对 因此在向set容器里面插入元素的时候 只需要插入value值就好
  4. set中的元素不能被修改 因为set的底层是用二叉搜索树来实现的 如果我们修改了二叉搜索树某个节点的值的话 那么这棵树就不再是二叉搜索树了
  5. 在内部 set中的元素总是按照其内部比较对象所指示的特定严格弱排序来进行排序 当不传入内部比较对象时 set中的元素默认用小于来比较
  6. set容器通过key访问单个元素的效率比unordered_set慢 但是set容器允许根据顺序对元素进行直接迭代
  7. set底层是用红黑树来实现的 所以说它查找的复杂度为LogN

set的定义方式

方式一: 构造一个某类型的空容器

      set<int> s1; // 构造int类型的空容器

方式二: 拷贝构造某类型set容器的复制品

      set<int> s2(s1) // 拷贝构造与s1相同的容器

方式三: 使用迭代器拷贝构造某一段内容

	string str("hello world");
	set<char> s3(str.begin(), str.end()); // 使用string的迭代器拷贝构造

方式四: 构造一个某类型的空容器 比较方式指定为大于

	set<int, greater<int>> s4; // 构造int类型的空容器 比较大小为greater

set的使用

常用接口函数
在这里插入图片描述
迭代器相关函数

在这里插入图片描述
使用代码如下

展示去重和范围for遍历

	set<int> s1;
	s1.insert(1);
	s1.insert(3);
	s1.insert(5);
	s1.insert(3);
	s1.insert(4);
	s1.insert(9);
	s1.insert(7);
	s1.insert(4);
	for (auto x : s1)
	{
		cout << x << endl;
	}

我们使用set容器 并且插入多组重复数据 之后使用范围for遍历 达到一个去重排序的效果
在这里插入图片描述

展示直接删除

	set<int> s1;
	s1.insert(1);
	s1.insert(3);
	s1.insert(5);
	s1.insert(3);
	s1.insert(4);
	s1.insert(9);
	s1.insert(7);
	s1.insert(4);
	
	for (auto x : s1)
	{
		cout << x << " ";
	}
	cout << endl;
	s1.erase(3);
	for (auto x : s1)
	{
		cout << x << " ";
	}
	cout << endl;

我们在使用erase括号后面跟上了要删除的值 之后遍历整个set容器

在这里插入图片描述
我们可以发现 3被删除了

那么如果我们连续两次删除3会有什么发生呢?

在这里插入图片描述
我们可以发现 还是和上面一样 只是删除了3而已

展示迭代器遍历(正反向遍历)

	set<int> s1;
	s1.insert(1);
	s1.insert(3);
	s1.insert(5);
	s1.insert(3);
	s1.insert(4);
	s1.insert(9);
	s1.insert(7);
	s1.insert(4);


	set<int>::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << " ";
		it++;
	}

	cout << endl;

	set<int>::reverse_iterator it1 = s1.rbegin();
	while (it1 != s1.rend())
	{
		cout << *it1 << " ";
		it1++;
	}

我们还是采用了上面的几组数据 并且使用正向反向迭代器来遍历数据 结果如下

在这里插入图片描述

展示查找计数交换等

	set<int> s1;
	s1.insert(1);
	s1.insert(3);
	s1.insert(5);
	s1.insert(3);
	s1.insert(4);
	s1.insert(9);
	s1.insert(7);
	s1.insert(4);


	cout << s1.count(3) << endl; 
	cout << s1.count(2) << endl;
	cout << s1.size() << endl;
	s1.clear();
	cout << s1.size() << endl;
	cout << s1.empty() << endl;

我们可以发现查找 3 2 出现的次数

s1的大小 清空后的大小 以及s1是否为空

在这里插入图片描述

展示交换容器

	set<int> s1;
	s1.insert(1);
	s1.insert(3);
	s1.insert(5);
	s1.insert(3);
	s1.insert(4);
	s1.insert(9);
	s1.insert(7);
	s1.insert(4);
	set<int> s2;
	cout << s2.size() << endl;
	cout << s1.size() << endl;
	s2.swap(s1);

	cout << s2.size() << endl;
	cout << s1.size() << endl;

我们可以发现 交换完之后它们的大小发生了改变

在这里插入图片描述

multiset

multiset和set的底层实现都是相同的 接口函数也十分类似

它们之间最大的区别就是 multiset中允许键值对冗余

	multiset<int> s1;
	s1.insert(1);
	s1.insert(3);
	s1.insert(5);
	s1.insert(3);
	s1.insert(4);
	s1.insert(9);
	s1.insert(7);
	s1.insert(4);
	
	for (auto x : s1)
	{
		cout << x << " ";
	}

在这里插入图片描述

我们可以发现 相比于set 这里多出来了很多冗余的数据

由于multiset容器允许键值冗余,因此两个容器中成员函数find和count的意义也有所不同

成员函数find功能
set对象返回值为val的元素的迭代器
multiset对象返回底层搜索树中序的第一个值为val的元素的迭代器
成员函数count功能
set对象值为val的元素存在则返回1,不存在则返回0(find成员函数可代替)
multiset对象返回值为val的元素个数(find成员函数不可代替)

map

map的介绍

  1. map是关联式容器 它按照特定的次序来存储 < key value >数据 当我们遍历map中的数据的时候 我们可以得到一组有序的数据
  2. 在map中 键值key用于排序和唯一的标识元素 而值value则储存与key相关的内容 它们的类型可能不同 并且在map的内部 它们通过成员类型value_type绑定在一起 并取别名pair
  3. map容器中的key值是不可以被改变的 但是value值可以被修改 这是因为map底层的二叉搜索是根据key建立的
  4. 在map内部 它的元素是按照key值来进行比较的 一般是默认小于
  5. map容器通过键值key访问单个元素的效率比unordered_map容器慢 但是map容器允许根据顺序对元素进行迭代
  6. map容器支持下标访问 即在【】中放入key 便可以找到对应的value
  7. map底层是用红黑树实现的 查找某个元素的效率为 logN

map的定义方式

方式一: 指定构造key value类型的空容器

	map<int, char> m1; // 创造一个key为int value为char的空容器

方式二: 拷贝构造某类型容器

	map<int, char> m2(m1) // 拷贝构造一个m1的复制品m2

方式三: 使用迭代器拷贝构造某一段内容

	map<int, char> m3(m2.begin(),m2.end()); // 使用m2的迭代器拷贝构造m3

方式四: 指定key和value的类型构造一个空容器 key比较方式指定为大于

	map<int, char, greater<int>> m4;// 创造一个key为int value为char 比较方式为greater的空容器

map的插入

map插入函数的原型如下

pair<iterator,bool> insert (const value_type& val);

insert函数的参数

insert函数的参数是value_type类型的 实际上就是pair类型的别名

typedef pair<const Key, T> value_type;

因此 我们向map容器插入元素的时候 我们需要用key和value构造一个pair对象 然后再将pair对象作为参数传入insert函数

方式一: 构造匿名对象插入

	map<string, string> dict;
	// 调用pair的构造函数 构造一个匿名对象
	dict.insert(pair<string, string>("left", "左边"));
	dict.insert(pair<string, string>("right", "右边"));
	dict.insert(pair<string, string>("hello", "你好"));

	for (auto x : dict)
	{
		cout << x.first << " : " << x.second << endl;
	}

运行结果如下

在这里插入图片描述
但是这种插入方式会导致代码变得很长 所以我们不经常使用它 更多的是使用方式二

方式二: 使用模板插入

在库当中提供了以下的模板

	template<class T1, class T2>
	pair<T1, T2> make_pair(T1 x, T2 y)
	{
		return (pair<T1, T2>(x, y));
	}

因此我们只需要向函数传递key和value 该函数模板就会根据类型自动推导 最终返回一个pair对象

比如说我们上面的代码可以改写成这样子

	map<string, string> dict;
	// 调用make_pair函数
	dict.insert(make_pair("left", "左边"));
	dict.insert(make_pair("right", "右边"));
	dict.insert(make_pair("hello", "你好"));


	for (auto x : dict)
	{
		cout << x.first << " : " << x.second << endl;
	}

是不是感觉代码整体简洁了不少呢

insert的返回值

我们首先来看官网的原文

The single element versions (1) return a pair, with its member pair::first set to an iterator pointing to either the newly inserted element or to the element with an equivalent key in the map. The pair::second element in the pair is set to true if a new element was inserted or false if an equivalent key already existed.

The versions with a hint (2) return an iterator pointing to either the newly inserted element or to the element that already had an equivalent key in the map.

Member type iterator is a bidirectional iterator type that points to elements.
pair is a class template declared in (see pair).

我们从上面可以知道

insert函数的返回值是一个pair对象 该对象中的第一个成员是map的迭代器类型 第二个成员是一个bool类型具体含义如下

  • 若待插入元素的键值key在map当中不存在 则insert标识插入成功 并返回插入后元素的迭代器和true
  • 若待插入元素的键值key在map当中存在 则insert插入失败 并返回map中键值为key的迭代器和false

map的查找

map查找函数的原型如下

iterator find (const key_type& k);

map查找函数是根据key值在map当中查找

  • 如果找到了 则返回对应的迭代器
  • 如果找不到 则返回end迭代器
	map<string, string> dict;
	// 调用make_pair函数
	dict.insert(make_pair("left", "左边"));
	dict.insert(make_pair("right", "右边"));
	dict.insert(make_pair("hello", "你好"));

	// 查找right并且打印right的中文意思
	auto it = dict.find("right");
	if (it != dict.end())
	{
		cout << it->second << endl;
	}
}

map的删除

map删除的函数原型如下:

删除指定k值

size_type erase (const key_type& k);

删除迭代器位置

void erase(iterator position);

这里和set是一样的

我们使用两种方式删除

	map<string, string> dict;
	// 调用make_pair函数
	dict.insert(make_pair("left", "左边"));
	dict.insert(make_pair("right", "右边"));
	dict.insert(make_pair("hello", "你好"));

	auto it = dict.find("right");
	dict.erase(it); // 删除right的迭代器


	dict.erase("left"); // 删除key值为left的数据
	for (auto x : dict)
	{
		cout << x.first << " : " << x.second << endl;
	}

在这里插入图片描述
我们可以发现 最后dict被删除的只剩下了 hello

map的[ ]运算符重载

map的【】运算符重载的函数原型如下:

mapped_type& operator[] (const key_type& k);

【】运算符重载函数的参数就是一个key值 而这个函数的返回值如下

(*((this->insert(make_pair(k, mapped_type()))).first)).second

看上去可能有点难理解 但是我们分成三个步骤就好理解多了

  1. 使用insert插入键值对
  2. 获取insert函数的返回值迭代器
  3. 返回该迭代器元素的值value

对应的分解代码如下

	mapped_type& operator[] (const key_type& k)
	{
		//1、调用insert函数插入键值对
		pair<iterator, bool> ret = insert(make_pair(k, mapped_type()));
		//2、拿出从insert函数获取到的迭代器
		iterator it = ret.first;
		//3、返回该迭代器位置元素的值value
		return it->second;
	}

那么这个重载【】有什么意义呢 我们看下面的这段代码

	map<string, string> dict;
	// 调用make_pair函数
	dict.insert(make_pair("left", "左边"));
	dict.insert(make_pair("right", "右边"));
	dict.insert(make_pair("hello", "你好"));


	dict["right"] = "正确";
	dict["go"] = "出发";

	for (auto x : dict)
	{
		cout << x.first << " : " << x.second << endl;
	}

以这两行代码为例

	dict["right"] = "正确";
	dict["go"] = "出发";

通过了【】的三个步骤之后 不管原来的dict里面有没有我们的key值

都会通过insert函数来返回一个迭代器的value的引用

从而会发生下面的情况

在这里插入图片描述
我们修改了right的含义 并且增加了go的含义 于是我们可以总结一下

  • 如果key不在map中 则使用【】会插入键值对 然后返回该键值对中value的引用
  • 如果key在map中 则会返回键值为K的元素的value的引用

map的其他成员函数

其他成员函数的用法基本上和set一样
在这里插入图片描述
这里就不过多介绍了 代码和运行结果如下

	map<string, string> dict;
	// 调用make_pair函数
	dict.insert(make_pair("left", "左边"));
	dict.insert(make_pair("right", "右边"));
	dict.insert(make_pair("hello", "你好"));
	map<string, string> dict1;
	// 调用make_pair函数
	dict1.insert(make_pair("left", "左边"));
	dict1.insert(make_pair("right", "右边"));
	dict1.insert(make_pair("hello", "你好"));

	cout << dict.size() << endl;
	cout << dict.empty() << endl;
	cout << dict.count("left") << endl;
	dict.clear();
	cout << dict.empty() << endl;
	dict.swap(dict1);
	cout << dict.size() << endl;

在这里插入图片描述

multimap

multimap和map的底层一样 都是由红黑树构造的

它们的接口函数也类似

它允许键值冗余

代码和演示结果如下

	multimap<string, string> dict;
	// 调用make_pair函数
	dict.insert(make_pair("right", "对的"));
	dict.insert(make_pair("left", "左边"));
	dict.insert(make_pair("right", "右边"));
	dict.insert(make_pair("hello", "你好"));

	for (auto x : dict)
	{
		cout << x.first << " : " << x.second << endl;
	}

在这里插入图片描述

由于multimap容器允许键值冗余 因此两个容器中成员函数find和count的意义也有所不同

成员函数find功能
map对象返回值为键值为key的元素的迭代器
multimap对象返回底层搜索树中序的第一个键值为key的元素的迭代器
成员函数count功能
map对象键值为key的元素存在则返回1,不存在则返回0(find成员函数可代替)
multimap对象返回键值为key的元素个数(find成员函数不可代替)

由于multimap允许键值冗余 所以说调用【】时 返回值存在歧义 所以在muitimap中没有重载【】

在这里插入图片描述

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

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

相关文章

声纹图-声谱图-js之wavesurfer.js(配置、事件、方法中文版翻译)

配置信息 optiontypedefaultEnglish descriptiontranslateaudioRatefloat1Speed at which to play audio. Lower number is slower.播放音频的速度。数值越低&#xff0c;速度越慢。audioContextobjectnoneUse your own previously initialized AudioContext or leave blank.n…

挺进2023 年的JavaScript 框架

瞥见未来的美妙之处在于&#xff0c;道路永远不会完全清晰。我们可以观察趋势&#xff0c;观察创新并尝试规划路线。更好的是&#xff0c;我们可以成为这些创新的一部分来指导方向。但没有什么是确定的。 2022 年发布了大量推动 Web 开发的重大版本。我们看到了 Astro 和 Svel…

Jetson NX + yolov5 v5.0 + TensorRT加速+调用usb摄像头

上一篇笔记记录了如何使用yolov5使用usb摄像头使用权重进行测试,测试效果如下 本篇文章具体操作步骤如下就可以了&#xff0c;切记版本要对应 &#xff0c;我产生这个错误的原因就是版本问题&#xff0c;成功转换但是还是卡顿&#xff0c;估计是硬件usb问题&#xff0c;加速以后…

2023年最值得学习的10大编程语言

作为一名程序员&#xff0c;我们的目标之一就是学习新技术和编程语言&#xff0c;但是你应该学习哪些语言呢&#xff1f;由于学习一门编程语言既需要时间又需要耐心&#xff0c;因此您应该学习一门值得付出努力的语言&#xff1b;我的意思是&#xff0c;它可以帮助你获得更好的…

2023前端必会手写面试题整理

实现一个compose函数 组合多个函数&#xff0c;从右到左&#xff0c;比如&#xff1a;compose(f, g, h) 最终得到这个结果 (...args) > f(g(h(...args))). 题目描述:实现一个 compose 函数 // 用法如下: function fn1(x) {return x 1; } function fn2(x) {return x 2; } …

UTF-8编码

阅读该文章之前&#xff0c;请阅读以下两篇文章&#xff0c;了解GBK编码和Unicode编码&#xff1a; GBK编码的理解_sgmcy的博客-CSDN博客 Unicode编码的理解_sgmcy的博客-CSDN博客 UTF的意思是&#xff1a;Unicode Transformation Format 。也就是Unicode 转换格式。可见&am…

python初级教程七 JSON 数据解析

JSON 数据解析 JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式。 Python3 中可以使用 json 模块来对 JSON 数据进行编解码&#xff0c;它包含了两个函数&#xff1a; json.dumps(): 对数据进行编码。 json.loads(): 对数据进行解码。 在 json 的编解码过程…

【数据结构】C语言实现双链表

目录 前言 双链表节点定义 接口函数实现 初始化函数 创建节点 打印双链表 尾插节点 尾删节点 头插节点 头删节点 指定位置前插入 删除指定位置节点 改写插入删除 判断链表是否为空 计算链表长度 销毁链表 双链表完整代码 浅谈链表及顺序表 前言 前面我们已经实…

Speed and Memory Efficient Dense RGB-D SLAM in Dynamic Scenes论文笔记

Speed and Memory Efficient Dense RGB-D SLAM in Dynamic Scenes论文笔记 论文中的主要引用文献&#xff1a; [7:A coarse and relevant 3d representation for fast and lightweight rgb-d mapping] 超表元建图 [14: Fast optical flow using dense inverse search] 稠密光流…

后端Web开发框架(Java)

为什么使用Spring Boot 简化配置&#xff0c;无需编写太多的 xml 配置文件&#xff0c;效率很高&#xff1b;Spring 可以整合很多各式各样的框架&#xff0c;并能很好的集成&#xff1b;基于 Spring 构建&#xff0c;使开发者快速入门&#xff0c;门槛很低&#xff1b;Spring …

LabVIEW调用自己写的DLL

首先&#xff0c;我用的LabVIEW是8.5版本的&#xff0c;比较老但工作需要 先新建VI 程序框图中选择 互连接口 - 库与可执行程序 选择 调用库函数… 拖到面板 并右击它 选择配置 在库名或路径中选择写好的DLL方案中的DEBUG中dll文件 确定以后就要选择哪个函数&#xff0c;并…

【数字图像处理】毛笔字细化

源码链接&#xff1a;calligraphy.cpp 一、实验要求 附件是书法毛笔字&#xff0c;请将附件图片中“年少有为”四个字进行笔画细化。 二、实验内容 首先观察图片&#xff0c;是只将黑色的毛笔字部分进行细化&#xff0c;所以需要先把印章这类的区域去除。先通过将图片转到h…

Content Security Policy (CSP) 介绍

内容安全策略 (CSP) 是一个额外的安全层&#xff0c;用于检测并削弱某些特定类型的攻击&#xff0c;包括跨站脚本 (XSS) 和数据注入攻击等。无论是数据盗取、网站内容污染还是散发恶意软件&#xff0c;这些攻击都是主要的手段。 起因 当我不经意间在 Twitter 页面 view source…

JavaScript 简单类型与复杂类型

JavaScript 简单类型与复杂类型 目录JavaScript 简单类型与复杂类型1. 简单类型与复杂类型2. 堆和栈3. 简单类型的内存分配4.复杂类型的内存分配5. 简单类型传参6. 复杂类型传参7.下面是代码1、Math对象最大值2. 封装自己的数学对象3. Math绝对值和三个取整方法4.Math对象获取随…

WebSocket实现聊天室

需求 实现用户登录功能展示用户好友列表功能实现用户历史消息展示实现单聊信息和群聊信息 效果展示 用户登录 好友列表展示 历史消息展示 聊天 代码实现 说明&#xff1a;Springboot项目&#xff0c;页面是用 thymeleaf 整合的。 maven依赖 <dependencies><depen…

π122E31兼容ISO7221CD 200Mbps高速率 双通道数字隔离器

π122E31兼容ISO7221CD 200Mbps高速率 双通道数字隔离器&#xff0c;具有出色的性能特征和可靠性&#xff0c;整体性能优于光耦和基于其他原理的数字隔离器产品。 产品传输通道间彼此独立&#xff0c;可实现多种传输方向的配置&#xff0c;可实现 3.0kVrms 隔离耐压等级和 DC 到…

MySQL数据库的安装与实现

MySQL在win系统中的安装 第1步&#xff1a;下载安装&#xff08;在windows系统中安装&#xff09; http://downloads.mysql.com/archives/community/ 我选择安装的是5.7.31&#xff0c;一般MySQL主要分为两个版本&#xff0c;一个是5.7系列&#xff0c;一个是5.8系列&#xf…

Linux 音频驱动

1 I.MX6ULL 开发板通过此接口外接了一个 WM8960 音频 DAC 芯片。 2 在信号处理领域&#xff0c;外界的声音是模拟信号&#xff0c;处理器能理解的是数字信号&#xff0c;因此这里就涉及到一个模拟信号转换为数字信号的过程&#xff0c;而完成这个功能的就是 ADC 芯片。 如果处…

MySQL的锁

把那些可能会被多个线程同时操作的资源称为临界资源&#xff0c;加锁的目的就是让这些临界资源在同一时刻只能有一个线程可以访问。数据库作为用户共享的一个资源&#xff0c;如何保证数据并发访问一致性也是所有数据库必须解决的问题&#xff0c;如何加锁是数据库并发访问性能…

字节前端高频手写面试题(持续更新中)

Promise // 模拟实现Promise // Promise利用三大手段解决回调地狱&#xff1a; // 1. 回调函数延迟绑定 // 2. 返回值穿透 // 3. 错误冒泡// 定义三种状态 const PENDING PENDING; // 进行中 const FULFILLED FULFILLED; // 已成功 const REJECTED REJECTED; // 已…