C++:STL简介和容器string用法篇

news2024/11/15 1:52:51

一、STL简介

       STL是C++中的标准模板库(Standard Template Library)的缩写。它是C++标准库的一部分,提供了一系列的数据结构和算法模板,包括各种容器、算法、迭代器、仿函数等,用于简化和加速C++程序的开发过程。STL的设计理念是提供通用、高效的数据结构和算法实现,使得开发者可以更加专注于业务逻辑的实现,提高代码的可读性、可维护性和可移植性。

       STL在C++中有以下几个主要用处:

  1. 提供丰富的数据结构:STL包含了各种常用的容器,如动态数组(vector)、双向链表(list)、映射(map)、集合(set)等。这些数据结构可以满足不同的需求,如存储数据、快速查找、有序存储等。

  2. 实现高效的算法:STL提供了大量的算法模板,包括排序、查找、变换、合并等,这些算法经过了优化和测试,通常具有高效的执行速度和可靠的性能。开发者可以直接使用这些算法,无需自己从头实现,提高了开发效率。

  3. 提供灵活的迭代器:STL中的迭代器提供了一种统一的访问数据结构元素的方式,包括输入迭代器、输出迭代器、正向迭代器、双向迭代器和随机访问迭代器等。开发者可以根据需要选择合适的迭代器类型进行数据遍历和操作。

  4. 支持泛型编程:STL是基于模板的,支持泛型编程。这意味着开发者可以编写通用的代码,适用于不同类型的数据结构和数据类型,提高了代码的复用性和通用性。

  5. 提高代码的可读性和可维护性:STL提供了经过优化和测试的标准化实现,使得代码更加清晰易懂。开发者可以直接使用STL提供的功能模块,避免了重复造轮子的工作,同时也降低了出错的风险,提高了代码的可维护性和可读性。

       综上所述,STL在提高开发效率和代码质量等方面具有很大的作用,是C++程序开发中不可或缺的重要工具。

       STL六大组件:

       网上有句话说:“不懂STL,不要说你会C++”。STL是C++中的优秀作品,有了它的陪伴,许多底层的数据结构以及算法都不需要自己重新造轮子,使我们能够快速地进行开发。

       我们的STL系列将从容器string部分开始介绍。

二、string类

       标准库类型string表示可变长的字符序列,使用string类型必须首先包含string头文件。作为标准库的一部分,string定义在命名空间std中。

1.定义和初始化string对象

       string是一个类,因此,实例化出string类时,会调用该类的构造函数。string对象也可以使用其他方式来初始化。常用的string对象构造方法有以下几种:

string s1;// 调用默认构造函数,s1是一个空字符串
string s2 = s1;// 调用拷贝构造函数
string s3 = "hello world!"; // 使用字符串初始化s3
string s4(10, 's'); // 使用10个's'字符初始化s4
string s5(s3, 6, 3);// 从第七个字符开始,将3个s3字符串中的字符拷贝到s5中
string s6(s3, 3);// 将前3个s3字符串中的字符拷贝到s6中

       在构造s5的过程中,可以不传第三个参数,那么编译器将会将第三个参数设为string::npos,而string::npos的值为大约42亿,也就是说,编译器会将s3中的从第七个字符开始,拷贝42亿个字符给s5,但是s3显然没有那么多字符。所以当遇到s3字符串的结尾时,编译器将会停止拷贝。那如果第三个参数传的过大,拷贝长度大于从开始到结尾的长度,那也是遇到字符串末尾然后停止拷贝。

       string类中重载了流插入<<、流提取>>操作符,使我们可以像打印、输入int型、char型等内置类型一样打印或输入string型。也重载了内置类型常用的大多数操作符。

2.遍历string对象

       string类中重载了'[]'运算符,使我们可以像遍历字符数组一样遍历string对象。

void test1()// string对象的遍历
{
	string s1("hello world!");
	for (int i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << ' ';
	}
}

int main()
{
	test1();
	return 0;
}

       运行程序,结果如下:

       也可以将上面的循环换为范围for循环来遍历string对象,使用起来更加方便。

三、string类成员函数

1.容量

(1)size和length

size_t size() const;
size_t length() const;

       string类中有求字符串长度的函数size()和length(),它们的作用相同。调用方法如下:

