简介
由于c++的历史包袱,c++要兼容c语言,c++的字符串要兼容c语言,在 C++ 中,字符串通常使用两种主要的方式来表示:
-
C风格字符串(C-style strings):
- 依然是以
'\0'
结尾的字符数组。这种表示方式与 C 语言中的字符串相同。例如:const char* str = "Hello, World!";
- 在内存中,它是一个字符数组,最后一个字符是空字符
'\0'
。
- 依然是以
-
C++ 的标准库字符串(
std::string
):- C++ 提供了一个
std::string
类型,属于标准库的一部分。它管理自己的内存,并且不需要手动处理'\0'
。使用std::string
更加方便和安全。例如:#include <string> std::string str = "Hello, World!";
std::string
处理字符串的长度、内存分配和释放,因此开发者可以专注于字符串的内容,而不必担心细节。
- C++ 提供了一个
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;
}
};