简介
由于c++的历史包袱,c++要兼容c语言,c++的字符串要兼容c语言,在 C++ 中,字符串通常使用两种主要的方式来表示:
C风格字符串(C-style strings):
依然是以'\0'
结尾的字符数组。这种表示方式与 C 语言中的字符串相同。例如: const char* str = "Hello, World!";
在内存中,它是一个字符数组,最后一个字符是空字符 '\0'
。 C++ 的标准库字符串(std::string
):
std::string
类型,属于标准库的一部分。它管理自己的内存,并且不需要手动处理 '\0'
。使用 std::string
更加方便和安全。例如: #include <string> std::string str = "Hello, World!";
std::string
处理字符串的长度、内存分配和释放,因此开发者可以专注于字符串的内容,而不必担心细节。 c风格类型的字符串我们都已经很熟悉,\0的存在让我们在处理在处理字符串的时候需要时刻小心,在手动拼接或复制字符串时经常会因为\0处理不妥而出错。c++为我们提供了一个全新的string来处理字符串的长度、内存分配和释放,因此开发者可以专注于字符串的内容,而不必担心细节。
在一切开始之前,我们先介绍一个网站。www.cplusplus.com是一个广受欢迎的C++编程语言资源网站,成立于2000年代初期。开始由一个c++爱好者建立,后来各路大佬汇聚,网站内容也非常成熟,可供我们学习和参考。
string类的常用接口说明
构造函数
c++98就为我们提供了7个构造函数,当然并不要求也没有必要对所有的函数烂熟于心,大多数时候把这个当成词典来使用就可以了,选常用的一部分学习。
学习类和对象很容易大家就能知道上面哪是重要的
不传参的默认构造,拷贝构造和第四个用常量字符串初始化的构造。
#define _CRT_SECURE_NO_WARNINGS 1#include <iostream>#include <string>using namespace std;int main(){string s1;//默认构造string s2("123456789");string s3(s2);//拷贝构造cout << s1 << endl;cout << s2 << endl;cout << s3 << endl;cin >> s1;cout << s1 << endl;return 0;}
我们也可以向里面输入中文
便利string的三种方式
operator[]
string类重载了[],这让我们可以像数组一样访问字符串。
同时注意看,重载后返回的类型是引用,不仅可以减少拷贝,关键在于这样我们能够直接通过下标对字符串进行修改。在重载的时候加入了断言,当pos大于size(越界时),编译器会报错。
#define _CRT_SECURE_NO_WARNINGS 1#include <iostream>#include <string>using namespace std;int main(){string s1;string s2("123456789");string s3(s2);cout << s1 << endl;cout << s2 << endl;cout << s3 << endl;cout << s2[2] << endl;//可以通过下标访问s2[0] = '5';//也可以直接修改cout << s2 << endl;/*cin >> s1;cout << s1 << endl;*/return 0;}
遍历string的第一种方法就是:下标 + []
#include <iostream>#include <string>using namespace std;int main(){string s1;string s2("123456789");string s3(s2);cout << s1 << endl;cout << s2 << endl;cout << s3 << endl;cout << s2[2] << endl;//可以通过下标访问s2[0] = '5';//也可以直接修改cout << s2 << endl;/*cin >> s1;cout << s1 << endl;*/for (size_t i = 0; i < s2.size(); i++){cout << s2[i] << " ";}cout << endl;return 0;}
迭代器
迭代器是一种用于遍历容器(如数组、向量、链表等)元素的对象。它提供了一种统一的方法来访问不同类型的容器中的元素(所有的容器都可以由它访问),而无需了解容器的内部结构。
正向迭代器
begin()返回第一个位置;
end()返回最后一个位置的下一个位置。
for (size_t i = 0; i < s2.size(); i++){cout << s2[i] << " ";}cout << endl;string::iterator it= s2.begin();while (it != s2.end()){cout << *it << " ";++it;}cout << endl;
反向迭代器
反向迭代器提供了一个rbegin()指向最后一个位置
rend()指向第一个位置的前一个位置。这里任然要使用++,++被重载了让它能倒着遍历。
string s1;string s2("123456789");string s3(s2);cout << s1 << endl;cout << s2 << endl;cout << s3 << endl;string::reverse_iterator it = s2.rbegin();while (it != s2.rend()){cout << *it << " ";++it;}cout << endl;
const迭代器
由于权限,普通迭代器可读可写,无法访问const对象,const对象要用const迭代器,只读不写。
const string s2("hello world!");//正向string::const_iterator cit = s2.begin();while (cit != s2.end()){cout << *cit << " ";++cit;}cout << endl;//反向string::const_reverse_iterator rcit = s2.rbegin();while (rcit != s2.rend()){cout << *rcit << " ";++rcit;}cout << endl;
范围for
字符赋值,自动迭代,自动判断结束,底层由迭代器实现。适用于容器和数组。
for (size_t i = 0; i < s2.size(); i++){cout << s2[i] << " ";}cout << endl;string::iterator it= s2.begin();while (it != s2.end()){cout << *it << " ";++it;}cout << endl;for (auto ch : s2){cout << ch << " ";}cout << endl;
但是由于范围for()是给ch这个局部变量赋值,并不能直接改变s2,而迭代器是直接可以改变s2的。
string::iterator it = s2.begin();while (it != s2.end()){*it += 2;cout << *it << " ";++it;}cout << endl;for (auto ch : s2){ch -= 2;cout << ch << " ";}cout << endl;cout << s2 << endl;
如果要使用范围for()来改变传入的形参,在传入时加上引用,这样就不会生成拷贝而是直接修改传入的形参。
capacity
不同编译器对内存空间的处理是不一样的
string s;size_t sz = s.capacity();cout << "original capacity:" << sz << endl;for (int i = 0; i < 100; i++){s.push_back('c');if (sz != s.capacity()){sz = s.capacity();cout << "capacity changed: " << sz << endl;}}
在vs中有一个buff数组,该开始中string中的数据是储存在buff中的,当要储存的数据空间大于15时才会额外开空间,该开始要把buff中的数据拷贝出来一共开了两倍空间,后边都是开的1.5倍空间。c++只规定了开空间,但是怎么开空间没有做具体的要求。其他的编译器开空间的方式可能会不同。
频繁的扩容会导致效率低下,为解决这一问题,c++提供了reserve()可以预留空间。
string s("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");cout << s.size() << endl;cout << s.capacity() << endl << endl;s.reserve(10);cout << s.size() << endl;cout << s.capacity() << endl << endl;s.reserve(30);cout << s.size() << endl;cout << s.capacity() << endl << endl;s.reserve(40);cout << s.size() << endl;cout << s.capacity() << endl << endl;s.reserve(50);cout << s.size() << endl;cout << s.capacity() << endl << endl;
但是c++标准并没有规定具体怎么处理空间,不同的编译器也不一样。
仅仅反转字母
https://leetcode.cn/problems/reverse-only-letters/
题目解析:
题目中让我们仅仅反转字母,首先我们要分类-----字母和非字母;很简单所有的字母并不多,我们只需要筛选出字母就好,写一个函数实现这个功能 。要实现反转首先就要有两个位置,使用双指针,从首尾开始遍历。基本框架已经确定,接下来就是敲定判断的细节了。首先考虑极限条件,输入的没有字母left会一直加,越界了;所以处理left和right时要加上判断条件。
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 left = 0; size_t right = s.size() - 1; while(left < right) { while(left < right && !Isletter(s[left])) { left++; } while(left < right && !Isletter(s[right])) { right--; } swap(s[left++],s[right--]); } return s; }};