当前位置:   article > 正文

C++--6.string类_c++6 += string

c++6 += string
为什么学习 string 类?
标准库中的 string
string 类的模拟实现

为什么学习string类?

C 语言中的字符串
C 语言中,字符串是以 '\0' 结尾的一些字符的集合,为了操作方便, C 标准库中提供了一些 str 系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP 的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。

标准库中的string

string

1. 字符串是表示字符序列的类
2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
3. string 类是使用 char( 即作为它的字符类型,使用它的默认 char_traits 和分配器类型 ( 关于模板的更多信息,请参阅basic_string)
4. string 类是 basic_string 模板类的一个实例,它使用 char 来实例化 basic_string 模板类,并用 char_traits和allocator 作为 basic_string 的默认参数 ( 根于更多的模板信息请参考 basic_string)
5. 注意,这个类独立于所使用的编码来处理字节 : 如果用来处理多字节或变长字符 ( UTF-8) 的序列,这个类的所有成员( 如长度或大小 ) 以及它的迭代器,将仍然按照字节 ( 而不是实际编码的字符 ) 来操作。
总结:
1. string 是表示字符串的字符串类
2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作 string 的常规操作。
3. string 在底层实际是: basic_string 模板类的别名, typedef basic_string<char, char_traits, allocator>
string;
4. 不能操作多字节或者变长字符的序列

string类的常用接口说明

string 类对象的常见构造

 下来我们对其进行实现展示

  1. #include<iostream>
  2. #include<string>//string的包
  3. #include<Windows.h>
  4. using namespace std;
  5. //4个默认的成员函数
  6. void test_string1()
  7. {
  8. string s1;//构造空的string类对象,即空字符串,s1
  9. string s2("hello");//用C-string来构造string类对象s2
  10. string s3("hello", 2);//拷贝前两个字符赋给s3
  11. string s4(s2);//拷贝构造函数
  12. string s5(s2, 1, 8);//提取s218位置的字符,字符小于8则输出剩下全不自负
  13. string s6(s2, 1, string::npos);//提取从1到字符末尾
  14. string s7(10,'a');//构造10个a
  15. cout << "s1: " << s1 << endl;
  16. cout << "s2: " << s2 << endl;
  17. cout << "s3: " << s3 << endl;
  18. cout << "s4: " << s4 << endl;
  19. cout << "s5: " << s5 << endl;
  20. cout << "s6: " << s6 << endl;
  21. s1 = s7;
  22. cout << "s7: " << s7 << endl;
  23. cout << "s1: " << s1 << endl;
  24. }
  25. int main()
  26. {
  27. test_string1();
  28. system("pause");
  29. return 0;
  30. }

 string类对象的访问及遍历操作

string类对象的方式问方式大体可以分为3种operator[]+下标,我们使用最多的访问方式,迭代器访问方式,范围for()访问方式,下面我们来一一介绍

operator[]+下标

  1. void test_string2()//[]+下标的遍历方式,推荐用法
  2. {
  3. string s1("hello");
  4. s1 += ' ';
  5. s1 += "world";
  6. cout << s1 << endl;
  7. //
  8. for (size_t i = 0; i < s1.size(); i++)//对字符+1后移1个字符
  9. {
  10. s1[i] += 1;
  11. }
  12. //
  13. for (size_t i = 0; i < s1.size(); i++)
  14. {
  15. cout << s1[i] << " ";
  16. }
  17. cout << endl;
  18. }

这种方式基本就是我们在数组操作字符串时进行遍历读写的操作,这个操作输出了一个hello world,而后我们对于字符遍历进行了+1操作,再次遍历输出,得到的结果是i f m m p  x p s m e对所有的字符进行了+1,在进行遍历输出

 []+下标的方式也是我们最为熟悉的

迭代器

字符迭代器是一种相对模板化的一种字符在操作方式格式为 string::iterator it = s1.begin();类似与这样,当然,如果我们想调用其他的迭代器比如vector,只需要将string换为vector,而后后面再<类型>即可

  1. //迭代器
  2. //
  3. string::iterator it = s1.begin();
  4. //auto it = s1.begin();也可以套用这种方式,之前学习的auto自动判断类型
  5. while (it != s1.end())
  6. {
  7. *it -= 1;
  8. ++it;
  9. }
  10. //
  11. it = s1.begin();
  12. while (it != s1.end())
  13. {
  14. cout << *it << " ";
  15. ++it;
  16. }
  17. cout << endl;
  18. }

