【问题标题】:auto_ptr designauto_ptr 设计
【发布时间】:2009-01-15 02:48:42
【问题描述】:

在我看来,一个类应该提供一个定义良好的抽象,并且不应该在没有类知识的情况下修改私有成员。但是当我检查“auto_ptr”(或任何其他智能指针)时,违反了这条规则。请看以下代码

class Foo{
public:
   Foo(){}
};

int main(int argc, char* argv[])
{
   std::auto_ptr<Foo> fooPtr(new Foo);
   delete fooPtr.operator ->();
   return 0;
}

运算符重载 (->) 给出了底层指针,它可以在不知道“auto_ptr”的情况下进行修改。我不能认为这是一个糟糕的设计,因为智能指针是由 C++ 极客设计的,但我想知道他们为什么允许这样做。有没有办法写一个没有这个问题的智能指针。

欣赏你的想法。

【问题讨论】:

  • 我想不出很多有用的类没有这样的东西。它很方便,但不要傻了。 delete[] &amp;vector[0];

标签: c++


【解决方案1】:

智能指针应该具有两个理想的属性:

  1. 可以检索原始指针(例如,用于传递给遗留库函数)
  2. 无法检索原始指针(防止双重删除)

很明显,这些属性是矛盾的,不能同时实现!甚至Boost的shared_ptr&lt;Foo&gt;等人。有get(),所以他们有这个“问题”。在实践中,第一个更重要,所以第二个必须走。

顺便说一句,当普通的旧 get() 方法导致同样的问题时,我不确定你为什么要使用稍微晦涩的 operator-&gt;()

std::auto_ptr<Foo> fooPtr(new Foo);
delete fooPtr.get();

【讨论】:

  • Re“用于传递给遗留库函数”:不需要调用遗留。任何不关心所有权的函数不应该将智能指针作为参数,而是将原始指针(或引用)作为参数。除了所有权可能会妨碍这一事实(许多智能指针不允许多个所有者)之外,函数不应将特定品牌的所有权管理强加给其客户端,除非它必须这样做。
  • @MarcvanLeeuwen:好点子,虽然我会将“不关心所有权”替换为“不获取所有权”——每个接口都应该关心(即明确)它的所有权语义。
  • @MarcvanLeeuwen:实际上...你能想到一种情况,非所有权获取函数通过T*而不是T&amp;来接受参数会更好吗?我不能,而且我认为后者更安全。
  • 我的意思是“不关心”,因为无论函数做什么都与所有权无关,而不是它想要造成内存泄漏。但是,除了获取所有权之外,它还可以放弃所有权,方法是存储由可修改引用传递的指针所拥有的东西。而对于另一条评论,T* 是必要的最明显情况是指针可能为空。一个更投机取巧的论点也可能是您知道该函数无论如何都需要初始化一个局部指针变量。你不妨传递那个指针。
【解决方案2】:

为了提供对底层对象的快速、方便、“类似指针”的访问,不幸的是,operator-> 不得不稍微“泄露”它的抽象。否则,智能指针将不得不手动包装所有允许公开的成员。这些要么需要实例化智能指针的部分进行大量“配置”工作,要么需要 C++ 中不存在的元编程级别。此外,正如 pyrsta 所指出的,即使这个漏洞被堵住了,仍然有许多其他(也许是非标准的)方法可以颠覆 C++ 的访问控制机制。

【讨论】:

  • 这很有意义。谢谢
【解决方案3】:

有没有什么办法可以写出没有这个问题的智能指针。

这并不容易,而且通常不会(即,您不能为每个通用的 Foo 类都这样做)。

我能想到的唯一方法是更改​​Foo 类的声明:将Foo 析构函数设为私有(或将私有delete 运算符定义为@ 的成员) 987654324@ 类),并在Foo 类的声明中指定std::auto_ptr&lt;Foo&gt;friend

【讨论】:

  • 是的,这是我认为的唯一方法。但是必须修改 Foo 以及将与 auto_ptr 一起使用的所有类。这将是矫枉过正。
  • 使用 std::auto_ptr 的人一定知道它是什么,以及如何使用它。有时,C++ 让坏事变得不可能;但有时它不能,它只会让好事变得容易(这里的“好事”是“记得删除对象”,坏事是“多次删除”)。
  • 在 C++ 中,开发人员可以使用所有工具来破坏程序。最好的办法是记录行为以及代码的正确使用。并遵循其他地方记录的最佳实践。 ;)
【解决方案4】:

不,没有办法在 C++ 中完全禁止这种不良用法。

作为一般规则,任何库代码的用户都不应在任何包装指针上调用 delete,除非特别说明。在我看来,所有现代 C++ 代码的设计都应该让类的用户永远不必承担手动释放她获得的资源的全部责任(即改用RAII)。

旁注:std::auto_ptr&lt;T&gt; 不再是最佳选择。它在复制时的不良行为可能导致严重的编码错误。通常更好的主意是改用std::tr1::scoped_ptr&lt;T&gt;std::tr1::shared_ptr&lt;T&gt; 或它们的Boost 变体。

此外,在 C++0x 中,std::unique_ptr&lt;T&gt; 将在功能上取代 std::auto_ptr&lt;T&gt; 作为更安全的类。可以在here 找到有关该主题的一些讨论以及最近 C++03 对 unique_ptr 仿真的实现。

【讨论】:

  • 谢谢。我使用 auto_ptr 只是为了解释。我正在使用 boost::shared_ptr。 RAII 看起来很有趣
【解决方案5】:

我不认为这表明 auto_ptr 存在封装问题。每当处理拥有的指针时,人们了解谁拥有什么是至关重要的。在 auto_ptr 的情况下,它拥有它持有的指针[1];这是 auto_ptr 抽象的一部分。因此,以任何其他方式删除该指针都违反了 auto_ptr 提供的合同。

我同意误用 auto_ptr[2] 相对容易,这很不理想,但在 C++ 中,你永远无法避免“谁拥有这个指针?”的基本问题,因为为了更好或更糟糕的是,C++ 不会为您管理内存。

[1] 来自 cplusplus.com 的引述:“auto_ptr 对象具有获取分配给它们的指针所有权的特性”:http://www.cplusplus.com/reference/std/memory/auto_ptr/

[2] 例如,你可能误认为它具有值语义,并将其用作向量模板参数:http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=4&ved=0CEEQFjAD&url=http%3A%2F%2Fwww.gamedev.net%2Ftopic%2F502150-c-why-is-stdvectorstdauto_ptrmytype--bad%2F&ei=XU1qT5i9GcnRiAKCiu20BQ&usg=AFQjCNHigbgumbMG3MTmMPla2zo4LhaE1Q&sig2=WSyJF2eWrq2aB2qw8dF3Dw

【讨论】:

    【解决方案6】:

    我认为这个问题解决了一个非问题。智能指针用于管理指针的所有权,如果这样做会使指针不可访问,它们就达不到目的。

    还要考虑这一点。任何容器类型都会为您提供迭代器;如果it 是这样一个迭代器,那么&amp;*it 是指向容器中项目的指针;如果你说delete &amp;*it,那么你就死定了。但是暴露其项目的地址并不是容器类型的缺陷。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-05
      • 1970-01-01
      • 2011-03-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多