【C++】string类(一)构造、重载、容量操作、访问与遍历(迭代器、范围for)、练习
文章目录
- 一、string类介绍
- 二、string构造
- 1、常用3种构造(对应文档1、2、4)
- 2、子字符串构造函数
- 3、拷贝前n个构造
- 4、填充构造
- 三、operator[ ]重载
- 四、string类对象的容量操作
- 1、size、length
- 2、max_size
- 3、capacity
- 4、reserver
- 5、clear
- 6、empty
- 7、shrink_to_fit
- 五、string类对象的访问及遍历(迭代器)
- 1、正向迭代器iterator
- 2、反向迭代器reverse_iterator
- 3、const迭代器const_iterator
- 4、const反向迭代器const_reverse_iterator
- 六、string类对象的访问及遍历(auto和范围for)
- 1、auto
- 2、范围for
- 3、结合使用
- (1)遍历字符串
- (2)遍历数组
- 七、练习
- 1、仅仅反转字母
- 2、字符串中的第一个唯一字符
- 3、字符串相加
- 方法1:尾插,O(n)
- 方法2:头插,O(N^2)
- 八、谢谢观看!
一、string类介绍
以下讲解内容均围绕该文档:string介绍文档
string:管理字符串的类(其底层还是模板)
需要包头文件:< string >
#include <iostream>
#include <string>
using namespace std;
int main()
{return 0;
}
常用接口:
1、构造
2、[]重载
3、容量操作
4、访问与遍历
5、修改
二、string构造
string类的构造函数C++98中有以下7种,其中常用的有三种:无参构造,带参构造,拷贝构造。
注:string类重载了流插入、流提取,可以直接输入输出。
1、常用3种构造(对应文档1、2、4)
#include <iostream>
#include <string>
using namespace std;
int main()
{string s1;//无参构造string s2("hello world");//带参构造string s3(s2);//拷贝构造//cin >> s1;//cout<<s2<<endl;string s4(s2,6,5);//从s2的下标为6的位置开始拷贝,拷贝5个字符存放到s4中。cout<<s4<<endl;//打印结果为worldreturn 0;
}
2、子字符串构造函数
用法介绍:
其中,npos为size_t的最大值。
int main()
{string s2("hello world");//4带参构造//用法1:string s4(s2,6,5);//从s2的下标为6的位置开始拷贝,拷贝5个字符存放到s4中。//用法2:string s5(s2,6,15);//复制的长度为15,越界了,但string会自动从下标6开始拷贝到字符串末尾//用法3:string s6(s2,6);//第三个参数len有缺省值npos,根据用法2,仍然是自动从下标6开始拷贝到字符串末尾cout<<s4<<endl;//打印结果为worldreturn 0;
}
3、拷贝前n个构造
拷贝字符串s的前n个字符,给该字符数组初始化。
int main()
{string s("hello world",5);//拷贝字符串的前5个字符,赋值给scout<<s<<endl;//结果为helloreturn 0;
}
4、填充构造
用n个字符c来填充,初始化该字符数组。
int main()
{string s(5,'a');//用5个'a'来初始化scout<<s<<endl;//结果为aaaaareturn 0;
}
三、operator[ ]重载
重载[ ]之后,我们可以直接通过[]来修改字符数组中的对应字符。能直接检查出来下标的越界。
int main()
{string s("hello world");s[0] = 'x';//s[15] 会越界报错cout<<s<<endl;//结果为xello worldreturn 0;
}
四、string类对象的容量操作
1、size、length
size和length的作用基本上没有区别,均为获取字符数组的长度。
int main()
{string s("hello world");cout<<s.size()<<endl;//结果为11//或 cout<<s.length()<<endl;return 0;
}
2、max_size
获取最大长度,这个接口没啥意义。了解即可
3、capacity
获取空间总大小。编译器为str开了16(15+1)个空间,其中有一个为‘\0’,(capacity返回的容量大小不包含‘\0’)
4、reserver
为字符串预留空间(第一次开的空间大小会大于等于预留的空间)所开的空间中默认不包含‘\0’
提前开空间,提高效率
vs中开空间比预留的大:
情况分析:
reserve(n)
n < size
size < n < capacity
n > capacity
(以VS编译器为例)
void test01()
{string s("hello worldxxxxxxxxxxx");cout << "初始:" << endl;cout << "size:" << s.size() << endl; //22cout << "capacity:" << s.capacity() << endl;//31cout << endl;cout << "n<size时:" << endl;s.reserve(10);//n=10cout << "size:" << s.size() << endl; //22cout << "capacity:" << s.capacity() << endl;//31cout << endl;cout << "size<n<capacity时:" << endl;s.reserve(25);//n=25cout << "size:" << s.size() << endl; //22cout << "capacity:" << s.capacity() << endl;//31cout << endl;cout << "n>capacity时:" << endl;s.reserve(100);//n=100cout << "size:" << s.size() << endl; //22cout << "capacity:" << s.capacity() << endl;//111
}
由上可知,在VS编译器下,当n>capacity时,才会进行扩容,其它情况时,容量不变。
在g++下,其它情况时,容量会缩小至n
5、clear
清空有效字符,不会清除容量。
void test02()
{string s("hello world");cout << "size:" << s.size() << endl;cout << "capacity:" << s.capacity() << endl;s.clear();cout << "size:" << s.size() << endl; cout << "capacity:" << s.capacity() << endl;
}
6、empty
检测字符串是否为空串,是返回true,不是返回false
7、shrink_to_fit
将多余的容量去除,缩小容量至size
五、string类对象的访问及遍历(迭代器)
迭代器分为正向迭代器和反向迭代器。
顾名思义,正向迭代器是正向地从首元素开始迭代。反向迭代器是反向地从末元素开始迭代。
简单来说,迭代器是像指针一样的东西,但又不是指针。所有的容器都可以用它来访问!
迭代器的指向范围是左闭右开!
vector < int >::iterator 其中,vector为容器名
string相关迭代器:
1、正向迭代器iterator
以begin、end为例:
返回第一个位置的迭代器
返回最后一个元素的下一个位置的迭代器
#include <iostream>
#include <string>
using namespace std;
int main()
{string s("hello world");string::iterator it = s.begin();while (it != s.end()){cout << *it;++it;}cout << endl;return 0;
}
2、反向迭代器reverse_iterator
这里以rbegin、rend为例,写法:
#include <iostream>
#include <string>
using namespace std;
int main()
{string str("abcdefg");string::reverse_iterator rit = str.rbegin();while (rit != str.rend()){cout << *rit << " ";++rit;}return 0;
}
3、const迭代器const_iterator
特点:只读,不能修改
begin和end支持const类型(begin和end支持const和非const,而cbegin、cend只返回const迭代器)
const string str("abcdefg");
string::const_iterator cit = str.begin();
//或 auto cit = str.begin(); 使用auto自动推导类型
while(cit != str.end())
{cout<< *cit << " ";++cit;
}
4、const反向迭代器const_reverse_iterator
rbegin、rend支持const类型(crbegin、crend确定返回const迭代器)
const string str("abcdefg");
string::const_reverse_iterator rcit = str.rbegin();
//或 auto rcit = str.rbegin(); 使用auto自动推导类型
while(rcit != str.rend())
{cout<< *rcit << " ";++rcit;
}
六、string类对象的访问及遍历(auto和范围for)
1、auto
- 在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。
- 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
- 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
- auto不能作为函数的参数,可以做返回值,但是建议谨慎使用
- auto不能直接用来声明数组
2、范围for
- 对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。
- for循环后的括号由冒号":"分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束
- 范围for可以作用到数组和容器对象上进行遍历
- 范围for的底层很简单,容器遍历实际就是替换为迭代器。
所有的容器都支持迭代器,迭代器支持范围for,即所有的容器都支持范围for
3、结合使用
当我们需要遍历字符串时,使用auto和范围for非常方便:
(1)遍历字符串
#include <iostream>
#include <string>
using namespace std;
int main()
{string s("hello world");//auto用来判断ch的类型,ch : s 是将s中的每一个元素存入ch中,然后通过cout打印每一个元素for (auto ch : s){cout << ch << " " ;}cout << endl;return 0;
}
(2)遍历数组
#include <iostream>
#include <string>
using namespace std;
int main()
{//遍历数组int arr[] = { 1,2,3,4,5,6,7,8,9 };for (auto e : arr){cout << e << " ";}return 0;
}
七、练习
1、仅仅反转字母
题目链接:仅仅反转字母
class Solution {
public://判断是否为字母bool isChar(char a){if((a>='a'&&a<= 'z')||(a>='A'&&a<='Z'))return true;elsereturn false;}string reverseOnlyLetters(string s) {//前后指针int left = 0,right=s.size()-1;while(left<right){while(left<right && !isChar(s[left]))left++;while(left<right && !isChar(s[right]))right--;swap(s[left++],s[right--]);}return s;}
};
2、字符串中的第一个唯一字符
题目链接:字符串中的第一个唯一字符
class Solution {
public:int firstUniqChar(string s) {//记录出现次数int count[26]={0};//遍历,统计次数for(auto ch : s){count[ch-'a']++;//相对位置的count值++}//返回下标for(int i=0;i<s.size();i++){//s[i]-'a'为该字符在count数组中的相对位置if(count[s[i]-'a']==1)return i;}return -1;//未找到}
};
3、字符串相加
题目链接:字符串相加
方法1:尾插,O(n)
reverse用法
reverse 封装在< algorithm >中,参数为begin()迭代器和end()迭代器,左闭右开
作用:将该字符串进行逆置
class Solution {
public:string addStrings(string num1, string num2) {int end1 = num1.size()-1,end2 = num2.size()-1;string str;//存放加完后的字符串//进位int next = 0;while(end1>=0 || end2>=0)//当两个字符串都结束时,才结束{int val1=end1>=0 ? num1[end1--]-'0' : 0; //当该字符串结束时,对应位 置为0,后进行相加int val2=end2>=0 ? num2[end2--]-'0' : 0;//和int ret = val1+val2+next;next = ret/10;//进位ret %= 10;//对应位应放的值//尾插str+=(ret+'0');}//处理最高位进位if(next==1)str+='1';//逆置reverse(str.begin(),str.end());return str;}
};
方法2:头插,O(N^2)
该方法的大致思路与尾插相似,只是结果插入str时进行的是头插,最终结果不必进行逆置
class Solution {
public:string addStrings(string num1, string num2) {int end1 = num1.size()-1,end2 = num2.size()-1;string str;//存放加完后的字符串//进位int next = 0;while(end1>=0 || end2>=0)//当两个字符串都结束时,才结束{int val1=end1>=0 ? num1[end1--]-'0' : 0; //当该字符串结束时,对应位 置为0,后进行相加int val2=end2>=0 ? num2[end2--]-'0' : 0;//和int ret = val1+val2+next;next = ret/10;//进位ret %= 10;//对应位应放的值//头插str.insert(str.begin(),ret+'0');}//处理最高位进位if(next==1)str.insert(str.begin(),'1');return str;}
};