
🎬 博主名称 :键盘敲碎了雾霭
🔥 个人专栏 : 《C语言》《数据结构》 《C++》
⛺️指尖敲代码,雾霭皆可破

文章目录
- 一、string类介绍
- 二、auto关键字
-
- [2.1 特点](#2.1 特点)
- [2.2 范围for](#2.2 范围for)
- [三、 string类的常用接口](#三、 string类的常用接口)
-
- [3.1 常见构造(constructor)函数](#3.1 常见构造(constructor)函数)
- [3.2 迭代器](#3.2 迭代器)
- [3.3 遍历string的方法](#3.3 遍历string的方法)
- [3.4 容量](#3.4 容量)
-
- [3.4.1 **VS的扩容方式**](#3.4.1 VS的扩容方式)
- [3.4.2 reserve](#3.4.2 reserve)
- [3.5 其他接口](#3.5 其他接口)
- [3.5 元素访问](#3.5 元素访问)
- 四、练习
-
- [4.1 仅仅反转字母](#4.1 仅仅反转字母)
- [4.2 字符串中的第一个唯一字符](#4.2 字符串中的第一个唯一字符)
- [4.3 验证一个字符串是否是回文串](#4.3 验证一个字符串是否是回文串)
- [4.4 字符串相加](#4.4 字符串相加)
- 文章结语
一、string类介绍
C语言中,字符串是以\O'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。(简单来说,字符串用类管理方便一写)
在使用string类时,必须包含#include头文件以及using namespace std;

二、auto关键字
2.1 特点
在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得
- 可以用来简化长类型
cpp
map<string, string> dit;
//map<string, string>::iterator mit = dit.begin();
cout << typeid(dit).name() << endl;
auto mit = dit.begin();
- 必须具有初始值设定项
cpp
//auto e;//必须使其初始化,
- 数组不能具有其中包含"auto"的元素类型
cpp
数组不能具有其中包含"auto"的元素类型
- auto不能作为函数的参数,可以做返回值,但是建议谨慎使用(不方便,需要一层一层来看)
cpp
void fuc(auto x)
{
}
2.2 范围for
对于一个有范围的集合 而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号":"分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。
- 范围for可以作用到数组和容器对象上进行遍历
cpp
int arr[] = { 1,2,3,4,5,6,7,8 };
for (auto a : arr)//要改变值加引用
{
cout << a << " ";
}
cout << endl;
- 范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。
三、 string类的常用接口
3.1 常见构造(constructor)函数

cpp
string s1;
string s2("hellow world");
string s3(s2);
string s4(s2, 5, 6);
string s5(s2, 2);
string s6(6, 'X');
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
cout << s4 << endl;
cout << s5 << endl;
cout << s6 << endl;
s2[0] = 'x';
cout << s2 << endl;
s2[19];//会报错,用于检查
3.2 迭代器

- 正向迭代器
cpp
string s1("hellow word");
string::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
it++;
}
cout << endl;
- 反向迭代器
cpp
string::reverse_iterator rit = s1.rbegin();
while (rit != s1.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
- const修饰正向迭代器 (也可以用
cbegin和cend)

cpp
string s2("hahahaha");
string::const_iterator cit = s2.begin();
while (cit != s2.end())
{
//*cit += 2;//不能修改
cout << *cit << " ";
cit++;
}
cout << endl;
- const修饰反向向迭代器 (也可以用
crbegin和crend)
cpp
string::const_reverse_iterator rcit = s2.rbegin();
while (rcit != s2.rend())
{
//*cit += 2;//不能修改
cout << *rcit << " ";
rcit++;
}
cout << endl;
3.3 遍历string的方法
- 下标
cpp
int main()
{
string s1("hellow world");
for (size_t i = 0; i < s1.size(); i++)
{
cout << s1[i] << " ";
}
cout << endl;
}
- 迭代器
cpp
string::iterator it = s1.begin();
while (it != s1.end())
{
//*it-=2;//可以修改
cout << *it << " ";
it++;
}
cout << endl;
- 范围for(底层也是迭代器)
cpp
for (auto ch : s1)
{
cout << ch << " ";
}
cout << endl;
注意范围for必须加引用才能修改
cpp
for (auto& ch : s1)//不加引用没法修改,如果对象比较大也要加上引用
{
ch -= 2;
cout << ch << " ";
}
cout << endl;
cout << s1 << endl;
3.4 容量
注意:容量的大小都不包含'\0'

cpp
string s1("haha");
cout << s1.size() << endl;
cout << s1.length() << endl;
cout << s1.max_size() << endl;//所存储的最大容量
3.4.1 VS的扩容方式
cpp
string s;
size_t sz = s.capacity();
cout << sz << endl;
for (int i = 0; i < 100; i++)
{
s.push_back('a');
if (s.capacity() != sz)
{
sz = s.capacity();
cout << sz << endl;
}
}
运行结果

一开始是二倍扩容的,后面是1.5倍扩容,因为第一次并不是在堆上开辟(存在buff数组,后面才存放在堆上),这个跟平台有关
3.4.2 reserve
当提前知道数量,有没有能提前扩容的?答案是有的

cpp
int main()
{
string s;
s.reserve(120);//编译器会整数倍对齐,使其比100大,没有扩容
size_t sz = s.capacity();
cout << sz << endl;
for (int i = 0; i < 100; i++)
{
s.push_back('a');
if (s.capacity() != sz)
{
sz = s.capacity();
cout << sz << endl;
}
}
return 0;
}
reserve不会缩容,不会影响长度和修改内容,这个也与平台有关,Linux上不同

cpp
string s1("hellow world !!!");
cout << s1.size() << endl;
cout << s1.capacity() << endl;
cout << endl;
s1.reserve(5);
cout << s1.size() << endl;
cout << s1.capacity() << endl;
cout << endl;
s1.reserve(20);
cout << s1.size() << endl;
cout << s1.capacity() << endl;
cout << endl;
s1.reserve(32);
cout << s1.size() << endl;
cout << s1.capacity() << endl;
cout << endl
3.5 其他接口
clear函数不清容量
cpp
string s1("hellow world !!!");
cout << s1.size() << endl;
cout << s1.capacity() << endl;
cout << endl;
s1.clear();
cout << s1.size() << endl;
cout << s1.capacity() << endl;
cout << endl;
clear判断是不是空
cpp
cout << s1.empty() << endl;
shrink_to_fit是一种缩容请求,具体看编译器是否实现
3.5 元素访问

[]与at的区别,下标访问报错返回assert断言,而at会抛出异常
四、练习
4.1 仅仅反转字母
仅仅反转字母
题目描述:给你一个字符串s,根据下述规则反转字符串:所有非英文字母保留在原有位置。所有英文字母(小写或大写)位置反转。返回反转后的s
- 下标法
cpp
class Solution {
public:
bool Check(string s1,int n)
{
if(isalpha(s1[n]))
{
return true;
}
else
{
return false;
}
}
string reverseOnlyLetters(string s)
{
int n=s.size();
int left=0;
int right=n-1;
while(left<right)
{
while(!Check(s,left)&&left<right)
{
left++;
}
while(!Check(s,right)&&left<right)
{
right--;
}
swap(s[left++],s[right--]);
}
return s;
}
};
- 迭代器(双指针法)
cpp
class Solution
{
public:
bool Check(char ch)
{
if(isalpha(ch))
{
return true;
}
else
{
return false;
}
}
string reverseOnlyLetters(string s)
{
string::iterator left=s.begin();
string::iterator right=s.end()-1;
while(left<right)
{
while(!Check(*left)&&left<right)
{
left++;
}
while(!Check(*right)&&left<right)
{
right--;
}
swap(*left++,*right--);
}
return s;
}
};
4.2 字符串中的第一个唯一字符
字符串中的第一个唯一字符
题目描述:给定一个字符串s,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回-1
cpp
class Solution {
public:
int firstUniqChar(string s)
{
int count[26]={0};
for(auto ch:s)
{
count[ch-'a']++;
}
for(size_t i=0;i<s.size();i++)
{
if(count[s[i]-'a']==1)
{
return i;
}
}
return -1;
}
};
4.3 验证一个字符串是否是回文串
验证一个字符串是否是回文
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个回串。字母和数字都属于字母数字字符。给你一个字符串s,如果它是回文串,返回true;否则,返回false
cpp
class Solution
{
public:
bool Check(char ch)
{
return isalpha(ch)||isdigit(ch);
}
bool isPalindrome(string s)
{
for(auto& ch:s)
{
if(Check(ch))
{
if(ch>='A'&&ch<='Z')
{
ch+=32;
}
}
}
auto left=s.begin();
auto right=s.end()-1;
while(left<right)
{
while(left<right&&!Check(*left))
{
left++;
}
while(left<right&&!Check(*right))
{
right--;
}
if(*left!=*right)
{
return false;
}
left++;
right--;
}
return true;
}
};
4.4 字符串相加
题目描述:给定两个字符串形式的非负整数num1 和num2,计算它们的和并同样以字符串形式返回。你不能使任何內建的于处理整数的库(比如 BigInteger),也不能直接将输入的字符串转换为整数形式。
cpp
class Solution
{
public:
string addStrings(string num1, string num2)
{
int end1=num1.size()-1;
int end2=num2.size()-1;
int next=0;
string s1;
while(end1>=0||end2>=0)
{
int val1=end1>=0?num1[end1--]-'0':0;
int val2=end2>=0?num2[end2--]-'0':0;
int ret=val1+val2+next;
next=ret/10;
s1.insert(s1.begin(),ret%10+'0');
}
if(next!=0)
{
s1.insert(s1.begin(),'1');
}
return s1;
}
};
文章结语
感谢你读到这里~我是「键盘敲碎了雾霭」,愿这篇文字帮你敲开了技术里的小迷雾 💻
如果内容对你有一点点帮助,不妨给个暖心三连吧👇
👍 点赞 | ❤️ 收藏 | ⭐ 关注
(听说三连的小伙伴,代码一次编译过,bug绕着走~)
你的支持,就是我继续敲碎技术雾霭的最大动力 🚀
🐶 小彩蛋:
/^ ^\
/ 0 0 \
V\ Y /V
/ - \
/ |
V__) ||
摸一摸毛茸茸的小狗,赶走所有疲惫和bug~我们下篇见 ✨