关于C++中vector的两个小tips分享

 更新时间:2019年05月05日 08:29:04   作者:findingsea  
这篇文章主要给大家介绍了关于C++中vector的两个小tips,文中通过示例代码介绍的非常详细,对大家学习或者使用C++具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

前言

本来这篇文章标题我想起成《关于 vector 的两个小坑》,后来想想,其实也不算是坑,还是自己对原理性的东西理解的没做那么透彻。工作中遇到的很多问题,后来归根到底都是基础不牢靠。

vector 扩容

这个问题很经典了,但还是不小心踩到。有一个需求是要对目标元素进行复制,而目标元素集合是保存在 vector 里面,于是简单思考下就有如下代码(大致含义):

void Duplidate(vector<Element>* element_list, Element* element) {
element_list.push_back(*element);
}

void Process() {
for (auto& package : package_list) {
if (IsNeedDuplicate()) {
Duplicate(element_list, package->element);
}
}
}

看起来好像没什么问题,就是当前的 package 对象是否满足复制的要求,需要的话,就对 package 的成员 origin_element 进行复制。跑 UT 也正常,然后在测试的时候就 coredump 了。看 core 文件就是挂在了复制的时候。这里我一开始就没明白,一个简单的复制为什么会有 coredump。

检查了很久 element 复制的场景,甚至想要专门写一个拷贝构造函数。最后才恍然大悟, origin_element 指针指向的就是 element_list 里面的元素, element_list 是整体流程的数据源, packge 对象是封装的中间处理对象。之前的开发人员为了方便,直接在 package 对象上保存了原始的 element 指针,而这个指针指向的是一个 vector 里的元素。而我新加的逻辑会往原始的 vector 里面再添加元素,那么就有可能导致 vector 扩容,而 vector 扩容会导致整体的复制,从而导致原来指向这些元素的指针都失效了,靠后的 package 对象再去访问 origin_element 就产生了 coredump。

当然,从设计上来说,就不应该保存指向 vector 元素的指针,但是这里有太多旧代码牵涉,这里就不做讨论。

vector::erase()

起因是我在代码里面新增了如下代码(大致):

void EraseElement(const vector<Element>::iterator& element_iter,
vector<Element>& element_list) {
while (element_iter != element_list.end()) {
element_list.erase(element_iter);
}
}

然后 cr 的同学提出了一个疑问是 element_iter 是 const 不可变的,但是在函数里有擦除了对应的元素,这里会不会有问题?虽然 UT 都已经跑过了,但是这种写法的确比较奇怪,于是就借机学习了一下 vector::erase() 的实现原理跟用法。

erase(iterator) 的实现原理其实不会改变 iterator ,而是把后面的元素一个个往前移动,相当于是 iterator 指向的元素本身发生了变化,所以可以用 const 来修饰这个 iterator 。但是这里用 cosnt & 其实是没有错但是无用的修饰,除了容易让人误判之外,其实没有什么实际用途。我之前是为了修正 cpplint 才把reference 改成 const reference。

另外 erase 本身的确比较危险,主要还是 erase 的时候 iterator 本身没发生变化,但是指向的元素变了,,在很多时候 iterator 会自然地指向下一个元素,但是由于这是未定义的行为,这里面可能会有不可预期的地方,所以最终改成显示的获取返回重新赋值( erase() 会返回下一个迭代器,但这一点常常被忽略),这样就能保证安全性了。更安全更推荐的做法应该是使用 remove_if() 这里就不展开讲了。

void EraseElement(vector<Element>& element_list,
vector<Element>::iterator element_iter ) {
while (element_iter != element_list.end()) {
element_iter = element_list.erase(element_iter);
}
}

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。

相关文章

  • C语言示例讲解switch分支语句的用法

    C语言示例讲解switch分支语句的用法

    这篇文章主要为大家介绍了switch语句,switch语句是我们常见会用到的结构,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • C语言编程数据在内存中的存储详解

    C语言编程数据在内存中的存储详解

    本篇文章是C语言编程篇,主要为大家介绍C语言编程中数据在内存中存储解析,有需要的朋友可以借鉴参考下,希望可以有所帮助
    2021-09-09
  • C语言18个必背经典程序

    C语言18个必背经典程序

    这篇文章主要分下工的是18个C语言必背的经典程序,下面文章我们就来看看实例,需要的小伙伴可以参考一下哟,希望对你有所帮助
    2021-10-10
  • C++超详细讲解稀疏矩阵

    C++超详细讲解稀疏矩阵

    今天小编就为大家分享一篇关于C++稀疏矩阵的转置思路并实现乘法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2022-05-05
  • set_new_handler(0)有什么用

    set_new_handler(0)有什么用

    本文主要介绍了set_new_handler(0)有什么用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • C++ com编程学习详解

    C++ com编程学习详解

    这篇文章主要介绍了C++ COM编程的学习过程,在C++中,可以使用抽象基类来实现COM接口,需要的朋友可以参考下,希望能够给你带来帮助
    2021-09-09
  • 关于C++中push_back()函数的用法及代码实例

    关于C++中push_back()函数的用法及代码实例

    push_back是vector的一个方法,表示将一个元素存储到容器的末尾,下面这篇文章主要给大家介绍了关于C++中push_back()函数用法的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-11-11
  • C++ 解决求两个链表的第一个公共结点问题

    C++ 解决求两个链表的第一个公共结点问题

    本文主要介绍了利用C++实现输入两个无环的单向链表时,找出它们的第一个公共结点的问题。文章中的示例代码简洁易懂,感兴趣的同学可以和小编一起学习一下
    2021-12-12
  • c++中nlohmann json的基本使用教程

    c++中nlohmann json的基本使用教程

    nlohmann/json 是一个C++实现的JSON解析器,使用非常方便直观,下面这篇文章主要给大家介绍了关于c++中nlohmann json基本使用的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-09-09
  • 数据结构C语言链表的实现介绍

    数据结构C语言链表的实现介绍

    大家好,本篇文章主要讲的是数据结构C语言链表的实现介绍,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2021-12-12

最新评论