void test1()
{
	string s1("hello world!");
	string s2("hellohello world!");
	cout << s1.size() <<endl;
	cout << s2.length();
}

       运行程序,结果如下:

(2)max_size

       string类中的max_size()函数用于求字符串的潜在最大长度。

size_t max_size() const;

       调用该函数之后,会得到一个非常大的数字。

(3)resize

void resize (size_t n);
void resize (size_t n, char c);

       string类中提供了两个调节字符串大小的函数。第一个参数都是一个无符号整形,意思是将字符串大小调整为n。若字符串长度大于n,那么n之后的字符将会丢弃,若字符串的长度小于n,那么第一个函数将会在n之后填充空字符,第二个函数则会使用指定的字符填充。

       我们知道,字符串末尾还隐藏了一个‘\0’字符。空字符是不计入长度的。计算字符串长度时,遇到任意空字符就停止计数。打印的时候也是以任意空字符为结尾的。

void test1()
{
	string s1("hello\0\0\0\0\0 world!    ");
	string s2("hellohello world!");
	//s1.resize(18);
	cout << s1 << 'a' << endl;
	cout << s1.size() << endl;
	
}

       运行程序,结果如下:

       但是我们将resize函数取消注释,再次运行程序:

       发现长度来到了18,也就是说,编译器填充的空字符,是计入长度的。

       通过监视窗口我们看到,没有执行resize语句时,s1中字符串只有五个字符,执行之后,后面13个字符都是\0并且计入长度。

(4)capacity

size_t capacity() const;

       string类中有计算容量的成员函数,它表示编译器为对象实际分配的内存空间,它可以大于或等于字符串的长度。若容量满时,会自动扩容。

(5)reverse

void reserve (size_t n = 0);

       reverse函数使用来修改字符串容量的。给reverse传的参数n是告诉编译器假定字符串的长度为n,然后让编译器重新为对象分配空间。编译器会结合实际字符串长度来分配空间。若n小于实际字符串长度,容量的变化具体看编译器。

(6)clear

void clear();

       它的作用为清空字符串。使对象变为长度为0的字符串。

(7)empty

bool empty() const;

       它的作用是判断字符串是否为空,并不会改变字符串中的内容。

(8)shrink_to_fit

bool empty() const;

       请求字符串减小其容量以适应其大小。

2.元素访问

(1)operator[]

       string类中具有运算符重载, 使我们可以像访问数组那样访问string对象中的值。

char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;

(2)at

char& at (size_t pos);
const char& at (size_t pos) const;

       返回对字符串中位置pos处的字符的引用。

(3)back

char& back();
const char& back() const;

       返回对字符串最后一个字符的引用。此函数不应在空字符串上调用。

(4)front

char& front();
const char& front() const;

       返回对字符串第一个字符的引用。此函数不应在空字符串上调用。

3.修改

(1)operator+=

string& operator+= (const string& str);
string& operator+= (const char* s);
string& operator+= (char c);

       string具有+=重载函数,使我们可以像内置类型一样使用+=操作符:

void test1()
{
	string s1("hello world!");
	string s2("hi");
	s1 += "sss";
	s1 += 'a';
	s1 += s2;
	cout << s1 << endl;
	cout << s1.size() << endl;
}

       运行代码,结果如图所示:

       有了+=重载操作符重载,我们可以很方便地为字符串后添加字符。

(2)append

string& append (const string& str);
string& append (const string& str, size_t subpos, size_t sublen);
string& append (const char* s);
string& append (const char* s, size_t n);
string& append (size_t n, char c);
template <class InputIterator>
string& append (InputIterator first, InputIterator last);

       append也表示向字符串的末尾添加字符串。它重载了许多函数,实现了很多功能。

string& append (const string& str);

       表示向一个string对象末尾添加另一个string对象,也就是在一个字符串的末尾添加另一串字符串。

string& append (const string& str, size_t subpos, size_t sublen);

       表示向一个string对象末尾添加另一个string对象的一部分,它从subpos位置开始,直到拷贝长度达到sublen时为止或者直到遇到字符串末尾。

string& append (const char* s);

       表示向string对象末尾附加一个由指针变量指向的字符串。

