为啥将 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&lt;&gt; 与标准容器一起使用是错误的?

【问题讨论】:

绝对是+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_ptrstd::shared_ptrstd::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 除非您根据需要 resetrelease @ratchetfreak:嗯,我不明白。什么? “除非你 resetrelease”,我看不出这如何适用于我的评论中的任何内容。请注意,auto_ptrunique_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 可以拥有对象的所有权? @Tracer unique_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

智能指针之 auto_ptr

c++11 auto_ptr介绍

auto_ptr 的动态内存分配

破坏 Glib::RefPtr 会导致 GTK 3 核心中的断言失败

STL 中使用的 Comp 比较器是不是需要从不更改 STL 中的比较对象?