如何在一个必须复制构造的类中使用std :: auto_ptr?

时间:2009-07-17 11:11:06

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

我有一个类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
  • 的原因
  • 我非常愿意考虑提升智能指针,但如果有替代方案,我希望保证这种独特性。

9 个答案:

答案 0 :(得分:16)

您可能想尝试以下代码:

    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,因为它不支持共享所有权。在这种情况下考虑使用来自shared_ptrBoost.SmartPtr(将包含在新的C ++标准中)。或者任何其他共享指针实现,因为这是一个很常见的概念,可以使用很多实现。

答案 1 :(得分:8)

正如您所发现的那样,您无法像这样复制std::auto_ptr。拥有该对象的副本指向后?相反,你应该使用引用计数智能指针。 Boost libraryshared_ptr你可以使用。

答案 2 :(得分: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);
         }
      }
      // ...
   }

答案 3 :(得分:1)

std::auto_ptr是用于管理C ++中的动态对象的好工具,但为了有效地使用它,了解auto_ptr的工作方式非常重要。 This article解释了应该使用此智能指针的原因,时间和地点。

在您的情况下,首先您应该决定要对auto_ptr中的对象执行什么操作。它应该克隆还是共享?

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

如果要共享,则应使用boost::shared_ptr作为 Martin Liversage 建议。

答案 4 :(得分:1)

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

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

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成员的可复制类强加的。

答案 5 :(得分:0)

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

由于您无法复制它,因此您也无法复制包含 auto_ptr的对象

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

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

在这种情况下,那么你需要让你的拷贝构造函数和赋值运算符接受对它们的参数的非const引用,并在那里执行初始化/赋值。

答案 8 :(得分:-1)

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

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

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