【问题标题】:How do use a std::auto_ptr in a class you have to copy construct?如何在必须复制构造的类中使用 std::auto_ptr?
【发布时间】:2009-07-17 11:11:06
【问题描述】:

我有一个类foo,其中包含一个我想复制构造的 std::auto_ptr 成员,但这似乎是不允许的。作业也有类似的情况。请参见以下示例:

struct foo
{
private:
    int _a;
    std::string _b;
    std::auto_ptr< bar > _c;

public:
    foo(const foo& rhs)
        :   _a(rhs._a)
        ,   _b(rhs._b)

        ,   _c(rhs._c)
                // error: Cannot mutate rhs._c to give up ownership - D'Oh!
    {
    }

    foo& operator=(const foo& rhs)
    {
         _a = rhs._a;
         _b = rhs._b;

         _c = rhs._c;
             // error: Same problem again.
    }
};

我可以将_c 声明为mutable,但我不确定这是否正确。谁有更好的解决方案?

编辑

好的,我没有得到我期望的那种答案,所以我会更具体地说明这个问题。

  • foo 类型的对象在堆栈上创建并按值传递到容器类(不是 stl),然后超出范围。我无法控制容器代码。 (它实际上是一个活跃的队列实现,有错误。)
  • bar 类是一个相当重量级的解析器。它在 newdelete 上的性能很差,所以即使它是可复制构造的,它也太贵了。
  • 我们可以保证,当创建bar 对象时,它一次只需要在一个地方拥有。在这种情况下,它在线程之间传递并在事务完成时被删除。这就是我希望使用std::autp_ptr 的原因。
  • 我非常愿意考虑提升智能指针,但如果有替代方案,我希望能保证这种唯一性。

