C++ | string用法指南

这篇具有很好参考价值的文章主要介绍了C++ | string用法指南。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

前言

一、初始string

1、string是什么

2、string的使用

(1)构造函数

(2)赋值重载

(3)容量相关接口

(4) 迭代器

(5)元素访问

(6)修改

(7)其他类型函数

(8)非成员函数


前言

        本文主要介绍STL容器之一  ----  string,在学习C++的过程中,我们要将C++视为一个语言联邦(摘录于Effective C++ 条款一)。如何理解这句话呢,我们学习C++,可将其分为四个板块;分别为C、Object-Oriented C++(面向对象的C++)、Template C++(模板)、STL。本文就介绍STL中的string;

一、初始string

        string,顾名思义,字符串的意思,在C语言中,我们用字符数组来代替字符串,而C++中,我们使用自定义类型string,本文采用先使用,再模拟实现的方式介绍string;首先我们需要学习string的具体使用方法,随后进行模拟实现,巩固对string的认识;本文所有的用法参考C++文档 C++参考文档 

1、string是什么

        string是STL文档的容器之一,是一个自定义类型,是一个类,由类模板basic_string实例化出来的一个类;

c++ string,C++,c++,开发语言

 类模板basic_string实例化出来了四个类,如下图所示;

c++ string,C++,c++,开发语言

        实例化出的这四个类不同的是他们的编码方式,分别为 char、char16_t、char32_t、wchar_t 这四种;这里提出了一个新的概念,编码;那么什么是编码呢?其实早在我们学习C语言时,我们就接触过编码;没错,就是ASCII(美国信息交换标准代码);以下来自百度百科;

c++ string,C++,c++,开发语言

        什么意思呢,在我们的计算机中,所有的数据只能够以01二进制编码的方式储存,ASCII就是将日常使用的一些符号数字字母收集起来;用数字一一对应表示;而随着计算机的发展,ASCII并不够满足各国的语言,只能针对于英语;因此,为了更好的传播计算机,出现了各种编码;Unicode(万国码)就是其中的一个代表;

c++ string,C++,c++,开发语言

        由于各国语言的差异,可能存在一个字节不能够一一对应文字的情况,不像英文由26个英文字母组成单词,只需保存26个字母即可,而实际上许多国家的语言并不能由一字节的空间能表示出来,因此由不同储存方式繁衍出了 UTF-8、UTF-16、UTF-32,其中数字代码比特位;其中上述 basic_string 实例化出的类储存方式如下:

char          ---  1字节

char16_t   ---   2字节

char32_t   ---   4字节

wchar_t    ---   2字节

总结:上述知识需要有几个基本的了解,我们需记住string是basic_string实例出来的一个类,其类型为一个字节的char;

2、string的使用

        由于string出现的时间实际是早于STL的,是后来划分进STL库的,所以string开始的设计比较冗余,有许多没有必要的接口(一共106个接口函数);这也是被广大C++程序员吐槽的一个槽点,我们无需将每一个接口都记住,我们需要将核心接口记住并熟练使用,遇见一些默认的接口查看文档即可;

(1)构造函数

        在C++98中,string的构造函数一种有如下7种;

int main()
{
	// 1、无参默认构造
	// string();
	string s1;
	// 2、拷贝构造   
	// string (const string& str);
	string s2(s1);
	// 4、通过字符串常量初始化   
	// string (const char* s);
	string s4("hello world");
	// 3、通过字符串子串初始化   
	// string (const string& str, size_t pos, size_t len = npos);
	string s3(s4, 5, 5);
	// 5、通过字符串前n个字符初始化   
	// string (const char* s, size_t n);
	string s5("hello wrold", 6);
	// 6、用n个字符c初始化字符串  
	//  string (size_t n, char c);
	string s6(10, 'x');
	// 7、迭代器区间初始化(暂不介绍)
	return 0;
}

        其中提一下第三种,pos为子串的位置,len子串的长度,若len大于从子串pos位置开始后面字符总数,则表示初始化到子串结尾即可,比如我们要用 “hello world” 初始化字符串,若pos为6,len为20,则用world初始化字符串s1;len还有一个缺省值npos,其数值为无符号整型的-1,也就是无符号的最大值(无符号无负数);

