C++ STL标准库std::vector扩容时进行深复制的原因是什么


今天小编给大家分享一下C++STL标准库std::vector扩容时进行深复制的原因是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。但是笔者却发现了一个奇怪的现象,std::vector扩容时,对其中的元素竟然进行的是深复制。请看示例代码:打印结果如下:Test
Test
Test copy
~Test
Test
Test copy
Test copy
~Test
~Test
~Test
~Test
~Test由于我们没有调用reverse函数,所以默认只分配了一个元素的大小。第一次emplace_back时,仅进行了一次普通构造。第二次emplace_back时,就需要进行扩容,然后把第一个元素拷贝过去,再释放原来的对象。所以这里除了有一次新的构造以外,还有一次复制和释放。后面的行为类似,不再赘述,但关键问题就在于,Test类明明实现了移动构造(浅复制),可这里竟然调用了拷贝构造(深复制)。如果vector扩容无脑调用拷贝构造,那么这个对象如果含有很多外链的成员(比如说指向buffer的指针、指向其他对象的指针等),调用拷贝构造就意味着要把这些链接的对象全部都重新构造一遍。这对于vector自身扩容来说,显然是没有必要的,会极度浪费内存空间。基于上述理由,我认为STL的开发者不可能连这个问题都考虑不到,但想不通为什么我明明实现了移动构造,却不能调用。带着这样的疑问我去研读了STL的源码(GNU版本),在vector扩容时,会调用_M_realloc_insert函数,该函数在vector.tcc文件中实现。在这个函数里面对已有元素进行拷贝的时候,看到了类似这样的代码:有趣的就是这个__uninitialized_move_if_noexcept_a,我们找到这个函数的实现:再看一下_GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR的实现也就是说,在C++11以前,这玩意就是对象本身(毕竟C++11以前还没有移动构造),而在C++11以后被定义成了__make_move_if_noexcept_iterator,继续查看其定义。这里用了一个conditional,来判断这个迭代器的类型,如果__move_if_noexcept_cond为真,就取迭代器本身,否则就取移动迭代器。看起来问题就在这里了,之前我们的例程中的Test一定就是符合了这个__move_if_noexcept_cond,导致用了原始迭代器。继续深挖这个__move_if_noexcept_cond,看到这样的代码:也就是说,如果一个类,不存在不会抛出异常的移动构造函数并且可拷贝,那么就为真。Test类显然符合,所以vector在复制时用了普通的迭代器进行了遍历,自然就会调用拷贝构造函数进行复制了。所以,我们需要让Test不符合__move_if_noexcept_cond的条件,也就是这里要将移动构造函数声明为noexcept表示它不会抛出异常,这样vector在复制时就会使用移动迭代器(就是会包装一层std::move),从而触发移动构免费云主机域名造。顺道我们也看一眼移动迭代器的原理:确实调用了std::move,证明我们的思路没错。所以,修改Test代码,实现noexcept移动构造:打印结果如下:Test
Test
Test move
~Test
Test
Test move
Test move
~Test
~Test
~Test
~Test
~Test这次如我们所愿,调用了移动构造。以上就是“C++STL标准库std::vector扩容时进行深复制的原因是什么”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注百云主机行业资讯频道。

相关推荐: tree shaking对打包体积优化及作用实例分析

这篇“treeshaking对打包体积优化及作用实例分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“treeshaking对打包体积优化及作用…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

(0)
打赏 微信扫一扫 微信扫一扫
上一篇 03/29 11:03
下一篇 03/29 11:15

相关推荐