恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
C++ 入门笔记(十一):string 的迭代器、容量管理与增删改
首页
资讯中心
/
C++ 入门笔记(十一):string 的迭代器、容量管理与增删改
C++ 入门笔记(十一):string 的迭代器、容量管理与增删改
发布时间:2026/10/18 7:41:36
C 入门笔记十一string 的迭代器、容量管理与增删改第十一节课把 string 用深了四种迭代器、size/capacity 这套容量管理、reserve/resize 的区别、越界检查的两种方式以及 insert/erase/replace 为什么都标 谨慎使用。最实用的收获是扩容是空间换时间、缩容是时间换空间知道要多大就 reserve 提前开好能省下一大串扩容拷贝。开篇学习方式不变听课 疑惑的地方自己敲代码验证 AI 辅助。这节代码全部在 VSMSVC实际编译运行四种迭代器、reserve/resize 的三种情况、at () 抛异常、空格替换、415 大数相加两种写法对比。一、四种迭代器两两组合**运行结果**四种迭代器本质是 能不能改 × 正反方向 的组合迭代器方向能否修改使用场景iterator正向可读可写普通对象遍历修改const_iterator正向只读const 对象遍历reverse_iterator反向可读可写反向遍历修改const_reverse_iterator反向只读const 对象反向遍历const iterator和const_iterator一字之差天壤之别const iteratorconst 修饰迭代器本身不能 、不能 --失去迭代的意义实践中根本无法使用const_iteratorconst 修饰迭代器指向的数据(*it)-- 不允许但 it 正常在函数成员后面加 const 才可以让迭代器指向的数据不能改变。const_iterator 就是把 指向的数据不能改 这件事做成了独立类型名跟 const iterator本身不能动完全不同。迭代器本身是用来遍历的如果写成 const iterator 连 都不行那就没有意义了。二、容量管理size、capacity、reserve、resize**运行结果**2.1 三个基本量size()有效字符数不含 \0s1123456 时是 6capacity()容量初始 15VSmax_size()理论上限本机 64 位下 92233720368547758072.2 reserve预留空间只改 capacitystring s2;s2.reserve(100); // 确定需要 100提前开好实测reserve(100) 后 capacity 直接到 111之后 100 次 push_back一次扩容都没有。而不 reserve 时VS 的扩容路径是15 → 31 → 47 → 70 → 105除了第一次后续 1.5 倍扩容。g 则是 2 倍15 → 30 → 60 → 120。扩容的本质是在当前空间堆后面开新空间如果后面没空间了就要迁移到异地开大空间、把数据拷贝过去效率很低。所以减少多次扩容就是为了避免异地扩容。string 初始给 15、扩容留余量capacity 永远 ≥ size都是 以空间换时间—— 多留点空间少几次拷贝。2.3 reserve 缩容不靠谱shrink_to_fit 才是真缩容实测reserve(3) 之后 capacity 还是 111——基本没缩。因为设计者规定缩容不能丢失数据如果缩容会导致有效数据放不下就不执行。真正缩容用 shrink_to_fit()实测从 105 缩回 15。为什么缩容代价大堆上申请的内存可以扩大但不允许原地缩小除非整个 free 掉所以缩容 100% 要异地开一个小空间再拷贝过去 —— 这就是 以时间换空间。而 clear 之后 capacity 不回收是为了复用紧接着还要塞同样多的数据回收了反而要再扩容拷贝效率更低。2.4 resize真插入 / 真删除resize 的三种情况实测写法情况结果resize(20, x)n sizesize20插入 x可能扩容cap 15→31resize(25)capacity n sizesize25插入的是 \0不扩容resize(5)n sizesize5删除尾字符变 12345resize(25) 插入 \0 这件事正好印证了上节课的问题string 必须存 _size不能靠 \0 判断长度。如果以 \0 结尾判断有效长度那 resize 补进去的 \0 本身也是有效字符程序员视角 size25靠 \0 数就数不对了。三、越界检查operator [] 断言 vs at () 抛异常**运行结果**三种越界处理方式对比方式行为特点原生数组 a[15]直接 *(a15)无检查历史原因数组本身拿不到长度没法检查s1[15]assert 断言debug 下崩、release 下可能不查s1.at(15)抛异常可以被 catch 捕获实测捕获到 invalid string position断言和抛异常两种都合理看需求抛异常可能被上层 catch 住然后吞掉让错误在别的地方以诡异方式爆发排查成本高断言就地崩溃直接暴露位置。但抛异常信息更准确、更便利。string 同时提供 [] 和 at ()就是为了让你按场景选。四、增删改 首选insert/erase/replace 谨慎**运行结果**尾插O (1)首选assign整体重新赋值insert谨慎使用头插 / 中间插都要挪动数据O (N)erase谨慎使用删中间数据要挪动O (N)replace谨慎使用替换后后面的数据都要挪O (N)经典需求把所有空格替换成%%低效方案反复 find replace—— 每次替换后面数据全挪动。高效方案reserve 预留 遍历尾插板书给的string s6;s6.reserve(s5.size()); // 提前预留避免扩容for (auto ch : s5) {if (ch ) s6 %%;else s6 ch;}为什么用 reserve 尾插因为 replace 每次替换后面数据都要挪动慢就慢在这而尾插是 O (1)最后拼完就是结果。用空间换时间多开一个字符串的空间换掉 O (N²) 的反复挪动。五、LeetCode 415大数相加的两种写法**运行结果**为什么大数必须用 stringint 是 4 字节 ≈ 10 位十进制long long 8 字节 ≈ 19 位。科研里 10 位远远不够20 位就装不下所以转成 string 逐位相加。方法一头部 insertO (N²)str.insert(str.begin(), 0 val); // 头插每次都要挪动全部数据底层是数组只能读头尾头插之后所有数据往后移一个O (N)循环里每次头插总共 O (N²)。方法二尾部 最后 reverseO (N)str.reserve(max(num1.size(), num2.size()) 1); // 预留str (0 val); // 尾插 O(1)reverse(str.begin(), str.end()); // 最后统一反转尾插 O (1)最后 reverse 一次 O (N)整体 O (N)。实测两种方法结果一致45677533、9999110000、22 位大数相加也正确。这题就是 以空间换时间 的实战与其每次头插挪数据不如多开一个字符串空间尾插完再反转。结尾这节把 string 从 会用 推进到 会用得聪明知道 reserve 提前预留省扩容、知道 resize 补 \0、知道 at () 能捕获、知道头插 O (N) 要避免。下一节应该开始看 string 的底层实现了学完再写下一篇。学习方式听课 疑惑处自己敲代码验证 AI 辅助查缺补漏。所有示例均在 VSMSVC实际编译运行。