因为我们刚在上面改变了hello world的字符,所以我们在这里用迭代器先将其改回来--对*it-=1,对每个字符-1,而后++it遍历,完成整个字符的-1,而后遍历字符串,进行输出

我们可以看到迭代器中的操作,很像指针,暂时我们可以将其当做指针

不过这只是最普通最常用的一种迭代器,还有三种迭代器

  1. void test_string3()
  2. {
  3. string s1("hello world");
  4. //倒着遍历
  5. string::reverse_iterator rit = s1.rbegin();
  6. while (rit != s1.rend())
  7. {
  8. cout << *rit << " ";
  9. ++rit;
  10. }
  11. string num("12345");
  12. cout << string2int(num) << endl;
  13. }

这种是reverse_iterator倒序迭代器,不过我们操作还是++进行操作的,内部自动转化为倒序

 这段代码利用倒序迭代器完成了将hello world,与12345倒序输出的功能

还有const修饰的迭代器,无法对原来的数据进行改变

  1. int string2int(const string& str)
  2. {
  3. int val = 0;
  4. //只能读,不能写
  5. string::const_iterator it = str.begin();
  6. while (it != str.end())
  7. {
  8. val *= 10;
  9. val += (*it - '0');
  10. ++it;
  11. }
  12. //val = 0;
  13. /*string::const_reverse_iterator rit = str.rbegin();
  14. while (rit != str.rend())
  15. {
  16. val *= 10;
  17. val += (*rit - '0');
  18. ++rit;
  19. }*/
  20. return val;
  21. int main()
  22. {
  23. string num("12345");
  24. //test_string2();
  25. //test_string3();
  26. cout << string2int(num) << endl;;
  27. system("pause");
  28. return 0;
  29. }

这段代码完成了字符串对于int型的转化,正着转化和倒着转化

从顺序看迭代器分为两种,正序和倒序,从属性来看,迭代器分为const和普通,自由组合一共4种

范围for

  1. 范围for
  2. C++11->原理其实是迭代器
  3. for (auto ch : s1)
  4. {
  5. cout << ch << " ";
  6. }
  7. cout << endl;
  8. }

范围for其实就是用我们从前的知识,对s1进行依次遍历,不过这种方式底层原理也是迭代器,这便是三种遍历string的方式

string类对象的容量操作

 这些是基本的一些字符串操作函数,下面我们对capacity()进行剖析

 我们可以看到,当我们不断地去加入字符时,容量的大小变化,从0开始,差不多是每次变化1.5倍左右进行扩大得到,我们还可以推出,到了后面增容会越来越慢,随着基数的增大每次扩大的容量也会增加,而且增容是有代价的,不能频繁去增加

 下面我们再来了解一个关键字,reserve,这个关键字的含义是直接开辟定量的空间

 此时我们会发现,就没有扩容了,直接将容量增到了100,其实我们在开辟时,显示的是开辟了100个空间,实际上是101个,因为还会腾出一个空间去给\0

在了解reserve后,我们有个与之相对应的resize()

 这种是仅有一个int参数的情况,起到的作用就是开辟了100个字符串这种是reserve重载的方式,开辟了100个字符串并赋值

 那么我们再来看下这种情况

 当我们reserve了5个大小的空间,但是字符串不够放进去,所以就只有前五个放进去了,而后我们进行开辟20个,放入x,也不会再将world放入,全为x

 这是我们的字符串插入操作,两种都可以完成操作,不过推荐第二种,方便可读