c++ string,C++,c++,开发语言

 (2)赋值重载

赋值重载使string能够用=对string对象重新赋值,string的赋值重载一共有有如下三种;

c++ string,C++,c++,开发语言

int main()
{
	string tmp("hello world");
	string s1;
	string s2;
	string s3;
	// 1、string类进行赋值重载
	s1 = tmp;
	// 2、使用字符串常量赋值重载
	s2 = "hello world";
	// 3、使用字符赋值重载
	s3 = 'A';
    return 0;
}

 (3)容量相关接口

         以下为string类容量相关接口,其中最后一个为缩容接口,由于性能原因,使用的并不多;

c++ string,C++,c++,开发语言

 首先介绍如下六个简单一些的接口;

int main()
{
	string s1("hello world");
	// string中储存的字符个数(不包括\0)
	cout << s1.length() << endl;
	// 与length功能相同
	cout << s1.size() << endl;
	// 可以最多储存多少个字符(理论值,实际上并没有那么多)
	cout << s1.max_size() << endl;
	// string的当前容量
	cout << s1.capacity() << endl;
	// 当前string对象是否为空
	cout << s1.empty() << endl;
	// 清空s1中所有字符
	s1.clear();
    return 0;
}

        其中的length与size两种并无相异,由于string出现的较早,当时没有STL其他容器,先出现了length,后来为了统一接口,于其他容器接口保持一致,因此出现了size;

int main()
{
	// reserve 提前开空间(可能会大于指定的大小,因此开空间规则不同)
	string s1;
	s1.reserve(100);
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	// resize 提前开空间并初始化 缺省值为0
	string s2;
	s2.resize(100);
	cout << s2.size() << endl;
	cout << s2.capacity() << endl;
	s2 += "hhhhhh";
    return 0;
}

c++ string,C++,c++,开发语言

c++ string,C++,c++,开发语言

        我们发现reserve仅仅只是修改capacity,而resize不仅会修改capacity,还会修改size,然后用第二个参数取去初始化新增的区间;当指定大小小于原来空间时,reserve什么都不会做,而resize则会则断大于指定大小后面的区域(在后面补零);

总结:reserve仅改变capacity,resize既改变capacity又改变size;当指定大小小于字符串的size时,resize还可以截断(在后面补 \0 )

(4) 迭代器

         迭代器是STL库中的一个特殊的存在,我们可以通过迭代器对string类中的字符进行增删查改; 在string类中,我们可将其视为指针;string类中的迭代器接口有如下几种;

c++ string,C++,c++,开发语言

c++ string,C++,c++,开发语言

        begin函数返回的是字符串中第一个字符的位置的迭代器,而end函数返回的字符串中最后一个字符的下一个位置的迭代器; 因此遍历一个string类,有一下三种方法;

int main()
{
	string s1("hello world");
	// 三种遍历方式
	// 1、通过[]来访问每一个字符
	for (int i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
	}
	cout << endl;
	// 2、通过迭代来来访问每一个字符
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	// 3、通过范围for(其实范围for就是编译器替换成了迭代器遍历的方法)
	for (auto ch : s1)
	{
		cout << ch << " ";
	}
	cout << endl;
    return 0;
}

c++ string,C++,c++,开发语言

        rbegin与rend系列为反向迭代器;rbegin返回的是最后一个字符的位置的迭代器,rend返回的是第一个字符的前一个位置的迭代器;

c++ string,C++,c++,开发语言

 我们可以通过反向迭代器,对其逆向遍历;反向迭代器的类型为 string::reverse_iterator;

int main()
{	
    string s1("hello world");
    string::reverse_iterator rit = s1.rbegin();
	while (rit != s1.rend())
	{
		cout << *rit << " ";
		rit++;
	}

}

c++ string,C++,c++,开发语言

         其中,还有const类型迭代器,const迭代器不允许修改对象的值;前面带c的接口都为返回的是const迭代器;如下场景;