string& append (const char* s, size_t n);

       表示向string对象末尾附加一个由指针变量指向的字符串的前n个字符。

string& append (size_t n, char c);

       表示向string对象末尾附加n个c字符。

(3)push_back

void push_back (char c);

       表示向string对象末尾附加1个c字符。

(4)assign

string& assign (const string& str);
string& assign (const string& str, size_t subpos, size_t sublen);
string& assign (const char* s);
string& assign (const char* s, size_t n);
string& assign (size_t n, char c);

        assign表示赋值,将当前值清空并赋新值。

string& assign (const string& str);

       表示将另一个string对象赋给当前string对象。

string& assign (const string& str, size_t subpos, size_t sublen);

       表示将另一个string对象的一部分赋给当前string对象。从subpos开始,到sublen处结束,或遇到字符串尾结束。

string& assign (const char* s);

       表示将s指向的字符串赋给当前string对象。

string& assign (const char* s, size_t n);

        表示将s指向的字符串的前n个字符赋给当前string对象。

string& assign (size_t n, char c);

       表示将n个c字符赋给当前string对象。

(5)insert

 string& insert (size_t pos, const string& str);
 string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);
 string& insert (size_t pos, const char* s);
 string& insert (size_t pos, const char* s, size_t n);
 string& insert (size_t pos, size_t n, char c);
void insert (iterator p, size_t n, char c);

        insert也表示插入,它支持在任意位置插入。不过它的效率不高。

string& insert (size_t pos, const string& str);

       表示在下标pos之后插入一个string对象。

string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);

       表示在下标pos之后插入一个string对象的一部分,它从subpos处开始,直到拷贝长度达到sublen或者遇到字符串结尾。

string& insert (size_t pos, const char* s);

       表示在pos位置插入一个s指向的字符串。

string& insert (size_t pos, const char* s, size_t n);

       表示在pos位置插入一个s指向的字符串的前n个字符。

string& insert (size_t pos, size_t n, char c);

        表示在pos位置插入n个字符c。

(6)erase

string& erase (size_t pos = 0, size_t len = npos);

       erase表示删除,删除从pos位置开始, 直到删除长度达到len或者对象中已经无字符。若不传参数,那么表示全部删除。

(7)replace

string& replace (size_t pos,  size_t len,  const string& str);
string& replace (size_t pos,  size_t len,  const string& str,
                 size_t subpos, size_t sublen);
string& replace (size_t pos,  size_t len,  const char* s);
string& replace (size_t pos,  size_t len,  const char* s, size_t n);
string& replace (size_t pos,  size_t len,  size_t n, char c);

       replace表示替换。

string& replace (size_t pos,  size_t len,  const string& str);

       表示从下标pos位置开始,len长度的字符替换为另一个string对象中的字符串。

string& replace (size_t pos,  size_t len,  const string& str,
                 size_t subpos, size_t sublen);

       表示从下标pos位置开始,len长度的字符替换为另一个string对象中的字符串的一部分,从小标subpos位置开始,拷贝sublen长度的字符串或遇到字符串末尾。

string& replace (size_t pos,  size_t len,  const char* s);

       表示从下标pos位置开始,len长度的字符替换为s指向的字符串。

string& replace (size_t pos,  size_t len,  const char* s, size_t n);

       表示从下标pos位置开始,len长度的字符替换为s指向的字符串的前n个字符。

string& replace (size_t pos,  size_t len,  size_t n, char c);

       表示从下标pos位置开始,len长度的字符替换为n个c字符。

(8)swap

void swap (string& str);
void swap (string& x, string& y);

       交换两string对象的值。

void test1()
{
	string s1("hello world!");
	string s2("abcdefg");
	s1.swap(s2);
	cout << s1 << endl;
	cout << s2 << endl;
}

       运行代码,结果如下图所示:

(9)pop_back

void pop_back();

       尾部删除一个字符。

(10)getline

istream& getline (istream& is, string& str, char delim);
istream& getline (istream& is, string& str);

       从流中提取一行字符串到string对象中,直到遇到delim(界定字符)或换行符。若指定了界定字符,那么当输入界定字符之后,编译器会只保留界定字符之前的字符,将string对象存储的值删除,并添加上新输入的字符。若没有指定界定字符,那么当遇到换行符时,即代表输入结束。