1. size() length() 方法底层实现原理完全相同,引入 size() 的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()
2. clear() 只是将 string 中有效字符清空,不改变底层空间大小。
3. resize(size_t n) resize(size_t n, char c) 都是将字符串中有效字符个数改变到 n 个,不同的是当字符个数增多时:resize(n) 0 来填充多出的元素空间, resize(size_t n, char c) 用字符 c 来填充多出的元素空间。注意:resize 在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
4. reserve(size_t res_arg=0) :为 string 预留空间,不改变有效元素个数,当 reserve 的参数小于string的底层空间总大小时, reserver 不会改变容量大小。

 这是我们的插入删除

 还有一个小小的接口,c_str(),获取字符数组首地址,用C形式遍历,其实最主要的差距就是中间有无\0,对于C++的遍历方式而言,遇到\0并不会停止遍历,而对于C语言,碰到\0就停止了

 还有一个substr(pos)函数,访问从pos位到结束的所有子串,以及我们的find('s')函数,找到第一个与s相同的字符

1. string 尾部追加字符时, s.push_back(c) / s.append(1, c) / s += 'c' 三种的实现方式差不多,一般情况下string 类的 += 操作用的比较多, += 操作不仅可以连接单个字符,还可以连接字符串。
2. string 操作时,如果能够大概预估到放多少字符,可以先通过 reserve 把空间预留好。
  1. void Teststring()
  2. {
  3. string str;
  4. str.push_back(' '); // 在str后插入空格
  5. str.append("hello"); // 在str后追加一个字符"hello"
  6. str += 'b'; // 在str后追加一个字符'b'
  7. str += "it"; // 在str后追加一个字符串"it"
  8. cout << str << endl;
  9. cout << str.c_str() << endl; // 以C语言的方式打印字符串
  10. // 获取file的后缀
  11. string file1("string.cpp");
  12. size_t pos = file.rfind('.');
  13. string suffix(file.substr(pos, file.size() - pos));
  14. cout << suffix << endl;
  15. // npos是string里面的一个静态成员变量
  16. // static const size_t npos = -1;
  17. // 取出url中的域名
  18. sring url("http://www.cplusplus.com/reference/string/string/find/");
  19. cout << url << endl;
  20. size_t start = url.find("://");
  21. if (start == string::npos)
  22. {
  23. cout << "invalid url" << endl;
  24. return;
  25. }
  26. start += 3;
  27. size_t finish = url.find('/', start);
  28. string address = url.substr(start, finish - start);
  29. // 删除url的协议前缀
  30. pos = url.find("://");
  31. url.erase(0, pos + 3);
  32. cout << url << endl;
  33. }

这是string一些接口的常用用法

还有一个需要我们注意的点是cin与getline,cin遇到空格或者换行都会停下来,而我们的getline只遇到换行会停下,空格不会,这在需要输入一些带空格的字符串时很有用

 String的模拟实现

在这里我们对String类进行模拟实现,加深我们对其的理解

构造函数

我们之前在其它类中定义构造函数的时候是怎么定义的呢?我们在这里试一下

 我们会发现,一般的写法对于读取字符串是可行的,但当我们想利用[ ]去改变它时,就会出现报错,原因是我们的s1存在栈中,去调用这个构造函数时会指向存在代码区的“hello”,而我们的operator[ ]返回的是字符串本身第i个位置的字符,因为存在代码段的字符串无法被修改,所以在我们想去修改字符串时会报错

所以我们就不能把字符串存储在常量区中,在这里我们选择堆,因为堆可以自行开辟大小

 而我们若想建立一个空对象,也不能直接取用nullptr,因为strlen时出现了空指针,找不到\0,所以我们在建立空对象时需要在堆上开辟一个大小的空间,附上\0

下面我们展示全缺省合并写法

 析构函数

析构函数就正常的资源清理与指针指控即可

 拷贝构造

我们先来试试如果我们不写拷贝构造会发生什么呢?

 我们先对c_str进行了重载,随后拷贝构造了s2,发现也可以实现拷贝构造,但是程序给崩溃了,这是什么原因呢,其实当我们没有重载拷贝构造时,系统自带的拷贝构造就会完成浅拷贝,也就是在栈中将s1中存的字符串地址按字节将值拷贝到同在栈中的s2,此时s1,s2指向堆中的同一个字符串,这里没有什么问题,但是最后在析构的时候,会对s1,s2分别进行析构,又因为他们指向同一地址空间,所以同一块空间被析构了两次,引起报错