void Print(const string& s)
{
	// 形参用const对象的引用接收,此时只能用const迭代器系列函数
	string::const_iterator cit = s.cbegin();
	while (cit != s.cend())
	{
		cout << *cit << " ";
		cit++;
	}
	cout << endl;
}

(5)元素访问

        关于元素的访问,也有如下四个接口,最常用的还是方括号; 

c++ string,C++,c++,开发语言

int main()
{
	// []重载使string可以像字符数组一样访问
	string s1("hello world");
	cout << s1[0] << endl;
	cout << s1[1] << endl;
	// at 于[] 功能相同,只不过[]的越界是由assert来限制,而at则是抛异常
	cout << s1.at(0) << endl;
	cout << s1.at(1) << endl;
	// front访问string中第一个字符
	cout << s1.front() << endl;
	// back访问string中最后一个字符
	cout << s1.back() << endl;
    return 0;
}

        方括号的使用如同数组的方括号使用相同;at与方括号用法相同,只是遇见非法访问时是抛异常解决;

(6)修改

        string的修改接口设计得十分冗余;其中我们可以用+=替代append与push_back;实际中,也是+=用得比较多,但是我们还是了解一下相关用法; 

c++ string,C++,c++,开发语言

         +=我们可以加等一个string类,可以加等一个字符,也可以加等一个字符指针;因此有以下用法;

c++ string,C++,c++,开发语言

int main()
{
	string tmp("xxxx");
	string s1("hello world");
	// += 字符
	s1 += ' ';
	// += string类
	s1 += tmp;
	// += 字符指针
	s1 += " hello world";
	cout << s1 << endl;
}

c++ string,C++,c++,开发语言

        +=在string类中是非常全能的一种运算符,可以说非常的好用了;接着就是append与push_back这两个接口;以下对每一种接口都进行了展示;

c++ string,C++,c++,开发语言

 c++ string,C++,c++,开发语言

int main()
{
	string tmp("xxxx");
	string s1;
	// 尾加字符
	// void push_back (char c);
	s1.push_back('c');
	// 尾加string类  
	// string& append (const string& str);
	s1.append(tmp);
	// 尾加string从subpos位置开始的sublen个字符   
	//string& append (const string& str, size_t subpos, size_t sublen);
	s1.append(tmp, 2, 3);
	// 用字符指针指向的字符串/字符尾加
	// string& append (const char* s);
	s1.append("hello world");
	// 用字符指针指向的字符串的前n个字符尾加
	// string& append (const char* s, size_t n);
	s1.append("hello world", 6);
	// 尾加n个c字符   
	// string& append (size_t n, char c);
	s1.append(5, 'x');
	// 迭代器区间追加
	// template <class InputIterator>
	// string& append(InputIterator first, InputIterator last);
    s1.append(tmp.begin(), tmp.end());
	cout << s1 << endl;
    return 0;
}

        assign为string的赋值函数;是一个扩增版的operator =,用的并不多,主要用法如下;

c++ string,C++,c++,开发语言

int main()
{
	string tmp("hello world");
	string s1;
	// 使用string类对其赋值
	// string& assign (const string& str);
	s1.assign(tmp);
	cout << s1 << endl;
	// 使用string类中从subpos位置开始的sublen个串来赋值
	// string& assign (const string& str, size_t subpos, size_t sublen);
	s1.assign(tmp, 2, 5);
	cout << s1 << endl;
	// 使用字符指针所指向的字符串对其赋值
	// string& assign (const char* s);
	s1.assign("hello naiths");
	cout << s1 << endl;
	// 使用字符指针所指向的字符串的前n个对其赋值
	// string& assign (const char* s, size_t n);
	s1.assign("hello naiths", 7);
	cout << s1 << endl;
	// 使用n个c字符对其赋值
	// string& assign (size_t n, char c);
	s1.assign(10, 'x');
	cout << s1 << endl;
	// 使用迭代器对其赋值
	// template <class InputIterator>
	// string& assign(InputIterator first, InputIterator last);
    s1.assign(tmp.begin(), tmp.end());
    cout << s1 << endl;
    return 0;
}