4.字符串操作

(1)c_str

const char* c_str() const;

       返回一个字符串指针,指向一块存储着C风格字符串的空间。字符串的末尾附加了一个空字符。

(2)data

const char* data() const;

       返回一个字符串指针,指向一块存储着C风格字符串的空间。在C++11之前,该字符串末尾不附加空字符,C++11之后,附加了一个空字符,执行与c_str一样的操作。

(3)copy

size_t copy (char* s, size_t len, size_t pos = 0) const;

       从pos位置开始,拷贝len个长度的字符到s指向的空间中。返回拷贝字符的个数。

(4)find

size_t find (const string& str, size_t pos = 0) const;
size_t find (const char* s, size_t pos = 0) const;
size_t find (const char* s, size_t pos, size_type n) const;
size_t find (char c, size_t pos = 0) const;

       第一个重载成员函数可以查找子字符串,pos是开始查找的位置。若找到则返回子字符串在字符串中的下标,若找不到则返回nops。

       第二个重载成员函数可以查找s指向的字符串。

       第三个重载成员函数可以查找s指向的字符串,第三个参数n是查找的长度,若小于被查找的字符串长度,则查找前n个字符组成的字符串。

       第四个重载成员函数可以查找指定的字符。

(5)rfind

size_t rfind (const string& str, size_t pos = npos) const;
size_t rfind (const char* s, size_t pos = npos) const;
size_t rfind (const char* s, size_t pos, size_t n) const;
size_t rfind (char c, size_t pos = npos) const;

       查找要查找的对象最后一次出现的位置。

(6)find_first_of

size_t find_first_of (const string& str, size_t pos = 0) const;
size_t find_first_of (const char* s, size_t pos = 0) const;
size_t find_first_of (const char* s, size_t pos, size_t n) const;
size_t find_first_of (char c, size_t pos = 0) const;

       在string对象中查找一个字符,它同时出现在另一个对象中并且在string对象中是第一个这样的字符。返回该字符的下标。支持查找另一个string对象、指针指向的空间,单个字符。

size_t find_last_of (const string& str, size_t pos = npos) const;
size_t find_last_of (const char* s, size_t pos = npos) const;
size_t find_last_of (const char* s, size_t pos, size_t n) const;
size_t find_last_of (char c, size_t pos = npos) const;

       在string对象中查找一个字符,它同时出现在另一个对象中并且在string对象中是最后一个这样的字符。

(7)find_first_not_of

size_t find_first_not_of (const string& str, size_t pos = 0) const;
size_t find_first_not_of (const char* s, size_t pos = 0) const;
size_t find_first_not_of (const char* s, size_t pos, size_t n) const;
size_t find_first_not_of (char c, size_t pos = 0) const;

       在string对象中查找一个字符,它在另一个对象中不存在,并且是第一个这样的字符,返回该字符的下标。

(8)find_last_not_of

size_t find_last_not_of (const string& str, size_t pos = npos) const;
size_t find_last_not_of (const char* s, size_t pos = npos) const;
size_t find_last_not_of (const char* s, size_t pos, size_t n) const;
size_t find_last_not_of (char c, size_t pos = npos) const;

       在string对象中查找一个字符,它在另一个对象中不存在,并且是最后一个这样的字符,返回该字符的下标。

(9)substr

string substr (size_t pos = 0, size_t len = npos) const;

       创建一个子字符串,该子字符串的值为string对象中从pos位置开始,len个长度或到达字符串末尾的字符串的拷贝。

(10)compare

int compare (const string& str) const;
int compare (size_t pos, size_t len, const string& str) const;
int compare (size_t pos, size_t len, const string& str,
             size_t subpos, size_t sublen) const;
int compare (const char* s) const;
int compare (size_t pos, size_t len, const char* s) const;
int compare (size_t pos, size_t len, const char* s, size_t n) const;

       将字符串的值与参数指定的字符串的值进行比较。

       返回值:

  • =0,两字符串相等。
  • >0,string对象1>string对象2。若遇到第一个字符串与第二个字符串不相等的第一个字符,比较它们的ascii值。
  • <0,string对象1<string对象2。