接下来看看正确做法

 正确的做法就是在堆中在开辟一块一模一样大的空间,再将值拷进去,进行深拷贝

赋值

我们用一般的方法进行赋值

 发现同样的,会引起崩溃,这与上面的拷贝构造是一样的,系统自动浅拷贝,会在最后析构的时候出现问题,所以也需要我们对其进行重载

 具体做法就是在堆中开辟一块与s3相同的tmp空间,将s3的拷贝给这个tmp,再将原来的s1空间释放掉,再将s1指向tmp,就完成了赋值

下面我们展示代码

  1. namespace wxy
  2. {
  3. class string
  4. {
  5. public:
  6. /*string()
  7. :_str(nullptr)//而我们在调用无参时也不能这样调,会有空指针的问题,无法找到\0结束
  8. {}*/
  9. /* string(char* str)
  10. :_str(str)//不能这么写,因为string对象中存的是指针,指针指向的数组中存储字符,字符无法修改
  11. {}*/
  12. //string()//可行方案
  13. // :_str(new char[1])
  14. //{
  15. // _str[0] = '0';
  16. //}
  17. //string(char* str)//将空间开辟在堆上,str+1是为了给\0空间
  18. // :_str(new char[strlen(str + 1)])//开辟一个str+1大小的新空间,而后用strcpy函数将在常量区的str拷到堆区的_str中
  19. //{
  20. // strcpy(_str, str);
  21. //}
  22. //将他们两个合并
  23. string(char* str = " ")
  24. :_str(new char[strlen(str) + 1])
  25. {
  26. strcpy(_str, str);
  27. }
  28. ~string()
  29. {
  30. delete[] _str;
  31. _str = nullptr;
  32. }
  33. //string s2(s1)
  34. string(const string& s)
  35. :_str(new char[strlen(s._str)+1])
  36. {
  37. strcpy(_str, s._str);
  38. }
  39. size_t size()
  40. {
  41. return strlen(_str);
  42. }
  43. char& operator[](size_t i)
  44. {
  45. return _str[i];
  46. }
  47. const char* c_str()
  48. {
  49. return _str;
  50. }
  51. //s1=s3
  52. //s1=s1
  53. string& operator=(const string& s)
  54. {
  55. if (this != &s)
  56. {
  57. char* tmp = new char[strlen(s._str) + 1];
  58. strcpy(tmp, s._str);
  59. delete[] _str;
  60. _str = tmp;
  61. return *this;
  62. }
  63. }
  64. private:
  65. char* _str;
  66. };
  67. void test_string2()
  68. {
  69. string s1("hello");
  70. string s2(s1);
  71. /*cout << s1.c_str() << endl;
  72. cout << s2.c_str() << endl;*/
  73. string s3("world");
  74. s1 = s3;
  75. cout << s1.c_str() << endl;
  76. cout << s3.c_str() << endl;
  77. }
  78. }
  79. void test_string1()
  80. {
  81. string s1("hello");
  82. string s2;
  83. for (size_t i = 0; i < s1.size(); ++i)
  84. {
  85. s1[i] += 1;//
  86. cout << s1[i] << " ";
  87. }
  88. cout << endl;
  89. for (size_t i = 0; i < s2.size(); ++i)
  90. {
  91. s1[i] += 1;//
  92. cout << s2[i] << " ";
  93. }
  94. cout << endl;
  95. }

我们的深拷贝其实可以有一种现代写法

  1. //深拷贝-现代写法
  2. string(const string& s)
  3. :_str(nullptr)
  4. {
  5. string tmp(s._str);
  6. swap(_str, tmp._str);
  7. }
  8. //s1=s2
  9. string& operator=(const string& s)
  10. {
  11. if (this != &s)
  12. {
  13. string tmp(s);
  14. swap(_str, tmp._str);
  15. }
  16. return *this;
  17. }
  18. //最简单版本
  19. string& operator=(const string& s)
  20. {
  21. swap(_str, s._str);
  22. return *this;
  23. }

 说明:上述string类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝

 如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。

