🤖个人主页: 起名字真南
目录
- 引言
- [1 string类的基础知识](#1 string类的基础知识)
-
- [1.1 什么是string类](#1.1 什么是string类)
- [1.2 string和STL](#1.2 string和STL)
- [2 范围for](#2 范围for)
-
- [2.1 范围for的语法](#2.1 范围for的语法)
- [3 string类的主要功能和操作](#3 string类的主要功能和操作)
-
- [3.1 string类的构造](#3.1 string类的构造)
- [3.2 string类对象空间容量操作](#3.2 string类对象空间容量操作)
- [3.3 string类对象的访问和遍历操作](#3.3 string类对象的访问和遍历操作)
- [3.4 string类对象的修改操作](#3.4 string类对象的修改操作)
- [3.5 string类对象的非成员函数](#3.5 string类对象的非成员函数)
引言
在C++中,string类是标准库中的一个重要工具,用于处理字符串。与传统的C风格字符串(char数组)相比,C++的string类提供了更高的灵活性和安全性,并且能够自动处理内存管理。因此,掌握string类的使用对于编写高效、安全的C++代码至关重要。本文将从string类的基础知识入手,逐步深入探讨它的常用操作、底层实现及高级技巧,帮助你更好地理解并应用这一强大的工具。
1 string类的基础知识
1.1 什么是string类
C++的string类其实是标准库std的一部分,并且定义在了< string >头文件中,封装了字符数组并且提供了很多便捷的方法
1.2 string和STL
在C++中std::string与STL有着密切的关系(Standard Template Library,标准模板库)有着密切的联系。std::string类实际上是基于STL设计原则构建的一的特殊模板类,用于处理字符串这种常见的数据类型。
1 std::string是STL容器的特例 :
容器是组成STL的六大组件之一,它包含了 std::vector std::list 等等一些容器,而std::string本质上是专门处理字符串的一个容器,和vector< char >有很多相似的地方
- 自动分配内存:std::string会自动管理储存的字符串内存,在对字符串进行增删查改等内容时会自动分配内存,不需要手动管理
- 迭代器 :std::string可以使用STL标准模板库中的迭代器进行遍历整个容器,可以使用begin() end() 用来遍历字符。
例如下面的代码 :
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string str = "hello world";
for (auto it : str)
{
cout << it;
}
return 0;
}
这里的for循环时范围for因为后续学习STL的过程中都少不了他的存在所以接下来为大家介绍一下
2 范围for
其实范围for的本质上还是使用了STL中的六大组件之一的迭代器,通过调用begin() end()两个函数来返回string容器中的开头和结尾的迭代器。
2.1 范围for的语法
cpp
for(auto it : 容器)
{
cout<< it ;
}
这里的it是一个迭代器而不是指针,但是你可以理解为他是一个智能指针,因为它提供了像(*解引用,++递增)的一些用法,但是并不一定是指向实际内存的地址。而且不同的容器迭代器的类型也不同,例如vector的迭代器就是普通类型,但是list迭代器的类型可能是一个复杂的对象。
指针:则是指向内存的地址,是指向内存中某个位置的变量,并且只能用于数组中还有连续内存中的对象。
如果看不懂的话范围for的写法也可以是这样:
cpp
auto begin = str.begin();
auto end = str.end();
for(auto it = begin; it != end; it++)
{
char currentChar = *it;
cout<< currentChar;
}
这里是定义了三个迭代器 分别是begin(用于记录str容器的开头位置)end(用于记录str的结尾位置) 还有it(作为循环变量),在for的循环体中我们定义了一个currentChar的变量用于记录当前字符,然后通过对it进行解引用来获取当前字符,最后输出当前字符然后it++,知道it == end的时候循环结束完整的输出整个字符串。
3 string类的主要功能和操作
在这里给大家提供一个可以作为参考的网址:
链接: C++string参考资料
3.1 string类的构造
函数名称 | 功能说明 |
---|---|
string() (重点) | 构造空字符串作为string 类的对象 |
string(const char * s) (重点) | 用字符串 作为string类的对象 |
string(size_t n, char c) | 用n 个字符c 来构造string类的对象 |
string(const string& str) (重点) | 用一个string 类型来初始化另一个string 类型的对象 |
string (const string& str, size_t pos, size_t len = npos) | 使用字符串st r的pos 位置向后len 个长度来构建string类的对象) |
代码演示:
cpp
int main()
int main()
{
//string()
string s1;
//string(const char* s)
string s2("hello world");
//string(const string& str)
string s3(s2);
//string(siez_t, char c)
string s4(10, 'a');
//string(const string& str, size_t pos, size_t len);
string s5(s3, 6, 5); //区间是左闭右开
cout << "s1 :" << s1 << "\ns2: " << s2 << "\ns3: " << s3 << endl;
cout << "s4: " << s4 << endl;
cout << "s5: " << s5 << endl;
return 0;
}
输出结果:
由于string的功能太多所以只对主要内容进行讲解。我们在写构造函数的时候需要注意在构造s1 的时候后面不能加()如果加了括号编译器会认为是一个函数声明并且不需要传参,不加括号编译器则会自动调用string的构造函数当我们在使用示例s5构造函数的时候要注意打印的区间是左开右闭,代码示例中是从第六个位置开始打印包括第六个位置后的五个字符
3.2 string类对象空间容量操作
函数名称 | 功能说明 |
---|---|
size() (重点) | 返回字符串有效字符长度 |
length() | 返回字符串有效字符长度 |
capacity() | 返回空间总大小 |
empty() (重点) | 检测字符串是否为空,是返回 true ,否则返回 false |
clear() (重点) | 清空有效字符串 |
reserve(size_t n) (重点) | 为字符开辟n 个空间 |
resize(size_t n, char c) (重点) | 将字符串的个数改成 n 个,多出的空间用字符 c 填充 |
代码演示:
cpp
int main()
{
string s1("hello world");
cout << "s1.size() :" << s1.size() << endl;
cout << "s1.length() :" << s1.length() << endl;
cout << "s1.capacity() :" << s1.capacity() << endl;
if (!s1.empty())
{
cout << "s1.empty() :" << "true" << endl;
}
s1.reserve(40);
cout << "s1.reserve(40) :" << s1.capacity() << endl;
s1.resize(20, 'p');
cout <<"s1.resize(20, 'p'):" << s1 << endl;
s1.clear();
cout<< "s1.clear(): " << s1 << endl;
return 0;
}
输出结果:
在我们reserve(40)之后为什么编译器开辟的空间是47?是因为编译器的内存管理策略,目的是提高性能并减少未来重新分配,一般开辟的空间只会比我们输入的更大不会更小
3.3 string类对象的访问和遍历操作
函数名称 | 功能说明 |
---|---|
operator[] (pos) (重点) | 返回 pos 位置的字符,const string 类对象调用 |
begin() + end() | begin() 获取第一个字符的迭代器 + end() 获取最后一个字符下一个位置的迭代器 |
rbegin() + rend() | rbegin() 获取反向迭代的第一个字符的迭代器 + rend() 获取最后一个字符前一个位置的反向迭代器 |
范围 for (for (auto& item : container)) | C++11 支持更简洁的范围 for 的新遍历方式 |
代码演示:
cpp
int main()
{
string s1("hello world");
for (int i = 0; i < s1.size(); i++)
{
cout << s1[i];
}
cout << endl;
auto begin = s1.begin();
auto end = s1.end();
for (auto it = begin; it != end; it++)
{
cout << *it;
}
cout << endl;
for (auto i : s1)
{
cout << i;
}
cout << endl;
auto rbegin = s1.rbegin();
auto rend = s1.rend();
for (auto it = rbegin; it != rend; it++)
{
cout << *it;
}
return 0;
}
输出结果:
需要注意一种情况就是在调用rbegin 和 rend的时候最后循环条件改变的方式依旧是++,而不是--。
3.4 string类对象的修改操作
函数名称 | 功能说明 |
---|---|
push_back (char c) | 在字符串后尾插字符 c |
append (const char * ch) | 在字符串后追加一个字符串 ch |
operator+= (const char * ch) (重点) | 在字符串后追加字符串 ch |
c_str() (重点) | 返回 C 格式字符串 |
find (const char * s, size_t pos = 0) + npos (重点) | 从字符串 pos 位置开始往后查找字符串 s ,返回字符串中的位置 |
rfind (const char * s, size_t pos = npos) | 从字符串 pos 位置开始往前查找字符串 s ,返回字符串中的位置 |
substr (size_t pos = 0, size_t len = npos) | 在字符串中从 pos 位置开始,截取 len 个字符,然后将其返回 |
代码演示:
cpp
int main()
{
string s1;
s1.push_back('a');
s1.push_back('a');
s1.push_back('a');
s1.push_back('a');
cout << s1 << endl;
s1.append("bxvxcvb");
cout << s1 << endl;
s1 += "xzviuynq";
cout << s1 << endl;
size_t c = s1.find('c', 0);
cout << "c第一次出现的坐标大小 :" << c << endl;
size_t rc = s1.rfind('c',-1);
cout << "c最后一次出现的坐标大小 :" << rc << endl;
string s2;
s2 = s1.substr(0, 9); //左闭右开所以返回的是下标从0-8的字符
cout << s2 << endl;
if (strcmp(s1.c_str(), s2.c_str()) > 0)
{
cout << "s1 > s2" << endl;
}
else if (strcmp(s1.c_str(), s2.c_str()) < 0)
{
cout << "s1 < s2" << endl;
}
else
{
cout << "s1 == s2" << endl;
}
return 0;
}
输出结果:
注意这里的find 和refind 的返回值都是字符c的坐标,我们调用c_str的原因是我们没有重载< 和 >操作符所以不能对string类直接进行比较,所以我们需要使用c_str来将他们转换成字符型来进行比较。
3.5 string类对象的非成员函数
函数名称 | 功能说明 |
---|---|
operator+ | 将两个字符串拼接并返回一个新字符串 |
relational operators (==, !=, <, >, <=, >=) | 用于比较两个字符串的大小或相等性 |
swap (string& str) | 交换两个字符串的内容 |
operator>> (istream& in, string& str) | 从输入流读取字符串 |
operator<< (ostream& out, const string& str) | 将字符串输出到输出流 |
getline (istream& in, string& str) | 从输入流中读取一行字符串,直到遇到换行符 |
代码演示:
cpp
int main()
{
string s1("hello ");
string s2("world");
string s3 = s1 + s2;
cout << s3 << endl;
string foo = "alpha";
string bar = "beta";
if (foo == bar) cout << "foo and bar are equal\n";
if (foo != bar) cout << "foo and bar are not equal\n";
if (foo < bar) cout << "foo is less than bar\n";
if (foo > bar) cout << "foo is greater than bar\n";
if (foo <= bar) cout << "foo is less than or equal to bar\n";
if (foo >= bar) cout << "foo is greater than or equal to bar\n";
string s4;
cout << "s4 = ";
cin >> s4;
cout << s4 << endl;
cin.ignore();
string s5;
cout << "输入一串文字 :";
getline(cin, s5);
cout << s5 <<endl;
return 0;
}
输出结果:
注意当我们在使用cin和getline的时候需要注意一种情况就是cin只会读取到空格而在你输出的完之后按下的空格cin不会读取而是会存到缓冲区里,当我们使用getline的时候会直接读取缓冲区的\n从而导致程序出错直接结束不会再读取你输入的内容。,
解决的办法有两种:
- 使用cin.ignore()用来清除缓冲区的一个字符一般是换行符'\n'
- 先执行getline()在用cin。