c++ string,C++,c++,开发语言

        接下来是我们在顺序便中常见的insert与erase接口, insert与erase也有许多重载,此处主要展示最长使用的几个;

c++ string,C++,c++,开发语言

c++ string,C++,c++,开发语言

int main()
{
	string tmp("hello world");
	string s1;
	// 在pos位置插入string类字符串
	// string& insert (size_t pos, const string& str);
	s1.insert(0, tmp);
	cout << s1 << endl;
	// 在pos位置插入str的子串(subpos位置开始的sublen个字符)
	// string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);
	s1.insert(7, tmp, 0, 6);
	cout << s1 << endl;
	// 在pos位置插入字符指针指向的字符串
	// string& insert (size_t pos, constchar* s);
	s1.insert(2, "xxx");
	cout << s1 << endl;
	// 在pos位置插入字符指针指向的字符串的前n个字符
	// string& insert (size_t pos, const char* s, size_t n);
	s1.insert(7, "hello naiths", 8);
	cout << s1 << endl;
	// 在pos位置插入n个c字符
	// string& insert (size_t pos, size_t n, char c);
	s1.insert(0, 5, 'y');
	cout << s1 << endl;
	// 指定迭代器的位置插入n个字符c
	// void insert (iterator p, size_t n, char c);
	string::iterator it = s1.begin() + 10;
	s1.insert(it, 10, 'z');
	cout << s1 << endl;
	// 指定迭代器的位置插入字符c
	// iterator insert (iterator p, char c);
	s1.insert(s1.begin(), 'A');
	cout << s1 << endl;
	// 指定p位置插入迭代器区间的字符
	// template <class InputIterator>
	// void insert(iterator p, InputIterator first, InputIterator last);
	s1.insert(s1.begin(), tmp.begin() + 3, tmp.begin() + 8);
	cout << s1 << endl;
	// 删除pos位置开始的len个字符
	// string& erase (size_t pos = 0, size_t len = npos);
	s1.erase(2, 5);
	cout << s1 << endl;
	// 删除迭代器位置的那个字符
	// iterator erase (iterator p);
	s1.erase(s1.begin());
	cout << s1 << endl;
	// 删除迭代器区间的字符
	// iterator erase (iterator first, iterator last);
	s1.erase(s1.begin() + 2, s1.begin() + 5);
	cout << s1 << endl;
    return 0;
}

        无需将每个接口都记住,但是必须得会使用常用的那几个接口;由于效率原因,建议少用insert、erase接口;以及下面的replace接口(取代);

c++ string,C++,c++,开发语言

        这里的学习与insert与erase类似,此处就不依次示范了;有一些朋友看到这里有swap函数,而我们的算法库也有一个swap函数;这两者有何差异呢?

c++ string,C++,c++,开发语言

 c++ string,C++,c++,开发语言

         既然这里提供swap函数肯定有其意义的,string中的swap效率更高;此处暂时记住,待会在我们模拟实现时会具体分析原因;

(7)其他类型函数

还有一些功能性函数,如下所示;

c++ string,C++,c++,开发语言

c_str与data可认为没有区别,都是返回string类中储存字符串的字符指针;

int main()
{
    string s1("hello world");
    cout << s1.c_str() << endl;
    cout << s1.data() << endl;
    return 0;
}

        copy为从string中拷贝给一个字符数组或字符指针(只能为char*类型),find为再一个字符串中寻找某个字符或字符串,若存在,返回其起始位置; rfind则是从后往前寻找;

c++ string,C++,c++,开发语言

c++ string,C++,c++,开发语言

c++ string,C++,c++,开发语言