int compare (size_t pos, size_t len, const string& str) const;

       将string对象中从pos位置开始,len个长度的字符串与另一个对象比较。

int compare (size_t pos, size_t len, const string& str,
             size_t subpos, size_t sublen) const;

       将string对象中从pos位置开始,len个长度的字符串与另一个对象中的subpos位置开始,sublen长度的字符串比较。

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

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

相关文章

[6] CUDA之线程同步

CUDA之线程同步 共享内存&#xff1a;线程时间需要互相交换数据才能完成任务的情况并不少见&#xff0c;因此&#xff0c;必须存在某种能让线程彼此交流的机制当很多线程并行工作并且访问相同的数据或者存储器位置的时候&#xff0c;线程间必须正确的同步线程之间交换数据并不…

Django-auth组件

Django-auth组件 1 表结构 我们从python manage.py migrate为我们创建的auth组件内置的表开始看 auth_user&#xff1a;用户表存储用户信息&#xff08;登录admin后台&#xff09; 里面的字段分两类&#xff1a;用户基本信息&#xff08;用户名&#xff0c;邮箱&#xff0c;密…

音视频开发4-补充 FFmpeg 开发环境搭建 -- 在windows 上重新build ffmpeg

本节的目的是在windows 上 编译 ffmpeg 源码&#xff0c;这样做的目的是&#xff1a;在工作中可以根据工作的实际内容裁剪 ffmpeg&#xff0c;或者改动 ffmpeg 的源码。 第一步 &#xff1a;下载&#xff0c; 安装&#xff0c;配置 &#xff0c;运行 msys64 下载 下载地址&…

字符串的周期:每一期都有那么几位

【题目描述】 如果一个字符串可以由某个长度为k的字符串重复多次得到,则称该串以k为周期。例 如,abcabcabcabc以3为周期(注意,它也以6和12为周期)。 输入一个长度不超过80的字符串(不含空格),输出其最小周期。 输入第一行表示有T组数据,后续是T行字符串。输出的每组…

透视App投放效果,Xinstall助力精准分析,让每一分投入都物超所值!

在移动互联网时代&#xff0c;App的推广与投放成为了每一个开发者和广告主必须面对的问题。然而&#xff0c;如何精准地掌握投放效果&#xff0c;让每一分投入都物超所值&#xff0c;却是一个令人头疼的难题。今天&#xff0c;我们就来谈谈如何通过Xinstall这个专业的App全渠道…

python纯脚本搬砖DNF之深度学习,工作室适用

声明&#xff1a; 本文章仅作学习交流使用,对产生的任何影响&#xff0c;本人概不负责. 转载请注明出处:https://editor.csdn.net/md?articleId103674748 主要功能 脚本已初步完成&#xff0c;可以上机实战了 1.搬砖研究所、海伯伦&#xff08;持续更新中&#xff09; 2.自…

【揭秘!在线ChatGPT神器,体验入口在此!】

&#x1f680;【揭秘&#xff01;在线ChatGPT神器&#xff0c;体验入口在此&#xff01;】&#x1f680; 前言 嘿&#xff0c;大家好&#xff01;今天我要和大家分享一些关于如何使用免费的ChatGPT的技巧。ChatGPT是一项令人兴奋的人工智能技术&#xff0c;它可以成为我们的好…

【vue-2】v-on、v-show、v-if及按键修饰符

目录 1、v-on事件 2、按键修饰符 3、显示和隐藏v-show 4、条件渲染v-if 1、v-on事件 创建button按钮有以下两种方式&#xff1a; <button v-on:click"edit">修改</button> <button click"edit">修改</button> 完整示例代码…

AlexNet论文解析—ImageNet Classification with Deep Convolutional Neural Networks

AlexNet论文解析—ImageNet Classification with Deep Convolutional Neural Networks 2012 研究背景 认识数据集&#xff1a;ImageNet的大规模图像识别挑战赛 LSVRC-2012&#xff1a;ImageNet Large Scale Visual Recoanition Challenge 类别训练数据测试数据图片格式Mnist1…

Block-level Image Service for the Cloud——论文泛读

