关于C++中vector的两个小tips分享
前言
本来这篇文章标题我想起成《关于vector的两个小坑》,后来想想,其实也不算是坑,还是自己对原理性的东西理解的没做那么透彻。工作中遇到的很多问题,后来归根到底都是基础不牢靠。
vector扩容
这个问题很经典了,但还是不小心踩到。有一个需求是要对目标元素进行复制,而目标元素集合是保存在vector里面,于是简单思考下就有如下代码(大致含义):
voidDuplidate(vector*element_list,Element*element){ element_list.push_back(*element); } voidProcess(){ 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()
起因是我在代码里面新增了如下代码(大致):
voidEraseElement(constvector::iterator&element_iter, vector &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改成constreference。
另外erase本身的确比较危险,主要还是erase的时候iterator本身没发生变化,但是指向的元素变了,,在很多时候iterator会自然地指向下一个元素,但是由于这是未定义的行为,这里面可能会有不可预期的地方,所以最终改成显示的获取返回重新赋值(erase()会返回下一个迭代器,但这一点常常被忽略),这样就能保证安全性了。更安全更推荐的做法应该是使用remove_if()这里就不展开讲了。
voidEraseElement(vector&element_list, vector ::iteratorelement_iter){ while(element_iter!=element_list.end()){ element_iter=element_list.erase(element_iter); } }
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对毛票票的支持。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。