为啥将 std::auto_ptr<> 与标准容器一起使用是错误的?
Posted
技术标签:
【中文标题】为啥将 std::auto_ptr<> 与标准容器一起使用是错误的?【英文标题】:Why is it wrong to use std::auto_ptr<> with standard containers?为什么将 std::auto_ptr<> 与标准容器一起使用是错误的? 【发布时间】:2010-09-11 19:22:26 【问题描述】:为什么将std::auto_ptr<>
与标准容器一起使用是错误的?
【问题讨论】:
绝对是+1,因为我看到很多人都弄错了。这是一个很好的问题。 请阅读相关条目。这个问题在这里是从另一面考虑的。可能有助于了解有关 auto_ptr 和 STL 容器的更多信息。 ***.com/questions/8630552/… C++ 常见问题解答:Can I have a container of smart pointers to my objects?move
语义和unique_ptr
旨在避免与auto_ptr
相关的问题。在 C++ 03 中,语言还不够强大,无法编写像 auto_ptr
这样的类,因为编译器和语言无法区分 l 和 r 值,因此在所有场景中都能正确和安全地运行,因此使用了一些“hacks”来获取大部分时间都是想要的行为。
好文章:STL Containers 和 Auto_ptrs - 为什么他们不混合quantstart.com/articles/…
【参考方案1】:
C++ 标准规定 STL 元素必须是“可复制构造的”和“可赋值的”。换句话说,一个元素必须能够被分配或复制,并且这两个元素在逻辑上是独立的。 std::auto_ptr
不满足此要求。
以这段代码为例:
class X
;
std::vector<std::auto_ptr<X> > vecX;
vecX.push_back(new X);
std::auto_ptr<X> pX = vecX[0]; // vecX[0] is assigned NULL.
要克服此限制,如果您没有 C++11,则应使用 std::unique_ptr
、std::shared_ptr
或 std::weak_ptr
智能指针或 boost 等效项。 Here is the boost library documentation for these smart pointers.
【讨论】:
如果您不需要共享所有权,您还应该考虑提升指针容器。unique_ptr
也不允许复制,因此某些 STL 操作将无法正常工作,除非它们可以使用其移动语义。
"要克服这个限制,你应该使用std::unique_ptr
":这个类模板只能存在因为移动语义(它的规范需要右值引用),所以它从根本上需要C++11。然而(和相关的)C++11 标准不再说 STL 元素类型必须是“可复制构造的”和“可赋值的”;可移动构造和可移动分配就足够了。事实上unique_ptr
实例只能移动构造和移动分配。但auto_ptr
实例也是如此!因此,在 C++11 中,您可以使用 auto_ptr
执行您可以使用 unique_ptr
执行的操作。
@MarcvanLeeuwen 除非您根据需要 reset
和 release
@ratchetfreak:嗯,我不明白。什么? “除非你 reset
和 release
”,我看不出这如何适用于我的评论中的任何内容。请注意,auto_ptr
和 unique_ptr
都具有这两种方法,并且它们在两种情况下执行相同的操作。【参考方案2】:
auto_ptr
的复制语义与容器不兼容。
具体来说,将一个auto_ptr
复制到另一个不会创建两个相等的对象,因为其中一个对象失去了对指针的所有权。
更具体地说,复制auto_ptr
会导致其中一个副本释放指针。其中哪些保留在容器中未定义。因此,如果您将 auto_ptrs
存储在容器中,您可能会随机失去对指针的访问权限。
【讨论】:
【参考方案3】:关于这个主题的两篇超级优秀的文章:
Smart Pointers - What, Why, Which? Guru of the Week #25【讨论】:
因为我觉得在这近两年的时间里,他大概处理了手头的问题。 @DeadMG:是的,你是对的。但这不是我的目的。如果有人在某个时候来到这个线程并想了解auto_ptr
和其他东西,我相信这些链接会很有帮助。
有很多更新的副本。
@DeadMG:这个问题没有因为重复而关闭,因此可以扩展。拉泽说了之前没说的话。我猜他是偶然来的。
第二个链接中的解释,在调用sort()
后分析问题,比这里所有的答案都清楚。【参考方案4】:
STL 容器需要能够复制您存储在其中的项目,并且旨在期望原始和副本是等效的。自动指针对象具有完全不同的合同,复制会产生所有权转移。这意味着 auto_ptr 的容器会表现出奇怪的行为,具体取决于使用情况。
Effective STL (Scott Meyers) 第 8 条中对可能出错的问题进行了详细描述,而 Effective C++ (Scott Meyers) 第 13 条中也有不太详细的描述。
【讨论】:
【参考方案5】:STL 容器存储所含项目的副本。复制 auto_ptr 时,它会将旧的 ptr 设置为 null。这种行为破坏了许多容器方法。
【讨论】:
但是,当使用 unique_ptr 时,您会得到几乎相同的结果,因为只有一个 unique_ptr 可以拥有对象的所有权? @Tracerunique_ptr
与任何适当的 C++11 对象一样,只能在移动构造或分配时转移其资源的所有权,确保程序员必须故意传递 std::move(sourceObject)
或临时,而不是传递一个 lvalue 并且不直观/不可预测地让它被复制分配突变......正如这里彻底强调的那样,这是auto_ptr
的核心问题。【参考方案6】:
C++03 标准 (ISO-IEC 14882-2003) 在第 20.4.5 条第 3 段中说:
[...] [注意:[...] auto_ptr 不满足标准库的 CopyConstructible 和 Assignable 要求 容器元素,从而实例化标准库容器 使用 auto_ptr 会导致未定义的行为。 — 尾注]
C++11 标准 (ISO-IEC 14882-2011) 在附录 D.10.1 第 3 段中说:
[...] 注意:[...] auto_ptr 的实例满足 MoveConstructible 和 MoveAssignable,但不满足要求 CopyConstructible 和 CopyAssignable。 ——尾注]
C++14 标准 (ISO-IEC 14882-2014) 在附录 C.4.2 中说 附件 D:兼容性特性:
更改:类模板 auto_ptr、unary_function 和 binary_function,函数模板 random_shuffle 和 函数模板(及其返回类型)ptr_fun、mem_fun、 未定义 mem_fun_ref、bind1st 和 bind2nd。基本原理:被新功能取代。对原始功能的影响:使用的有效 C ++ 2014 代码这些类模板和函数模板可能无法在此编译 国际标准。
【讨论】:
以上是关于为啥将 std::auto_ptr<> 与标准容器一起使用是错误的?的主要内容,如果未能解决你的问题,请参考以下文章
std::auto_ptr 在我的模板类中编译,但不是 std::unique_ptr