接下来我们想实现一个更加复杂的string类

  1. namespace wxy
  2. {
  3. class string
  4. {
  5. public:
  6. typedef char* iterator;//迭代器
  7. iterator begin()//迭代器起点
  8. {
  9. return _str;
  10. }
  11. iterator end()//迭代器终点
  12. {
  13. return _str + _size;
  14. }
  15. string(const char* str = " ")//构造函数,堆上开辟空间将字符串拷进去
  16. {
  17. _size = strlen(str);
  18. _capacity = _size;
  19. _str = new char[_capacity + 1];
  20. strcpy(_str, str);
  21. }
  22. ~string()//析构函数
  23. {
  24. delete[] _str;
  25. _str = nullptr;
  26. _size = _capacity = 0;
  27. }
  28. size_t size()const//计算有效字符个数
  29. {
  30. return _size;
  31. }
  32. size_t capacity()const//计算字符串容量,\0不算
  33. {
  34. return _capacity;
  35. }
  36. char& operator[](size_t i)//[]重载
  37. {
  38. assert(i < _size);
  39. return _str[i];
  40. }
  41. const char& operator[](size_t i)const
  42. {
  43. assert(i < _size);
  44. return _str[i];
  45. }
  46. const char* c_str()//c语言形式输出字符
  47. {
  48. return _str;
  49. }
  50. void reserve(size_t n)//开空间函数
  51. {
  52. if (n > _capacity)//当要开的空间大于当前总容量时
  53. {
  54. char* newstr = new char[n + 1];//堆上开辟n+1
  55. strcpy(newstr, _str);//原字符串拷进堆
  56. delete[] _str;//析构原字符串
  57. _str = newstr;//堆上开的新空间赋回字符串
  58. _capacity = n;//容量改为n
  59. }
  60. }
  61. void resize(size_t n, char ch = '\0')//改有效字符个数
  62. {
  63. if (n < _size)//当要改的有效字符个数小于当前有效字符个数
  64. {
  65. _str[n] = '\0';//将第n号下标元素改为\0,系统会自动识别到\0,剩下的就不识别了
  66. _size = n;//有效字符个数改为n
  67. }
  68. else
  69. {
  70. if (n > _capacity)//当有效字符个数大于整个容量
  71. {
  72. reserve(n);//开空间到n
  73. }
  74. for (size_t i = _size; i < n; ++i)
  75. {
  76. _str[i] = ch;//将剩下空出来的位置赋成字符ch,缺省时为\0
  77. }
  78. _size = n;//有效字符个数改为n
  79. _str[_size] = "\0";//最后一个位置上改为\0,好让系统识别结束
  80. }
  81. }
  82. void push_back(char ch)//尾插字符
  83. {
  84. //空间满,进行增容
  85. if (_size == _capacity)
  86. {
  87. size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;
  88. reserve(newcapacity);
  89. }
  90. _str[_size] = ch;
  91. ++_size;
  92. _str[_size] = '\0';
  93. }
  94. void append(const char* str)//尾插字符串
  95. {
  96. size_t len = strlen(str);//计算字符串长度
  97. if (_size + len > _capacity)//当最终插入后的字符串长度大于容量
  98. {
  99. reserve(_size + len);//扩容到应该的长度
  100. }
  101. strcpy(_str + _size, str);//将字符拷贝到增容后的空间
  102. _size += len;//有效字符个数变为len
  103. }
  104. //s1+='a'
  105. string& operator+=(char ch)//+=重载
  106. {
  107. this->push_back(ch);
  108. return *this;
  109. }
  110. //s1+="aaaa"
  111. string& operator+=(const char* str)
  112. {
  113. this->append(str);
  114. return *this;
  115. }
  116. string& intsert(size_t pos, char ch)//插入字符
  117. {
  118. assert(pos < _size);//当插入位置小于有效字符个数断言
  119. if (_size == _capacity)//容量不够,扩容
  120. {
  121. size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;
  122. reserve(newcapacity);
  123. }
  124. int end = _size;//size开始移动
  125. while (end >= pos)//从后往前循环全部后移1
  126. {
  127. _str[end + 1] = _str[end];
  128. --end;
  129. }
  130. _str[pos] = ch;//腾出来的位置插入ch
  131. ++_size;//有效字符个数+1
  132. }
  133. string& insert(size_t pos, const char* str)
  134. {
  135. assert(pos < _size);
  136. size_t len = strlen(str);//计算字符串长度
  137. if (_size + len > _capacity)
  138. {
  139. reserve(_size + len);
  140. }
  141. int end = _size;
  142. while (end >= pos)
  143. {
  144. _str[end + len] = _str[end];//全部后移len位
  145. --end;
  146. }
  147. /*for (size_t i = 0; i < len; ++i)
  148. {
  149. _str[pos] = str[i++];
  150. }*/
  151. strncpy(_str + pos, str, len);//拷贝len位
  152. _size += len;
  153. }
  154. void erase(size_t pos, size_t len = npos)//删除pos开始之后长为len的字符串
  155. {
  156. assert(pos < _size);
  157. if (len >= _size - pos)//字符串长度大于等于有效字符长度-删除的位置
  158. {
  159. _str[pos] = '\0';//删除的位置变为\0
  160. _size = pos;//有效长度变为前面没删的部分
  161. }
  162. else//当有效字符长度大于字符串长度+pos时
  163. {
  164. size_t i = pos + len;//删除位置+字符串长度=删后字符串长度i,i后数据要挪到前面
  165. while (i <= _size)
  166. {
  167. _str[i-len] = _str[i];//将i后数据挪到前len位置
  168. ++i;
  169. }
  170. _size -= len;//最终长度
  171. }
  172. }
  173. size_t find(char ch, size_t pos = 0)//从pos开始寻找ch
  174. {
  175. for (size_t i = pos; i < _size; i++)//
  176. {
  177. if (_str[i] == ch)
  178. {
  179. return i;//找到
  180. }
  181. }
  182. return npos;//未找到
  183. }
  184. size_t find(const char* str, size_t pos = 0)
  185. {
  186. char* p = strstr(_str, str);//在_str中寻找str
  187. if (p == nullptr)//未找到
  188. {
  189. return npos;
  190. }
  191. else
  192. {
  193. return p-_str;//返回
  194. }
  195. }
  196. bool operator<(const string& s)
  197. {
  198. int ret = strcmp(_str, s._str);//s与_str比较,_str大返回>0,相等=0,小<0
  199. return ret < 0;
  200. }
  201. bool operator==(const string& s)
  202. {
  203. int ret = strcmp(_str, s._str);
  204. return ret == 0;
  205. }
  206. bool operator<=(const string& s)
  207. {
  208. return *this<s || *this == s;
  209. }
  210. bool operator>(const string& s)
  211. {
  212. return !(*this <= s);
  213. }
  214. bool operator>=(const string& s)
  215. {
  216. return !(*this < s);
  217. }
  218. bool operator!=(const string& s)
  219. {
  220. return !(*this == s);
  221. }
  222. private:
  223. char* _str;
  224. size_t _size;//已有多少个有效字符
  225. size_t _capacity;//可以存储多少个有效字符 \0不是有效字符
  226. static size_t npos;
  227. };
  228. size_t string::npos = -1;
  229. //getlin遇到空格不结束
  230. istream& operator>>(istream& in, string& s)
  231. {
  232. while (1)
  233. {
  234. char ch;
  235. //in >> ch;
  236. ch = in.get();
  237. if (ch == ' ' || ch == '\n')
  238. {
  239. break;
  240. }
  241. else{
  242. s += ch;
  243. }
  244. }
  245. }
  246. ostream& operator<<(ostream& out,const string& s)
  247. {
  248. for (size_t i = 0; i < s.size(); ++i)
  249. {
  250. cout << s[i];
  251. }
  252. return out;
  253. }
  254. void test_string1()
  255. {
  256. string s1;
  257. string s2("hello");
  258. string::iterator it2 = s2.begin();
  259. while (it2 != s2.end())
  260. {
  261. cout << *it2 << " ";
  262. ++it2;
  263. }
  264. cout << endl;
  265. //范围for,本质迭代器
  266. for (auto e : s2)
  267. {
  268. cout << e << " ";
  269. }
  270. cout << endl;
  271. }
  272. }

 对于string的学习还需要在不断地练习中精进

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/490452
推荐阅读
相关标签
  

闽ICP备14008679号