?个人主页: 起名字真南
?个人专栏:【数据结构初阶】 【C语言】 【C++】
目录
引言1 string类的基础知识1.1 什么是string类1.2 string和STL 2 范围for2.1 范围for的语法 3 string类的主要功能和操作3.1 string类的构造3.2 string类对象空间容量操作3.3 string类对象的访问和遍历操作3.4 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 >有很多相似的地方
例如下面的代码 :
#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的语法
for(auto it : 容器){cout<< it ;}
这里的it是一个迭代器而不是指针,但是你可以理解为他是一个智能指针,因为它提供了像(*解引用,++递增)的一些用法,但是并不一定是指向实际内存的地址。而且不同的容器迭代器的类型也不同,例如vector的迭代器就是普通类型,但是list迭代器的类型可能是一个复杂的对象。
指针:则是指向内存的地址,是指向内存中某个位置的变量,并且只能用于数组中还有连续内存中的对象。
如果看不懂的话范围for的写法也可以是这样:
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类的对象) |
代码演示:
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 填充 |
代码演示:
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 的新遍历方式 |
代码演示:
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 个字符,然后将其返回 |
代码演示:
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) | 从输入流中读取一行字符串,直到遇到换行符 |
代码演示:
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。