【问题讨论】:

    标签: c++ smart-pointers auto-ptr


    【解决方案1】:

    您可能想尝试以下代码:

        foo(const foo& rhs)
            :   _a(rhs._a)
            ,   _b(rhs._b)
            ,   _c(_rhs._c.get() ? new bar(*_rhs._c.get()) : 0)
        {
        }
    

    (赋值运算符类似。)

    但是,这只有在 bar 是 CopyConstructible 并且确实符合您的要求时才有效。问题是foo 对象(_rhs 和构造的一个)在_c 中都有不同的指针。

    如果您希望他们共享指针,那么您不能使用auto_ptr,因为它不支持共享所有权。例如,考虑在这种情况下使用来自Boost.SmartPtr@987654321@(它将包含在新的C++ 标准中)。或任何其他共享指针实现,因为这是一个常见的概念,有很多实现可用。

    【讨论】:

    • 即使在编辑之后仍然是最佳答案。 foo 将需要复制或共享 bar,而 auto_ptr 不能很好地共享。由于现在也排除了复制,因此您需要一个 shared_ptr。
    【解决方案2】:

    正如您所发现的,您不能像这样复制std::auto_ptr。拷贝后指向的对象是谁拥有?相反,您应该使用引用计数的智能指针。 Boost library 有一个 shared_ptr 你可以使用。

    【讨论】:

      【解决方案3】:

      首先,我会避免使用 auto_ptr

      所有权转移在某些情况下很好,但我发现它们很少见,而且“成熟”的智能指针库现在很容易获得。 (IIRC auto_ptr 是一种折衷方案,在标准库中至少包含一个示例,而没有良好实现所需的延迟)。

      See, for example here
      or here

      确定语义
      foo 的副本是否应该包含对同一个 bar 实例的引用?在这种情况下,请使用 boost::shared_ptr 或 (boost::intrusive_ptr) 或类似的库。

      或者应该创建一个深层副本? (有时可能需要这样做,例如当有延迟创建状态时)。我不知道该概念的任何标准实现,但构建类似于现有智能指针的方法并不复杂。

         // roughly, incomplete, probably broken:
         template <typename T>
         class deep_copy_ptr
         {
            T * p;
           public:
            deep_copy_ptr()  : p(0) {}
            deep_copy_ptr(T * p_)  : p(p_) {}
            deep_copy_ptr(deep_copy_ptr<T> const & rhs)  
            {
              p = rhs.p ? new T(*rhs.p) : 0;
            }
            deep_copy_ptr<T> & operator=(deep_copy_ptr<T> const & rhs)
            {
               if (p != rhs.p)
               {
                 deep_copy_ptr<T> copy(rhs);
                 swap(copy);
               }
            }
            // ...
         }
      

      【讨论】:

        【解决方案4】:

        std::auto_ptr 是在 C++ 中管理动态对象的好工具,但为了有效地使用它,了解 auto_ptr 的工作原理很重要。 This article 解释了为什么、何时以及何地应该使用这个智能指针。

        在你的情况下,首先你应该决定你想对你的 auto_ptr 中的对象做什么。它应该被克隆还是共享?

        如果应该克隆它,请确保它具有复制构造函数,然后创建一个新的 auto_ptr,其中包含对象的副本,请参阅 Adam Badura 的答案。

        如果应该共享,您应该按照 Martin Liversage 的建议使用 boost::shared_ptr

        【讨论】:

          【解决方案5】:

          如果我有包含 auto_ptr 的类,并且想要深度复制语义,我通常只对具有虚拟复制运算符的类执行此操作,即 clone()。

          然后,在复制构造函数中,我将 auto_ptr 初始化为另一个的 clone();例如

          class Foo
          {
             public:
                Foo(const Foo& rhs) : m_ptr(rhs.m_ptr->clone());
             private:
                std::auto_ptr<T> m_ptr;
          };
          

          clone() 通常实现如下:

          class T
          {
             std::auto_ptr<T> clone() const
             {
                return std::auto_ptr<T>(new T(*this));
             }
          };
          

          我们强加了 T 是可克隆的条件,但这个条件本质上是通过拥有一个带有 auto_ptr 成员的可复制类来强加的。

          【讨论】:

            【解决方案6】:

            auto_ptr 的整体理念是所引用对象只有一个所有者。这意味着您不能在不删除原始所有权的情况下复制指针。

            因为你不能复制它,你也不能复制一个包含auto_ptr的对象。

            您可以尝试使用移动语义,例如使用std::swap 而不是复制。

            【讨论】:

            • -1,复制包含 auto_ptr 的对象很简单。为此,您可以创建一个指向复制的 T 的新 auto_ptr
            • @MSalters:但是您并没有复制 auto_ptr。您正在复制引用的对象。
            【解决方案7】:

            我的第一选择是在这种情况下完全避免使用 auto_ptr。但如果我靠墙支持,我可能会尝试在 _c 的声明中使用关键字 mutable - 这将允许即使从 const 引用修改它。

            【讨论】:

              【解决方案8】:

              鉴于编辑,您似乎想要转让所有权语义。

              在这种情况下,您需要让复制构造函数和赋值运算符接受对其参数的非常量引用,并在那里执行初始化/赋值。

              【讨论】:

                【解决方案9】:

                您不能在涉及auto_ptr&lt;&gt; 的复制构造函数或赋值运算符中使用 const 引用。删除常量。换句话说,使用像

                这样的声明
                foo(foo & rhs);
                foo & operator=(foo & rhs);
                

                标准中明确提到了这些形式,主要是在第 12.8 节中。它们应该可用于任何符合标准的实现。事实上,12.8 的第 5 和 10 段说,如果任何成员需要,隐式定义的复制构造函数和赋值运算符(分别)将采用非常量引用。

                【讨论】:

                • 当然可以使用它们;你只需要重写隐式复制构造函数和赋值运算符。
                • 根据标准,它们隐式复制构造函数和赋值运算符。我给了参考。如果您知道它无法运行的实现,请告诉我们。
                • 您的实现将起作用,我认为我对隐式生成的部分不正确。我不同意的是第一句话,你不能在带有 aut_ptr 的复制构造函数中使用 const 引用。你可以,你只需要让他们做一些不同于默认行为的事情。
                • 你想做什么?您不能只从 const 参数中复制 auto_ptr 。您可以进行深度复制,这在此处是不需要的。您可以创建两个指向同一事物的 auto_ptr,这是等待发生的灾难。
                猜你喜欢
                • 2011-04-27
                • 1970-01-01
                • 2011-03-17
                • 2016-06-03
                • 1970-01-01
                • 2017-08-17
                • 2013-06-02
                • 2016-03-26
                • 1970-01-01
                相关资源
                最近更新 更多