C++从入门到实战(十六)String(中)String的常用接口(构造接口,析构接口,迭代器,遍历修改,容量管理与数据访问)

C++从入门到实战(十六)String(中)String的常用接口(构造接口,析构接口,迭代器,遍历修改,容量管理与数据访问)

C++从入门到实战(十六)String(中)详细讲解String的常用接口


前言

  • 在上一篇博客中,我们深入探讨了 STL 与 String 的内在联系,解析了 string 类的设计初衷以及它在 C++ 编程中的重要价值
  • 通过剖析 string 类在内存管理、字符操作和 STL 容器适配等方面的独特优势,我们理解了它作为 C++ 标准字符串处理工具的核心地位。
  • 本篇博客将进入 string 的实战环节,重点解析 string 类的常用接口
我的个人主页,欢迎来阅读我的其他文章
https://blog.ZEEKLOG.net/2402_83322742?spm=1011.2415.3001.5343
我的C++知识文章专栏
欢迎来阅读指出不足
https://blog.ZEEKLOG.net/2402_83322742/category_12880513.html?spm=1001.2014.3001.5482

C++string的官方讲解网站

https://cplusplus.com/reference/string/string/?kw=string

一、std::string

typedef basic_string<char> string;

std::string 是 C++ 标准库中用于处理字符串的类,它让我们可以像操作普通变量一样方便地处理文本数据。

  • 简单来说,string 就是 C++ 为程序员提供的「字符串神器」,不用再手动管理内存,也不用操心字符串长度,各种常用操作都已经帮你写好了。
  • string的接口有很多,我们先来看一看他的接口有哪些,后面会挨个详细讲
接口名功能简述
构造函数创建字符串对象
析构函数销毁字符串对象
operator=字符串赋值操作
迭代器
begin()返回指向字符串起始位置的迭代器
end()返回指向字符串末尾(‘\0’ 之后)的迭代器
rbegin()返回反向迭代器,指向字符串末尾
rend()返回反向迭代器,指向字符串起始位置之前
cbegin()返回 const 迭代器,指向字符串起始位置
cend()返回 const 迭代器,指向字符串末尾
crbegin()返回 const 反向迭代器,指向字符串末尾
crend()返回 const 反向迭代器,指向字符串起始位置之前
容量操作
size() / length()返回字符串长度(不包含终止符)
max_size()返回字符串的最大可能长度
resize(n)调整字符串长度为 n,超出部分截断,不足部分填充默认字符
capacity()返回当前分配的存储空间大小
reserve(n)请求至少能容纳 n 个字符的存储空间(可能扩大 capacity)
clear()清空字符串内容
empty()判断字符串是否为空
shrink_to_fit()释放未使用的内存,使 capacity 等于 size
元素访问
operator[]通过下标访问字符(不检查越界)
at()通过下标访问字符(越界时抛出异常)
back()访问最后一个字符
front()访问第一个字符
修改操作
operator+=追加内容到字符串末尾
append()追加内容到字符串末尾
push_back()追加单个字符到字符串末尾
assign()赋值新内容给字符串
insert()在指定位置插入内容
erase()删除指定位置的字符或子串
replace()替换指定位置的子串
swap()交换两个字符串的内容
pop_back()删除最后一个字符
字符串操作
c_str()返回 C 风格字符串(以 ‘\0’ 结尾的 const char*)
data()返回字符串数据的指针(C++11 起与 c_str() 相同)
get_allocator()返回用于内存分配的 allocator 对象
copy()将字符串内容复制到字符数组中
find()查找子串或字符首次出现的位置
rfind()从后向前查找子串或字符首次出现的位置
find_first_of()查找字符串中第一个与指定字符集中任何字符匹配的位置
find_last_of()从后向前查找字符串中第一个与指定字符集中任何字符匹配的位置
find_first_not_of()查找字符串中第一个不与指定字符集中任何字符匹配的位置
find_last_not_of()从后向前查找字符串中第一个不与指定字符集中任何字符匹配的位置
substr()返回子串(从指定位置开始的指定长度的子串)
compare()比较两个字符串的大小(字典序)