int main()
{
	// 从string拷贝给字符数组
	// size_t copy (char* s, size_t len, size_t pos = 0) const;
	char arr[] = "hello world";
	string s1("xxxxxxxxxxxxxxxx");
	s1.copy(arr, 6, 2);
	cout << s1 << endl;
	// 寻找某个字符串的起始位置
	// size_t find (const string& str, size_t pos = 0) const;
	string tmp("abc");
	string s2("abbadabcdeabcd");
	size_t pos1 = s2.find(tmp, 0);
	// 从后往前找
	//size_t rfind (const string& str, size_t pos = npos) const;
	size_t pos2 = s2.rfind(tmp, s2.size() - 1);
	cout << pos1 << endl;
	cout << pos2 << endl;
    return 0;
}

        以下这四个函数前两个为从一个字符中寻找另一个字符串任意一个字符的位置,后两个则为从一个字符串中找任意一个非另一个字符串中的字符的位置;不多说,直接用起来

c++ string,C++,c++,开发语言

int main()
{
	string tmp("acm");
	string s1("This is a program");
	// 从前往后寻找tmp中任意一个字符的位置(参数二不填则默认从第一个位置开始寻找)
	size_t pos1 = s1.find_first_of(tmp, 0);
	// 从后往前寻找tmp中任意一个字符的位置(参数二不填则默认从最后一个位置开始寻找)
	size_t pos2 = s1.find_last_of(tmp, s1.size() - 1);
	// 从前往后寻找任意一个非tmp中的字符的位置(参数二不填则默认从第一个位置开始寻找)
	size_t pos3 = s1.find_first_not_of(tmp, 0);
	// 从后往前寻找任意一个非tmp中的字符的位置(参数二不填则默认从最后一个位置开始寻找)
	size_t pos4 = s1.find_last_not_of(tmp, s1.size() - 1);
	cout << pos1 << endl;
	cout << pos2 << endl;
	cout << pos3 << endl;
	cout << pos4 << endl;
    return 0;
}

         substr则时从一个母串中提取出子串,compare则是比较两个字符串;返回值还是int,比较规则与返回值和之前字符串规则保持一致,按字母ASCII逐个比较,若参数一大于参数二,则返回大于0的数,相等返回0,小于返回小于0的数;

c++ string,C++,c++,开发语言

c++ string,C++,c++,开发语言

int main()
{
	//string substr(size_t pos = 0, size_t len = npos) const;
	string tmp("hello world");
	string s1 = tmp.substr(6, 5);
	cout << s1 << endl;
	string s2("abc");
	string s3("abcc");
	string s4("aac");
	string s5("abc");
	cout << s2.compare(s3) << endl;
	cout << s2.compare(s4) << endl;
	cout << s2.compare(s5) << endl;
    return 0;
}

c++ string,C++,c++,开发语言

(8)非成员函数

 以下是一些与string相关却又不是string成员函数的函数;

c++ string,C++,c++,开发语言

        string对operator+进行了重载,我们可以通过+对拼接两个string或一个string和一个字符字符串常量/字符;但需要注意的是,一定左右参数要有一个string类型;

c++ string,C++,c++,开发语言

int main()
{
	string tmp("bbb");
	string s1("aaa");
	s1 = s1 + 'b';
	cout << s1 << endl;
	string s2 = s1 + tmp;
	cout << s2 << endl;
    return 0;
}

c++ string,C++,c++,开发语言

        ralational operators则重载了一些比较大小的运算符;如下,比较规则与strcmp的比较规则相同;

c++ string,C++,c++,开发语言

        getline函数, 从用户输入中获取一行数据,这也就意味着,当我们我们从用户输入数据中,遇到了空格,我们也会获取空格字符,直到遇到\n为止,这也就是此函数与流插入(>>)最大的区别;

c++ string,C++,c++,开发语言

        string还重载了流插入与流提取,这也就意味着,我们可以像操作自定义类型一样操作string类型,对string进行读入与写出;

c++ string,C++,c++,开发语言文章来源地址https://www.toymoban.com/news/detail-736238.html

