string 介绍
1、 标准库中的string类
1.1 对string类的理解
1.2 auto 和 范围 for
auto 关键字
1、在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。
2、用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&(重点)
3、当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
4、auto不能作为函数的参数,可以做返回值,但是建议谨慎使用
5、auto不能直接用来声明数组
实现:
#include<iostream>
using namespace std;
int func1()
{
return 1;
}
//void func2(auto a)//这里的auto关键字不能作为参数出现
//但是可以用auto当作返回值
auto func3()
{
return 2;
{
int main()
{
int a=1;
auto b=a;//auto 会自动推导a的类型
auto c='a';
auto d =func1();
//auto e;//auto定义的类型必须有初始值,否则这里的auto会报错
//typeid().name()可以打印类型
cout<<typeid(b).name()<<endl;
cout<<typeid(c).name()<<endl;
cout<<typeid(d).name()<<endl;
int x= 10;
auto y=&x;
auto* z=&x;
auto& m= x;
cout<<typeid(x).name()<<endl;
cout<<typeid(y).name()<<endl;
cout<<typeid(z).name()<<endl;
auto aa=1,bb=2;//这是被允许的他们类型一致
auto cc=3,dd='a'; 编译报错:error C3538: 在声明符列表中,“auto”必须始终推导为同一类型
auto array[]={3,4,4,5};// 编译报错:error C3318: “auto []”: 数组不能具有其中包含“auto”的元素类型
}
auto的实战效果
#include<iostream>
#include<strinig>
#include<map>
using namespace std;
int main()
{
std:: map<std::string,std::string> dict= { { "apple", "苹果" },{ "orange",
"橙子" }, {"pear","梨"} };
//std::map<std::string,std::string>::iterator it =dict.begin();
//这里的iterator是map的迭代器,it前面是类型,那么这时我们就可以用auto来代替他
auto it = dict.begin();
while (it != dict.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}
}
但是为此牺牲了一定的可读性!!!!
范围 for
1、对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。 (范围for的格式)
2、范围for可以作用到数组和容器对象上进行遍历(作用)
3、范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。(介绍)
#include<iostream>
#include <string>
#include <map>
using namespace std;
int main()
{
int array[] = { 1, 2, 3, 4, 5 };
// C++98的遍历
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)//下标访问
{
array[i] *= 2;
}
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
{
cout << array[i] << endl;
}
// C++11的遍历
for (auto& e : array)//范围for访问(引用)类型
e *= 2;
for (auto e : array)
cout << e << " " << endl;
string str("hello world");
for (auto ch : str)//普通范围for
{
cout << ch << " ";
}
cout << endl;
return 0;
}
1.3string类的常用接口说明
- string类对象的常见构造
(constructor)函数名称 | 功能说明 |
---|---|
string()(常用) | 构造空的string类对象,即空字符串 |
string(const char* s) (常用) | 用C-string来构造string类对象 |
string(size_t n, char c) | string类对象中包含n个字符c |
string(const string&s) (重点) | 拷贝构造函数 |
void test1()
{
strnig s1;//造空的string类对象,即空字符串
string s2("hello world");//用C-string来构造string类对象
stirng s3(s2);//|拷贝构造函数
{
底层:
sting()、string(cons char* s)、string(const string&s)
class string
{
public:
string()
:_str(nullptr)
,_size(0);
,_capacity(0);
{}
string(const char* s)
{
_size=strlen(s);
_size=_capacity;
_str=new char[strlen(s)+1];
strcpy(_str,s);
}
string(const string&s)
{
_str=new char[s.capacity+1];
_size=s._size;
_capacity=s._capacity;
strcpy(_str,s._str);
}
private:
char* _str;
size_t _size;
size_t _capacity;
}
运用:
- string类对象常见的容量操作
函数名称 | 功能说明 |
---|---|
size(重点)链接: size | 返回字符串有效字符长度 |
length 链接: length | 返回字符串有效字符长度 |
capacity 链接: capacity | 返回空间总大小 |
empty (重点)链接: empty | 检测字符串释放为空串,是返回true,否则返回false |
clear (重点)链接: clear | 清空有效字符 |
reserve (重点)链接: reserve | 为字符串预留空间 |
resize (重点)链接: resize | 将有效字符的个数该成n个,多出的空间用字符c填充 |
链接: 演示代码
size:
length:
capacity:
empty:
判断是否为空,是返回true,否则返回false
clear:
清除字符串的有效空间,size变为0,但是capacity还是不变
reverse:
resize:
功能进一步诠释:
- size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接 口保持一致,一般情况下基本都是用size()。
- clear()只是将string中有效字符清空,不改变底层空间大小。
- resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不 同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char
c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数
增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。- reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参 数小于string的底层空间总大小时,reserver不会改变容量大
- string类对象常见的访问及遍历操作
函数名称 | 功能说明 |
---|---|
operator[] (重点)链接: operator | 返回pos位置的字符,const string类对象调用 |
begin+ end 链接: begin+end | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 |
rbegin + rend链接: rbegin+rend | rbegin获取最后一个字符的下一个迭代器 + rend获取第一个字符的迭代器 |
链接: strig中元素访问及遍历
4.string类对象常见的修改操作
需要注意的是:
- 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c’三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
- 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留
好。
函数名称 | 功能说明 |
---|---|
push_back 链接: push_back | 在字符串后尾插字符c |
append 链接: append | 在字符串后追加一个字符串 |
operator+= (重点) 链接: operator+= | 在字符串后追加字符串 |
c_str(重点)链接: c_str | 返回C格式字符串 |
find链接: find | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
rfind链接: rfind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
substr链接: substr | 在字符串中pos位置开始,截取n个字符,然后将其返回 |
链接: strig类对象的修改
push_back:
append:
operator+=:
字符串追加用法与appen、 push_back 差不多
c_str:
find:
rfind:
与find类似只不过rfind是从后往前
substr:
5.string类非成员函数
函数名称 | 功能说明 |
---|---|
operator+链接: operator+ | 字符相加(因为是传值返回,避免不了拷贝构造,尽量少用) |
operator>>链接: operator>> | >>(输入运算符重载) |
operator<< 链接: operator<< | <<(输出运算符重载) |
getline链接: getline | 获取一行字符串(相比输入运算符重载,getline可以控制结束的符号,可以说比cin更灵活) |
relational operators 链接: relational operators | 大小比较 |
练习
反转字符串
思路简单:找头和尾在依次交换
class Solution {
public:
bool isLetter(char ch)
{
if(ch >= 'a' && ch <= 'z')
return true;
if(ch >= 'A' && ch <= 'Z')
return true;
return false;
}
string reverseOnlyLetters(string S) {
if(S.empty())
return S;
size_t begin = 0, end = S.size()-1;
while(begin < end)
{
while(begin < end && !isLetter(S[begin]))
++begin;
while(begin < end && !isLetter(S[end]))
--end;
swap(S[begin], S[end]);
++begin;
--end;
}
return S;
}
};
找字符串第一个只出现一个的字符
class Solution {
public:
int firstUniqChar(string s) {
// 统计每个字符出现的次数
int count[256] = {0};
int size = s.size();
for(int i = 0; i < size; ++i)
count[s[i]] += 1;
// 按照字符次序从前往后找只出现一次的字符
for(int i = 0; i < size; ++i)
if(1 == count[s[i]])
return i;
return -1;
}
};
思路:通过数组来确定字符出现的次数,在遍历数组,得出只出现一次的第一次字符
字符串里面最后一个单词的长度
运用到getline来代替cin输入字符串,因为cin>>与scanf类似遇到字符空格(‘ ’)就会结束,而getline则不会
getline(cin,line);最后再用 总数-1-pos=最后一个单词长度
#include<iostream>
#include<string>
using namespace std;
int main()
{
string line;
// while(cin>>line)
while(getline(cin, line))
{
size_t pos = line.rfind(' ');
cout<<line.size()-pos-1<<endl;
}
return 0;
}