二、string的构造接口

在这里插入图片描述
  • string 类提供了多种构造函数,让你可以用不同的方式创建字符串对象。下面是最常用的几种构造方式。

1. 默认构造函数:创建空字符串

string();

作用:创建一个空的字符串对象,不包含任何字符。
例子

string s;// 创建空字符串 cout << s << endl;// 输出空行
在这里插入图片描述

2. 拷贝构造函数:复制已有字符串

string(const string& str);

作用:用一个已有的 string 对象创建新的字符串,内容完全相同。
例子

string original ="hello"; string copy(original);// 拷贝构造 cout << copy << endl;// 输出: hello
在这里插入图片描述

3. 从已有字符串截取部分

string(const string& str, size_t pos, size_t len = npos);

作用:从 strpos 位置开始,截取 len 个字符作为新字符串。
关键参数

  • pos:起始位置(从0开始)。
  • len = nposnpos 是一个特殊值,表示「直到字符串末尾」。

例子

string original ="hello world"; string substr1(original,6);// 从位置6开始到末尾,输出: world string substr2(original,0,5);// 从位置0开始取5个字符,输出: hello
在这里插入图片描述

4. 用C风格字符串构造

string(constchar* s);

作用:把 C 风格的字符串(以 '\0' 结尾的字符数组)转换为 string 对象。
例子

constchar* c_str ="hello"; string s(c_str);// 用C字符串构造 cout << s << endl;// 输出: hello

5. 用C风格字符串的前n个字符构造

string(constchar* s, size_t n);

作用:取 C 风格字符串的前 n 个字符(不要求以 '\0' 结尾)。
例子

constchar* c_str ="abcdef"; string s(c_str,3);// 取前3个字符 cout << s << endl;// 输出: abc
在这里插入图片描述

6. 用重复字符构造

string(size_t n,char c);

作用:创建包含 n 个重复字符 c 的字符串。
例子

string s(5,'a');// 创建5个'a'的字符串 cout << s << endl;// 输出: aaaaa
在这里插入图片描述

7. 用迭代器构造(了解即可)

template<classInputIterator>string(InputIterator first, InputIterator last);

作用:用迭代器范围内的字符构造字符串,常用于从其他容器(如 vector<char>)创建字符串。

三、string的赋值与析构

1. 析构函数 ~string()

每个 string 对象在生命周期结束时,会自动调用析构函数,释放占用的内存。你不需要手动操作,C++ 会帮你搞定。

例子