到了这里,关于C++ | string用法指南的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++深入浅出】STL之string用法详解

    目录 一. 前言 二. STL概要 2.1 什么是STL 2.2 STL的六大组件 2.3 STL的缺陷 三. string类概述 3.1 什么是string类 3.2 为什么要使用string类 四. string类的使用 4.1 包含头文件 4.2 构造函数 4.3 赋值运算符重载 4.4 容量操作 4.5 访问/遍历操作 4.6 查找修改操作 4.7 子串操作 ​4.8 非成员函数  

    2024年02月05日
    浏览(53)
  • 【C++杂货铺】string使用指南

    2024年02月14日
    浏览(45)
  • 【C++】STL容器——string类的使用指南(含代码演示)(8)

    前言 大家好吖,欢迎来到 YY 滴C++系列 ,热烈欢迎! 本章主要内容面向接触过C++的老铁 主要内容含: 欢迎订阅 YY 滴C++专栏!更多干货持续更新!以下是传送门! YY的《C++》专栏 YY的《C++11》专栏 YY的《Linux》专栏 YY的《数据结构》专栏 YY的《C语言基础》专栏 YY的《初学者易

    2024年02月08日
    浏览(45)
  • 【C++心愿便利店】No.11---C++之string语法指南

    👧个人主页:@小沈YO. 😚小编介绍:欢迎来到我的乱七八糟小星球🌝 📋专栏:C++ 心愿便利店 🔑本章内容:string 记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~ 提示:以下是本篇文章正文内容,下面案例可供参考 C语言中,字符串是以’\\0’结尾的一些字符的集合,为了操作方

    2024年02月05日
    浏览(52)
  • C++ 编程入门指南:深入了解 C++ 语言及其应用领域

    C++ 是一种跨平台的编程语言,可用于创建高性能应用程序。 C++ 是由 Bjarne Stroustrup 开发的,作为 C 语言的扩展。 C++ 为程序员提供了对系统资源和内存的高级控制。 该语言在 2011 年、2014 年、2017 年和 2020 年进行了 4 次重大更新,分别为 C++11、C++14、C++17 和 C++20。 C++ 是世界上

    2024年03月21日
    浏览(51)
  • “C++基础入门指南:了解语言特性和基本语法”

    C++是在C的基础之上,容纳进去了面向对象编程思想,并增加了许多有用的库,以及编程范式 等。熟悉C语言之后,对C++学习有一定的帮助 工作之后,看谁的技术牛不牛逼,不用看谁写出多牛逼的代码,就代码风格扫一眼,立刻就能看出来是正规军还是野生的程序员。代码的风

    2024年02月16日
    浏览(40)
  • 《Qt 6 C++开发指南》提供4个版本的示例程序

    《Qt 6 C++开发指南》包含丰富的示例项目,为了方便读者使用《Qt 6 C++开发指南》学习Qt编程,本书提供了4个版本的示例程序。读者可在人民邮电出版社异步社区本书的配套资源(如图1)里下载这4个版本的示例程序。 图1 异步社区本书配套资源下载界面 这4个版本的关系如图

    2024年02月08日
    浏览(33)
  • 16-1_Qt 5.9 C++开发指南_多语言界面

    有些软件需要开发多语言界面版本,如中文版和英文版,并且在软件里可以方便地切换界面语言。Qt 为多语言界面提供了很好的支持,使用 Qt 的一些规则和工具,可以很方便地为应用程序开发提供多语言界面支持。 用 Qt 开发多语言界面应用程序,主要包括以下几个步骤。

    2024年02月14日
    浏览(47)
  • 09-1_Qt 5.9 C++开发指南_Qchart概述

    Qt Charts 可以很方便地绘制常见的折线图、柱状图、饼图等图表,不用自己耗费时间和精力开发绘图组件或使用第三方组件了。 本章首先介绍 Qt Charts 的基本特点和功能,以画折线图为例详细说明 Qt Charts 各主要部件的操作方法,再介绍各种常用图表的绘图方法,最后介绍鼠标

    2024年02月13日
    浏览(52)
  • 04-2_Qt 5.9 C++开发指南_SpinBox使用

    QSpinBox 用于整数的显示和输入,一般显示十进制数,也可以显示二进制、十六进制的数,而且可以在显示框中增加前缀或后缀。 QDoubleSpinBox 用于浮点数的显示和输入,可以设置显示小数位数,也可以设置显示的前缀或后缀。 实例samp4_3 演示QSpinBox和QDoubleSpinBox这两个组件的使

    2024年02月14日
    浏览(68)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包