【发布时间】:2013-04-14 23:26:48
【问题描述】:
据我了解,当将内容 push_back 放入向量时,有时它必须分配一个新的内存块,从而将所有元素从旧内存块复制到其中,调用它们的析构函数。由于 unique_ptr 析构函数删除了拥有的内存,它们怎么可能与向量一起使用? 在向量中使用 unique_ptr 是否安全? 是不是比普通指针慢?
【问题讨论】:
标签: c++ memory vector c++11 smart-pointers
据我了解,当将内容 push_back 放入向量时,有时它必须分配一个新的内存块,从而将所有元素从旧内存块复制到其中,调用它们的析构函数。由于 unique_ptr 析构函数删除了拥有的内存,它们怎么可能与向量一起使用? 在向量中使用 unique_ptr 是否安全? 是不是比普通指针慢?
【问题讨论】:
标签: c++ memory vector c++11 smart-pointers
根据cppreference,std::unique_ptr 的用例之一是在“移动感知容器”中使用,例如std::vector。这意味着std::vector<T> 足够聪明,可以检测到T 何时可移动,std::unique_ptr 就是这种情况,并在调整向量大小时使用移动构造函数。
【讨论】:
使用移动语义可以将 unique_ptr 从一个位置移动到另一个位置。这将避免在指针使用std::move 从一个内存位置“移动”到另一个内存位置时破坏指针所保存的数据。
这有点像在向量增长时从旧向量数组到新内存位置执行浅拷贝。
【讨论】:
导致将所有元素从旧内存块复制到其中,调用它们的析构函数。由于 unique_ptr 析构函数删除了拥有的内存,它们怎么可能与向量一起使用?
事实上,它不再是严格地复制元素,它是移动。对于没有隐式或显式移动构造函数的类型,也是如此。但是对于unique_ptrs,这意味着新的unique_ptrs 是在新分配的内存中构造的,给定对“旧”unique_ptrs 的右值引用。他们的移动构造函数做了正确的事,即将所有权从旧的unique_ptrs 转移到新的unique_ptrs,让旧的为空,因此在销毁时不会删除任何内容。
在向量中使用 unique_ptr 是否安全?
只要您尊重语言规则和“C++ 常识”,使用unique_ptrs 无处不在 是省事。这意味着,您必须明确 做一些愚蠢的事情来破坏unique_ptrs 的安全行为。其中包括例如假装将拥有对象的所有权赋予另一个智能指针:
unique_ptr<T> ptr1 {new T()};
unique_ptr<T> ptr2 {ptr1.get()}; //two unique_ptr's owning the same object...
或涉及reinterpret_cast、memcpy、memset 或其他不尊重 C++ 对象生命周期的破坏性操作。
它比普通指针慢吗?
也许吧。与复制原始指针(即将原始指针设置为零)相比,移动unique_ptr 显然会涉及一些小的开销。但是是否可以优化该开销取决于您的编译器和优化器。与以往一样,请咨询您的分析器以评估它是否变慢以及放慢是否重要。我打赌不会,考虑到智能指针会给你带来的安全性,永远不要询问unique_ptr 的性能。
旁注:如果您事先知道向量的大小,请记住使用vector::reserve()。它将为您节省unique_ptrs 的重新分配和移动。如果你只知道你的向量的大概大小,不要小题大做——unique_ptrs 不是很大(通常和原始指针加上删除器一样大,可能会也可能不会优化掉),所以除非你真的有严格的内存限制,否则多保留几个百分点不会有什么坏处。
【讨论】:
auto_ptrs 的工作方式有点像unique_ptrs,因为它们转移了所有权。问题是他们在copying 时这样做,而unique_ptr 在moving 时转移对象的所有权。这意味着使用auto_ptr,您可能会意外地创建指针的临时副本,将所有权转移给它,并在临时超出范围时删除对象。使用 unique_ptr 是不可能的,因为他们删除了 copy-ctor。因此,auto_ptr 自 C++11 以来已被弃用,unique_ptr 是更安全的完全替代品。