TOS 2024 Paper 论文阅读笔记整理 问题 企业越来越需要敏捷和弹性的计算基础设施来快速响应现实世界的情况。容器通过提供高效的基于流程的虚拟化和分层映像系统&#xff0c;可以实现灵活和弹性的应用程序部署。然而&#xff0c;由于图像下载和解包过程&#xff0c;创建或更新…

Web开发学习总结

学习路线 Web 全球广域网&#xff0c;也称为万维网(www World Wide Web)&#xff0c;能够通过浏览器访问的网站 初识Web前端 Web标准也称为网页标准&#xff0c;由一系列的标准组成&#xff0c;大部分由W3C(World Wide Web Consortium&#xff0c;万维网联盟)负责制定。三个组…

Qt pro工程文件编写汇总(区分debug和release、32位和64位的方法,编译输出目录等)

前言&#xff1a; 从事qt开发已经好几年了&#xff0c;但有关pro编写的一些细节问题一直没有一个很好的梳理汇总——因为实际工作开发中&#xff0c;往往只需要编译特定版本的软件&#xff08;例如32位release版本&#xff09;&#xff0c;项目创建好后并设置好编译路径&#x…

OSPF网络类型实验2

对R4 对R5&#xff0c;找R1注册 对R1宣告环回&#xff0c;再宣告一下tunnel接口 本实验不考虑区域划分 现在已经全部宣告完成 对R1&#xff0c;2&#xff0c;3改接口 broadcast工作方式hello时间10s&#xff0c;然后进行dr选举&#xff0c;由于2&#xff0c;3之间没有伪广播 …

【探索数据结构】线性表之双链表

&#x1f389;&#x1f389;&#x1f389;欢迎莅临我的博客空间&#xff0c;我是池央&#xff0c;一个对C和数据结构怀有无限热忱的探索者。&#x1f64c; &#x1f338;&#x1f338;&#x1f338;这里是我分享C/C编程、数据结构应用的乐园✨ &#x1f388;&#x1f388;&…

乡村振兴的农业品牌建设:打造农业品牌,提升农产品附加值,增强乡村经济竞争力,实现美丽乡村经济繁荣

目录 一、引言 二、农业品牌建设的重要性 &#xff08;一&#xff09;提升农产品附加值 &#xff08;二&#xff09;增强乡村经济竞争力 &#xff08;三&#xff09;实现美丽乡村经济繁荣 三、农业品牌建设的现状及问题 &#xff08;一&#xff09;现状 &#xff08;二…

Go微服务: Nacos的搭建和基础API的使用

Nacos 概述 文档&#xff1a;https://nacos.io/docs/latest/what-is-nacos/搭建&#xff1a;https://nacos.io/docs/latest/quickstart/quick-start-docker/有很多种搭建方式&#xff0c;我们这里使用 docker 来搭建 Nacos 的搭建 这里&#xff0c;我们选择单机模式&#xf…

java实现图书系统源码

建包和类: Book Book: package Book;public class Book {private String name;private String author;private int price;private String type;private boolean isBorrowed;public Book(String name, String author, int price, String type) {this.name name;this.author …

【Qnx 】Qnx IPC通信PPS

Qnx IPC通信PPS Qnx自带PPS服务&#xff0c;PPS全称Persistent Publish/Subscribe Service&#xff0c;就是常见的P/S通信模式。 Qnx PPS的通信模式是异步的&#xff0c;Publisher和Subscriber也无需关心对方是否存在。 利用Qnx提供的PPS服务&#xff0c;Publisher可以通知多…

OrangePi KunPengPro | 开发板开箱测评之学习与使用

OrangePi KunPengPro | 开发板开箱测评之学习与使用 时间&#xff1a;2024年5月23日20:51:12 文章目录 OrangePi KunPengPro | 开发板开箱测评之学习与使用概述1.参考2.资料、工具3.使用3-1.通过串口登录系统3-2.通过SSH登录系统3-3.安装交叉编译工具链3-4.复制文件到设备3-5.第…

Android 使用 ActivityResultLauncher 申请权限

前面介绍了 Android 运行时权限。 其中&#xff0c;申请权限的步骤有些繁琐&#xff0c;需要用到&#xff1a;ActivityCompat.requestPermissions 函数和 onRequestPermissionsResult 回调函数&#xff0c;今天就借助 ActivityResultLauncher 来简化书写。 步骤1&#xff1a;创…