为什么在标准容器中使用std::auto_ptr<>是错误的?

225

为什么在标准容器中使用std::auto_ptr<>是错误的?


6
非常同意这一点,因为我看到很多人在这方面犯错。这是一个很好的问题需要问。 - twokats
请同时阅读相关项目。这个问题从另一个角度来考虑。了解更多关于auto_ptr和STL容器的内容可能会有所帮助。https://dev59.com/dV7Va4cB1Zd3GeqPLqIh - nickolay
1
move语义和unique_ptr旨在避免与auto_ptr相关的问题。在C++03中,语言不够强大,无法编写像auto_ptr这样在所有情况下都能正确且安全地运行的类,因为编译器和语言无法区分左值和右值,所以有些“技巧”被用来大部分时间获得所需的行为。 - Phil1970
不错的文章:STL容器和Auto_ptrs - 为什么它们不能混合使用 https://www.quantstart.com/articles/STL-Containers-and-Auto_ptrs-Why-They-Dont-Mix/ - alfC
6个回答

126

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.

为了克服这个限制,您应该使用std::unique_ptr, std::shared_ptrstd::weak_ptr智能指针,如果您没有C++11,则可以使用boost等效指针。这是这些智能指针的boost库文档。

7
如果您不需要共享所有权,那么您应该考虑使用Boost指针容器。 - me22
5
unique_ptr 也不允许复制,因此某些STL操作将无法正常工作,除非它们可以使用其移动语义。 - Mike Weller
4
为了克服这个限制,您应该使用 std::unique_ptr :该类模板只能存在于 C++11 中,因为它需要移动语义(其规范要求 rvalue 引用)。然而(与此相关),C++11 标准不再要求 STL 元素类型必须是“可复制构造”和“可赋值”的;只要是可移动构造和可移动赋值的就足够了。实际上,unique_ptr 实例仅可移动构造和可移动赋值。但是,auto_ptr 实例也是如此!因此,在 C++11 中,使用 auto_ptr 所能做的与使用 unique_ptr 相同。 - Marc van Leeuwen
除非您根据需要进行“重置”和“释放”,否则请勿使用@MarcvanLeeuwen。 - ratchet freak
2
@ratchetfreak:嗯,我不明白。什么?“除非你resetrelease”,我不明白它如何适用于我的评论中的任何内容。请注意,auto_ptrunique_ptr都具有这两种方法,在这两种情况下它们的作用是相同的。 - Marc van Leeuwen

67

auto_ptr复制语义与容器不兼容。

具体而言,将一个 auto_ptr 复制到另一个并不会创建两个相等的对象,因为一个已经失去了指针所有权。

更具体地说,复制一个 auto_ptr 会导致其中一个副本放弃指针。哪个副本留在容器中是未定义的。因此,如果您将 auto_ptr 存储在容器中,则可能随机丢失指针访问权限。


39

因为我认为在过去的将近两年时间里,他可能已经处理了手头的问题。 - Puppy
28
@DeadMG: 是的,你说得对。但那不是我的目的。如果有人在未来浏览这个帖子并想要学习auto_ptr等内容,这些链接肯定会很有帮助。 - Lazer
有很多更新的重复内容。 - Puppy
8
这个问题没有被关闭为重复问题,因此可以继续延伸讨论。Lazer提到了之前没有提到的内容,我猜他是偶然路过。 - Sebastian Mach
第二个链接中对调用sort()后分析问题的解释比这里所有答案都更清晰。 - chaosink

17

STL容器需要能够复制存储在其中的项目,并且设计为期望原始项和副本是等效的。自动指针对象具有完全不同的合约,即复制创建所有权的转移。这意味着auto_ptr容器将根据使用情况展现出奇怪的行为。

在Effective STL(Scott Meyers)第8项中详细描述了可能出现的问题,而在Effective C++(Scott Meyers)第13项中则提供了不太详细的描述。


12

STL容器存储包含项的副本。当auto_ptr被复制时,它会将旧指针设置为null。许多容器方法因此行为而失效。


但是,当使用unique_ptr时,由于只有一个unique_ptr可以拥有对象的所有权,因此您基本上会得到相同的结果? - Tracer
2
@Tracer unique_ptr 就像任何合适的 C++11 对象一样,只能在移动构造或分配时传递其资源的所有权,确保程序员必须有意识地传递 std::move(sourceObject) 或临时对象,而不是传递 lvalue 并且不直观/不可预测地通过复制赋值进行突变...正如在这里彻底强调的那样,这是 auto_ptr 的一个核心问题。 - underscore_d

4

C++03标准(ISO-IEC 14882-2003)在第20.4.5段第3句中说:

[...] [注意: [...] auto_ptr不满足标准库容器元素的CopyConstructible和Assignable要求,因此使用auto_ptr实例化标准库容器将导致未定义行为。— end note]

C++11标准(ISO-IEC 14882-2011)在附录D.10.1段第3句中说:

[...] 注意:[...] auto_ptr的实例满足MoveConstructible和MoveAssignable的要求,但不满足CopyConstructible和CopyAssignable的要求。— end note]

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代码可能无法在此国际标准中编译。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接