1. 为什么学习 string 类?
1.1 C 语言中的字符串
C 语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C 标准库中提供了一些 str 系列的库函数,
但是这些库函数与字符串是分离开的,不太符合 OOP (面向对象程序设计)的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问
2.标准库中的 string 类
2.1 string 类的常用接口说明(注意下面只讲解最常用的接口)
(constructor)函数名称 | 功能说明 |
string() (重点) | 构造空的 string 类对象,即空字符串 |
string(const char* s) (重点) | 用 C-string 来构造 string 类对象 |
string(size_t n, char c) | string 类对象中包含 n 个字符 c |
string(const string&s) (重点) | 拷贝构造函数 |
void Teststring()
{ string s1; // 构造空的string类对象s1 string s2("hello bit"); // 用C格式字符串构造string类对象s2 string s3(s2); // 拷贝构造s3
}
2.2string 类对象的容量操作
函数名称 | 功能说明 |
size(重点) | 返回字符串有效字符的长度 |
length | 返回字符串有效字符的长度(与 size 功能一致) |
capacity | 返回字符串当前分配的总空间大小(包括未使用的预留空间) |
empty(重点) | 检测字符串是否为空串,若是则返回 true,否则返回 false |
clear(重点) | 清空字符串中的有效字符(不改变空间总大小,仅将 size 置为 0) |
reserve(重点) | 为字符串预留指定大小的空间,用于减少后续插入操作的内存分配次数 |
resize(重点) | 将有效字符的个数调整为 n 个: - 若 n 小于当前 size,则截断字符串; - 若 n 大于当前 size,则用指定字符 c(默认 '\0')填充多出的部分 |
示例:
#include <iostream>
#include <string>
using namespace std;int main() {// 初始化一个字符串string str = "Hello";cout << "初始字符串: " << str << endl;// 1. size() 和 length():返回有效字符长度(功能一致)cout << "\n1. size() = " << str.size() << endl;cout << " length() = " << str.length() << endl;// 2. capacity():返回当前总空间大小(包括预留空间)cout << "\n2. 初始 capacity = " << str.capacity() << endl;// 3. reserve(n):预留空间(不改变有效字符数)str.reserve(20); // 预留至少20个字符的空间cout << " 调用 reserve(20) 后,capacity = " << str.capacity() << endl;cout << " 此时 size = " << str.size() << "(reserve不改变有效字符数)" << endl;// 4. resize(n):调整有效字符数(截断或填充)cout << "\n3. 原字符串: " << str << endl;str.resize(8, '!'); // 扩展到8个字符,新增部分用'!'填充cout << " 调用 resize(8, '!') 后: " << str << endl;cout << " 此时 size = " << str.size() << ", capacity = " << str.capacity() << endl;str.resize(3); // 截断到3个字符cout << " 调用 resize(3) 后: " << str << endl;cout << " 此时 size = " << str.size() << ", capacity = " << str.capacity() << endl;// 5. empty():检测是否为空串cout << "\n4. 字符串是否为空? " << (str.empty() ? "是" : "否") << endl;// 6. clear():清空有效字符(size置0,capacity不变)str.clear();cout << " 调用 clear() 后,字符串: \"" << str << "\"" << endl;cout << " 此时 size = " << str.size() << ", capacity = " << str.capacity() << endl;cout << " 清空后是否为空? " << (str.empty() ? "是" : "否") << endl;return 0;
}
注意:
1. size()与 length()方法底层实现原理完全相同,引入 size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用 size()。
2. clear()只是将 string 中有效字符清空,不改变底层空间大小。
3.resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到 n 个,不同的是当字符个数增多时:resize(n)用 0 来填充多出的元素空间,resize(size_t n, char c)用字符 c 来填充多出的元素空间。注意:resize 在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
4.reserve(size_t res_arg=0):为 string 预留空间,不改变有效元素个数,当 reserve 的参数小于 string 的底层空间总大小时,reserver 不会改变容量大小。
2.3 string 类对象的访问及遍历操作
函数 / 特性名称 | 功能说明 |
operator[] | (重点)返回字符串中 pos 位置的字符。其中,const string 类对象调用时返回不可修改的字符(const char&),非 const 对象调用时返回可修改的字符(char&)。 |
begin() + end() | begin() 获取指向字符串第一个字符的迭代器;end() 获取指向字符串最后一个字符下一个位置的迭代器(作为遍历结束标志)。两者配合可遍历整个字符串。 |
rbegin() + rend() | rbegin() 获取指向字符串最后一个字符的反向迭代器(用于逆向遍历的起始);rend() 获取指向字符串第一个字符前一个位置的反向迭代器(作为逆向遍历结束标志)。两者配合可逆向遍历整个字符串。 |
范围 for 循环 | C++11 新增的简洁遍历方式,可自动遍历字符串中的每个字符(语法:for (char c : str) { ... }),本质是迭代器的语法糖,遍历过程中会自动处理迭代器的移动和边界判断。 |
这份代码展示了 C++ string
的多种元素访问和遍历方式:
operator[]
访问:最常用的访问方式,通过索引直接访问字符
支持读写操作,但不进行边界检查,访问越界会导致未定义行为
at()
方法访问:与
operator[]
类似,但会进行边界检查访问越界时会抛出
out_of_range
异常,更安全
迭代器遍历:
begin()
和end()
提供正向迭代器,用于从前往后遍历rbegin()
和rend()
提供反向迭代器,用于从后往前遍历
const 迭代器:
用于
const string
对象的遍历保证不会修改字符串内容
范围 for 循环:
C++11 引入的简洁遍历方式
通过引用 (
char&
) 可以修改元素,否则只能读取
#include <iostream>
#include <string>
#include <algorithm>using namespace std;int main() {string str = "Hello, String!";cout << "原始字符串: " << str << endl;cout << "字符串长度: " << str.size() << endl << endl;// 1. 使用operator[]访问单个元素cout << "1. 使用operator[]访问:" << endl;if (!str.empty()) {cout << "第一个字符: " << str[0] << endl;cout << "最后一个字符: " << str[str.size() - 1] << endl;// 修改元素str[0] = 'h';cout << "修改后第一个字符: " << str[0] << endl;}cout << endl;// 2. 使用at()方法访问单个元素(带边界检查)cout << "2. 使用at()访问:" << endl;try {cout << "索引为7的字符: " << str.at(7) << endl;// 尝试访问超出范围的索引,会抛出out_of_range异常// cout << str.at(100) << endl;} catch (const out_of_range& e) {cout << "访问异常: " << e.what() << endl;}cout << endl;// 3. 使用迭代器遍历cout << "3. 使用迭代器遍历:" << endl;cout << "正向遍历: ";for (string::iterator it = str.begin(); it != str.end(); ++it) {cout << *it;}cout << endl;// 4. 使用反向迭代器遍历cout << "4. 使用反向迭代器遍历:" << endl;cout << "反向遍历: ";for (string::reverse_iterator rit = str.rbegin(); rit != str.rend(); ++rit) {cout << *rit;}cout << endl << endl;// 5. 使用const迭代器遍历(用于const string)cout << "5. 使用const迭代器遍历:" << endl;const string const_str = "Const String";cout << "const字符串正向遍历: ";for (string::const_iterator cit = const_str.begin(); cit != const_str.end(); ++cit) {cout << *cit;}cout << endl;cout << "const字符串反向遍历: ";for (string::const_reverse_iterator crit = const_str.rbegin(); crit != const_str.rend(); ++crit) {cout << *crit;}cout << endl << endl;// 6. 使用范围for循环遍历(C++11及以上)cout << "6. 使用范围for循环遍历:" << endl;cout << "范围for遍历: ";for (auto c : str) {cout << c;}cout << endl;// 范围for修改元素for (char& c : str) { // 注意使用引用才能修改if (c == ',') {c = ';';}}cout << "修改后的字符串: " << str << endl;return 0;
}