Как использовать std :: auto_ptr в классе, который вы должны скопировать конструкции? - PullRequest
8 голосов
/ 17 июля 2009

У меня есть класс 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, но я не уверен, что это правильно. У кого-нибудь есть лучшее решение?

EDIT

ОК, я не получаю того ответа, который ожидал, поэтому я немного подробнее расскажу о проблеме.

  • Объект типа foo создается в стеке и передается по значению в класс контейнера (не stl), а затем выходит из области видимости. Я не имею никакого контроля над кодом контейнера. (На самом деле это активная реализация очереди с ошибками.)
  • Класс bar является довольно тяжелым парсером. Он имеет очень низкую производительность на new и delete, поэтому даже если бы его можно было копировать, это было бы слишком дорого.
  • Мы можем гарантировать, что при создании объекта bar он будет принадлежать только одному месту за раз. В этом случае он передается между потоками и удаляется по завершении транзакции. Вот почему я надеялся использовать std::autp_ptr.
  • Я очень готов рассмотреть вопрос об улучшении умных указателей, но я надеялся гарантировать эту уникальность, если есть альтернатива.

Ответы [ 9 ]

16 голосов
/ 17 июля 2009

Возможно, вы захотите попробовать следующий код:

    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, поскольку он не поддерживает совместное владение. В этом случае рассмотрим, например, использование <a href="http://www.boost.org/doc/libs/1_39_0/libs/smart_ptr/shared_ptr.htm" rel="noreferrer">shared_ptr</a> из Boost.SmartPtr (которое будет включено в новый стандарт C ++). Или любая другая реализация общего указателя, так как это настолько распространенная концепция, что доступно множество реализаций.

8 голосов
/ 17 июля 2009

Как вы обнаружили, вы не можете скопировать std::auto_ptr подобным образом. После копии, кому принадлежит объект, на который указывает? Вместо этого вы должны использовать умный указатель с подсчетом ссылок. Библиотека Boost имеет shared_ptr , который вы можете использовать.

3 голосов
/ 17 июля 2009

Во-первых, я бы избегал auto_ptr

Передача права собственности хороша в некоторых сценариях, но я считаю, что они редки, и «полноценные» библиотеки интеллектуальных указателей теперь легко доступны. (IIRC auto_ptr был компромиссом, включив хотя бы один пример в стандартную библиотеку, без задержек, которые потребовались бы для хорошей реализации).

См., Например, здесь
или здесь

Определить семантику
Должна ли копия 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);
         }
      }
      // ...
   }
1 голос
/ 17 июля 2009

Если у меня есть класс, содержащий 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.

1 голос
/ 17 июля 2009

std::auto_ptr - хороший инструмент для управления динамическими объектами в C ++, но для его эффективного использования важно понять, как работает auto_ptr. В этой статье объясняется, почему, когда и где следует использовать этот умный указатель.

В вашем случае, прежде всего, вы должны решить, что вы хотите сделать с объектом внутри вашего auto_ptr. Должен ли он быть клонирован или распространен?

Если он должен быть клонирован, убедитесь, что у него есть конструктор копирования, а затем создайте новый auto_ptr, содержащий копию вашего объекта, см. Adam Badura .

Если он должен использоваться совместно, вы должны использовать boost :: shared_ptr в качестве Martin Liversage .

0 голосов
/ 17 июля 2009

Учитывая редактирование, кажется, вы хотите семантику передачи собственности.

В этом случае вы захотите, чтобы ваш конструктор копирования и оператор присваивания принимали неконстантные ссылки на свои аргументы и выполняли там инициализацию / присваивание.

0 голосов
/ 17 июля 2009

Вы не можете использовать константные ссылки в конструкторе копирования или операторе присваивания, который включает auto_ptr<>. Удалить конст. Другими словами, используйте объявления типа

foo(foo & rhs);
foo & operator=(foo & rhs);

Эти формы прямо упомянуты в Стандарте, в первую очередь в разделе 12.8. Они должны быть применимы в любой реализации, соответствующей стандартам. Фактически, в параграфах 5 и 10 из 12.8 говорится, что неявно определенный конструктор копирования и оператор присваивания (соответственно) будут использовать неконстантную ссылку, если это потребуется любому из членов.

0 голосов
/ 17 июля 2009

Мой первый выбор - вообще не использовать auto_ptr в этой ситуации. Но если бы меня прижали к стене, я мог бы попытаться использовать ключевое слово mutable в объявлении _c - это позволит изменить его даже из константной ссылки.

0 голосов
/ 17 июля 2009

Вся идея auto_ptr в том, что существует только один владелец упомянутого объекта. Это означает, что вы не можете скопировать указатель без удаления первоначального владельца.

Поскольку вы не можете скопировать его, вы также не можете скопировать объект , содержащий и auto_ptr.

Вы можете попытаться использовать семантику перемещения, например, используя std::swap вместо копии.

...