{ string s ="hello";// 创建对象}// 离开作用域,s 自动销毁,内存自动释放

2. 赋值运算符 operator=

2.1 用另一个 string 对象赋值(常用)

string&operator=(const string& str);

作用:把一个 string 对象的内容复制到另一个 string 对象。
例子

string s1 ="hello"; string s2 ="world"; s1 = s2;// s1 现在是 "world" cout << s1 << endl;// 输出: world

2.2 用 C 风格字符串赋值(了解即可)

string&operator=(constchar* s);

作用:把 C 风格字符串(如 "hello")赋值给 string 对象。
例子

string s; s ="hello";// 等价于 string s = "hello"; cout << s << endl;// 输出: hello

2.3 用单个字符赋值

string&operator=(char c);

作用:让 string 对象只包含一个字符。
例子

string s; s ='A';// s 现在是 "A" cout << s << endl;// 输出: A

四、string::iterator迭代器(重点掌握)

1. 为什么需要迭代器?

迭代器是C++中访问容器(如string、vector)元素的通用方式,它让我们可以:

  • 统一语法:用相同的方式遍历不同类型的容器。
  • 支持算法:STL算法(如findsort)依赖迭代器工作。
  • 隐藏底层细节:无需关心容器内部如何存储数据。

类比:迭代器就像“容器的指针”,让你可以逐个访问元素。

在这里插入图片描述

2. 迭代器的基本使用(像指针一样操作)

定义迭代器

string::iterator it;// 正向迭代器(从前往后) string::reverse_iterator rit;// 反向迭代器(从后往前)

遍历字符串

#include<iostream>#include<string>usingnamespace std;intmain(){ string s ="hello";// 正向遍历(从前往后)for(string::iterator it = s.begin(); it != s.end();++it){ cout <<*it;// *it 是当前字符,输出: hello} cout << endl;// 反向遍历(从后往前)for(string::reverse_iterator rit = s.rbegin(); rit != s.rend();++rit){ cout <<*rit;// 输出: olleh}return0;}
在这里插入图片描述

简化写法(用auto自动推导类型)

for(auto it = s.begin(); it != s.end();++it){ cout <<*it;// 效果同上}

3. 迭代器的4种类型

类型功能例子
iterator正向遍历,可读写auto it = s.begin();
const_iterator正向遍历,只读(用于常量字符串)const string cs = "hi";
auto cit = cs.begin();
reverse_iterator反向遍历,可读写auto rit = s.rbegin();
const_reverse_iterator反向遍历,只读auto crit = cs.rbegin();

4. 迭代器的边界

  • s.begin():指向第一个字符(如 'h' in "hello")。
  • s.end():指向最后一个字符的下一个位置(即字符串结束标记 '\0' 的位置),不包含该字符
  • s.rbegin():指向最后一个字符(如 'o' in "hello")。
  • s.rend():指向第一个字符的前一个位置(反向的结束标记)。

为什么不访问'\0'
因为迭代器设计为只访问有效字符,而 '\0' 是C风格字符串的结束标记,string 类用 size() 明确记录长度,不需要 '\0'

5. 修改字符串(通过迭代器写操作)

string s ="hello";for(auto it = s.begin(); it != s.end();++it){*it =toupper(*it);// 转为大写} cout << s;// 输出: HELLO

6. 迭代器与STL算法结合

例子1:查找字符

string s ="hello";auto it =find(s.begin(), s.end(),'l');if(it != s.end()){ cout <<"找到 'l' 在位置: "<< it - s.begin()<< endl;// 输出: 2}
在这里插入图片描述

例子2:反转字符串

string s ="hello";reverse(s.begin(), s.end());// 直接反转 cout << s;// 输出: olleh

7. 安全注意事项

迭代器失效:修改字符串可能导致迭代器失效,例如:

for(auto it = s.begin(); it != s.end();){if(*it =='l'){ s.erase(it);// 错误!删除元素后迭代器失效}else{++it;}}

正确做法erase() 返回下一个有效迭代器:

for(auto it = s.begin(); it != s.end();){if(*it =='l'){ it = s.erase(it);// 正确!更新迭代器}else{++it;}}

五、string的遍历与修改

1. string的遍历(逐个访问字符)

方法1:用下标 [](最常用)
类似数组,用 s[i] 访问第 i 个字符(索引从0开始)。
例子

string s ="hello";for(size_t i =0; i < s.size(); i++){ cout << s[i]<<" ";// 输出: h e l l o}
在这里插入图片描述

方法2:用迭代器(适合STL算法)
迭代器像「指针」,指向字符串中的字符。
例子

string s ="hello";for(auto it = s.begin(); it != s.end();++it){ cout <<*it <<" ";// 输出: h e l l o}
在这里插入图片描述

方法3:范围for循环(C++11起,最简单)
直接遍历每个字符,无需索引。
例子

string s ="hello";for(char c : s){ cout << c <<" ";}
在这里插入图片描述

2. string的修改(增删改查)

修改单个字符
用下标或迭代器直接修改。
例子

string s ="hello"; s[0]='H';// s 变为 "Hello" s.back()='!';// 修改最后一个字符,s 变为 "Hello!"

追加内容
+=append() 方法。
例子

string s ="hello"; s +=" world";// s 变为 "hello world" s.append("!");// s 变为 "hello world!"

插入字符
insert() 方法在指定位置插入。
例子

string s ="hello"; s.insert(1,"X");// 在位置1插入 'X',s 变为 "hXello"

删除字符
erase() 方法删除指定位置或范围。
例子

string s ="hello"; s.erase(1,2);// 从位置1开始删除2个字符,s 变为 "heo"

替换内容
replace() 方法替换指定范围的字符。
例子

string s ="hello"; s.replace(1,3,"XXX");// 替换位置1~3的字符,s 变为 "hXXXo"

3. string的底层(了解即可)

string 的底层是一个动态字符数组,类似 char*,但会自动管理内存:

  • 自动扩容:当字符串变长时,string 会自动申请更大的内存,并复制原有内容。
  • 内存优化:现代C++对短字符串有特殊优化(SSO,小字符串优化),直接存储在对象内部,避免堆内存分配。

例子(帮助理解底层)

string s ="a";// 可能直接存对象内部(SSO) s +="bcdef";// 内容太长,转为堆内存存储

注意事项

  1. 索引越界:访问 s[i] 时,确保 i < s.size(),否则会导致未定义行为(比如崩溃)。
  2. 性能提示:频繁插入/删除可能导致内存频繁重新分配,性能较低。

迭代器失效:修改字符串可能导致迭代器失效,比如:

for(auto it = s.begin(); it != s.end();++it){ s.erase(it);// 错误!删除元素后迭代器失效}

六、String里的Capacity(容量管理)

1. size() 和 length()(获取字符串长度)

  • 作用:返回字符串中实际字符的数量(不包含结尾的 '\0')。
  • 区别size() 是所有容器(如 vector)通用的接口,length() 是专门为字符串设计的,两者功能完全相同。
    例子
string s ="hello"; cout << s.size()<< endl;// 输出: 5 cout << s.length()<< endl;// 输出: 5(和size()一样)
在这里插入图片描述

2. max_size()(最大容量)

作用:返回当前系统中 string 对象能存储的最大字符数(理论上限)。
注意:实际中几乎不可能达到这个值,因为内存会先耗尽。
例子

string s; cout << s.max_size()<< endl;// 输出一个很大的数
在这里插入图片描述

3. resize(n) 和 resize(n, c)(调整大小)

作用:调整字符串长度为 n

  • n 小于当前长度,截断多余字符。
  • n 大于当前长度,用字符 c 填充(默认用 '\0' 填充)。
    例子
string s ="hello"; s.resize(3);// s 变为 "hel" s.resize(5,'!');// s 变为 "hel!!"
在这里插入图片描述

4. capacity()(当前分配的容量)

作用:返回字符串当前分配的内存能容纳的最大字符数(不包含 '\0')。
特点:容量通常大于等于实际长度,避免频繁重新分配内存。
例子

string s ="hello"; cout << s.size()<< endl;// 输出: 5(实际长度) cout << s.capacity()<< endl;// 输出: 15(不同系统可能不同)
在这里插入图片描述

5. reserve(n)(预分配容量)

作用:提前分配至少能容纳 n 个字符的内存,减少后续扩容次数。
例子

string s; s.reserve(100);// 预先分配100个字符的空间 cout << s.capacity()<< endl;// 输出: 100 或更大(取决于实现)
在这里插入图片描述

6. clear()(清空字符串)

作用:清空字符串内容,使其长度为0,但不释放内存(容量不变)。
例子

string s ="hello"; s.clear(); cout << s.size()<< endl;// 输出: 0 cout << s.capacity()<< endl;// 输出: 15(和clear前一样)
在这里插入图片描述

7. empty()(判断是否为空)

作用:检查字符串是否为空(长度是否为0)。
例子

string s ="hello"; cout << s.empty()<< endl;// 输出: 0(false) s.clear(); cout << s.empty()<< endl;// 输出: 1(true)
在这里插入图片描述

8. shrink_to_fit()(缩容)

作用:将容量调整为当前实际长度,释放多余内存。
注意:缩容需要重新分配内存并复制数据,代价较高,建议少用。
例子

string s; s.reserve(100);// 容量变为100 s ="hello";// 长度变为5,但容量仍为100 s.shrink_to_fit();// 容量缩小到5(或略大于5)

总结

方法作用例子
size()返回字符串实际长度string s = "a"; s.size();
capacity()返回当前分配的容量s.capacity();
reserve(n)预分配至少 n 个字符的空间s.reserve(100);
clear()清空字符串(长度为0,但容量不变)s.clear();
empty()判断字符串是否为空if (s.empty()) { ... }
shrink_to_fit()释放多余内存(慎用,代价高)s.shrink_to_fit();

底层原理(了解即可)

string 的内存管理策略:

  • 小字符串优化(SSO):短字符串(如 "hello")直接存储在对象内部,不使用堆内存,提高性能。

动态扩容:当字符串长度超过容量时,自动申请更大的内存(通常翻倍),例如:

string s; s +='a';// 容量可能变为1 s +='b';// 容量可能变为2 s +='c';// 容量可能变为4(翻倍)

注意事项

  1. 容量 vs 长度
    • 长度:实际字符数(size())。
    • 容量:已分配的内存大小(capacity())。
  2. 性能提示
    • 提前 reserve() 可避免频繁扩容。
    • 少用 shrink_to_fit(),除非确实需要释放大量内存。
  3. 清空字符串clear()s = "" 效率更高,因为不涉及内存分配。

八、string数据访问

1. operator[](下标访问)

作用:通过索引访问字符串中的字符,类似数组。
重载版本

  • char& operator[] (size_t pos); // 用于非const字符串(可修改)
  • const char& operator[] (size_t pos) const; // 用于const字符串(只读)

为什么需要两个版本?
为了区分读写操作

  • 对非const字符串(如 string s = "hello";),s[0] = 'H'; 可以修改字符。
  • 对const字符串(如 const string cs = "hi";),cs[0] 只能读,不能修改。

例子

string s ="hello"; s[0]='H';// 修改第一个字符,s 变为 "Hello" cout << s[1];// 输出: econst string cs ="hi";// cs[0] = 'H'; // 错误!const版本不允许修改 cout << cs[0];// 输出: h

2. at() 方法(安全访问)

作用:同 [],但会检查索引是否越界(若越界则抛出 out_of_range 异常)。
例子

string s ="hello";try{ cout << s.at(10);// 越界,抛出异常}catch(const out_of_range& e){ cout <<"错误: "<< e.what();// 输出错误信息}

对比 []at()

方法越界检查性能建议场景
[]不检查已知索引合法时
at()检查稍慢不确定索引是否合法时

3. back()(访问最后一个字符)

作用:返回字符串的最后一个字符的引用。
例子

string s ="hello"; cout << s.back();// 输出: o s.back()='!';// 修改最后一个字符,s 变为 "hell!"

4. front()(访问第一个字符)

作用:返回字符串的第一个字符的引用。
例子

string s ="hello"; cout << s.front();// 输出: h s.front()='H';// 修改第一个字符,s 变为 "Hello"

注意事项

  1. 空字符串风险:对空字符串调用 back()front() 会导致未定义行为(如崩溃),需先检查 s.empty()
  2. 性能取舍at() 更安全但稍慢,[] 更快但需自行确保索引合法。
  3. 引用返回[]at()back()front() 返回的都是引用,可直接修改字符。

以上就是这篇博客的全部内容,下一篇我们将继续探索STL中String里更多精彩内容。

我的个人主页,欢迎来阅读我的其他文章
https://blog.ZEEKLOG.net/2402_83322742?spm=1011.2415.3001.5343
我的C++知识文章专栏
欢迎来阅读指出不足
https://blog.ZEEKLOG.net/2402_83322742/category_12880513.html?spm=1001.2014.3001.5482
非常感谢您的阅读,喜欢的话记得三连哦
在这里插入图片描述

Read more

N46Whisper:让日语视频字幕制作变得如此简单

N46Whisper:让日语视频字幕制作变得如此简单 【免费下载链接】N46WhisperWhisper based Japanese subtitle generator 项目地址: https://gitcode.com/gh_mirrors/n4/N46Whisper 还在为日语视频制作字幕而头疼吗?N46Whisper正是你一直在寻找的智能解决方案!这款基于云端AI技术的日语语音识别工具,彻底改变了传统字幕制作的繁琐流程,让每个人都能轻松上手。 为什么你需要这款工具 想象一下,原本需要数小时手动打字的工作,现在只需要几分钟就能完成。这就是N46Whisper带来的效率革命: * 零门槛使用:无需安装任何软件,打开浏览器就能开始工作 * AI精准识别:采用先进的Whisper技术,日语语音识别准确率惊人 * 云端极速处理:借助Google Colab的强大计算能力,处理速度超乎想象 * 双格式支持:ass和srt两种主流格式任你选择 快速入门:三步搞定日语字幕 第一步:准备环境 打开Google Colab,上传N46Whisper.ipynb文件,系

By Ne0inhk
2026论文降AI实战:知网AIGC检测怎么过?手把手教你手动降AI技巧与高效工具避坑指南

2026论文降AI实战:知网AIGC检测怎么过?手把手教你手动降AI技巧与高效工具避坑指南

毕业季最崩溃的瞬间,不是查重没过,而是查重过了,AI率却爆红。 面对知网、维普、Turnitin不断进化的AIGC检测算法,很多同学发现:手动降AI太累,乱用工具又容易“翻车”。要么是改出来的文章逻辑不通,要么是降下来了但排版全乱,改格式改到通宵。 为了帮大家避坑,我们实测了市面上热门的几款工具,并总结了一套科学的评测标准。无论你是想白嫖算力,还是愿意为效率买单,这篇文章都能帮你找到最优解。 评测标准——为什么我们只看这3点? 在挑选降AI工具前,你必须明白,能把数值降下来只是及格线。真正的“保命”工具,必须通过以下三个维度的考验: 降AI效果(硬指标): 1.   为什么重要? 这是核心目的。现在的检测器不只看词汇重复,更看语义逻辑(困惑度)和句子节奏(突发性)。工具必须能打破AI的死板逻辑,而不仅仅是换词。 格式保留度(效率核心,最易被忽视): 1.   为什么重要? 很多工具采用“复制文本-改写-粘贴”的逻辑,这会导致论文原本的引用角标、

By Ne0inhk

Claude, Cursor, Aider, Copilot,AI编程助手该选哪个?

2026年,AI编程工具已经非常成熟了。市面上这么多AI编程工具,哪个最好用? 本文选取了当前最具代表性的六款工具:Claude Code、Aider、Cursor、GitHub Copilot、MetaGPT 以及 OpenHands,从技术特性、优缺点及部署门槛进行客观对比。 Claude Code Anthropic 于2025年推出了 Claude Code,这是一款基于命令行的编程智能体工具。它不同于网页版的对话框,而是直接运行在终端中,能够深度理解本地项目结构。最出名的 AI 编程助手,很贵,但一分钱一分货,不得不说它很好用。 通过终端直接通过自然语言操作。它不仅能写代码,还能自主运行测试、解释复杂的架构、甚至执行终端命令来修复错误。其背后依托的是推理能力极强的 Claude 3.5/3.7 Sonnet 模型。 优势: * 推理能力极强:在处理复杂的逻辑重构和长代码理解上,目前处于行业顶尖水平。 * 自主性:

By Ne0inhk

LLaMA-Factory 推理全攻略:从配置到优化实战

LLaMA-Factory 推理实战:从配置到部署的完整工程化路径 你有没有遇到过这样的场景?模型终于训练好了,LoRA 权重也保存下来了,满心欢喜地想试试效果——结果一运行就报错:“Template not found”、“CUDA out of memory”,甚至 API 返回空内容。调试半天才发现是配置写错了、模板不匹配,或者忘了启用量化。 这其实不是你的问题,而是大模型推理落地过程中的典型“断点”。训练只是起点,真正让模型产生价值的是推理环节的稳定与高效。而 LLaMA-Factory 正是在这个关键节点上,提供了一套开箱即用的解决方案。 它不只是一个微调框架,更是一条贯穿“训练 → 推理 → 部署”的完整流水线。无论是本地调试、网页交互,还是批量处理、API 服务集成,都可以通过一个 YAML 文件驱动完成。更重要的是,它的设计哲学是“降低认知负担”——不用再手动拼接 prompt

By Ne0inhk