C++程序设计:原理与实践(进阶篇)15.8 调整vector类达到STL版本的功能
15.8 调整vector类达到STL版本的功能
在15.5节中为vector增加了begin()、end()和类型别名后,现在只差insert()和erase()就接近我们设计一个std::vector的近似版本的目标了:
我们还是使用指向元素类型的指针T*作为迭代器的类型,这是最简单的方法。我们将边界检查迭代器的实现留作练习(习题18)。
人们通常不会为元素连续存储的数据类型(如vector)提供列表操作,如insert()或erase()。但insert()和erase()这样的列表操作对短vector或少量元素极为有用也极为高效。我们已经反复看到了push_back()的作用,它是另一个常见的列表操作。
基本上,我们可以通过拷贝所有位于所删除元素之后的元素来实现vector<T,A>::erase()。利用14.3.6节中定义的vector再加上上述内容,我们得到:
借助下面的图示,你可以更容易理解上面代码:
erase()的代码非常简单,但在纸上试着画几个例子可能是个好主意。有没有正确地处理空vector?为什么要判断p==end()?如果删除vector的最后一个元素会怎么样?如果使用下标语法的话代码的可读性会不会更好?
相对而言,vector<T, A>::insert()就有一点复杂了:
请注意:
由于迭代器不能指向序列之外,所以我们使用指针来完成,比如elem+sz。这就是为什么分配器用指针而不是迭代器来定义。
当我们使用reserve()时,元素会被移动到一块新的内存中。因此,我们必须要记住插入元素位置的索引,而不是指向它的迭代器。当vector为其元素重新分配内存时,指向vector内部的迭代器会失效——可以理解为它们指向的是旧的内存。
我们使用分配器参数A的方式很直观,但不准确。当你需要实现一个容器时,最好还是仔细读一下相关的标准。
由于这些微妙的细节,我们尽量避免处理底层内存问题。自然地,标准库vector(以及所有其他标准库容器)能正确实现这些重要的语义细节。这也是优先使用标准库而不是“家庭制作”的原因之一。
出于性能原因,你不应在一个含有100?000个元素的vector内部使用insert()或erase();对这种操作,使用list(或map,参见16.6节)更为适合。但是,vector确实提供了insert()和erase()操作,而且如果我们只是移动几个或几十个字的数据的话,其性能是没有问题的——毕竟现代计算机已非常擅长这种拷贝操作(见习题20)。注意不要用list(链表)表示少量元素的列表。
相关文章
- 在 Go 里用 CGO?这 7 个问题你要关注!
- 9款优秀的去中心化通讯软件 Matrix 的客户端
- 求职数据分析,项目经验该怎么写
- 在OKR中,我看到了数据驱动业务的未来
- 火山引擎云原生大数据在金融行业的实践
- OpenHarmony富设备移植指南(二)—从postmarketOS获取移植资源
- 《数据成熟度指数》报告:64%的企业领袖认为大多数员工“不懂数据”
- OpenHarmony 小型系统兼容性测试指南
- 肯睿中国(Cloudera):2023年企业数字战略三大趋势预测
- 适用于 Linux 的十大命令行游戏
- GNOME 截图工具的新旧截图方式
- System76 即将推出的 COSMIC 桌面正在酝酿大变化
- 2GB 内存 8GB 存储即可流畅运行,Windows 11 极致精简版系统 Tiny11 发布
- 迎接 ecode:一个即将推出的具有全新图形用户界面框架的现代、轻量级代码编辑器
- loongarch架构介绍(三)—地址翻译
- Go 语言怎么解决编译器错误“err is shadowed during return”?
- 敏捷:可能被开发人员遗忘的部分
- Denodo预测2023年数据管理和分析的未来
- 利用数据推动可持续发展
- 在 Vue3 中实现 React 原生 Hooks(useState、useEffect),深入理解 React Hooks 的