Занимательная задачка: понимание deep copy

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Аватара пользователя
Din666
Сообщения: 52
Зарегистрирован: 17 июл 2015, 13:25
Откуда: Moscow
Контактная информация:

Да возможно умный указатель любого типа и излишен. Однако в данном случае надо внимательно посмотреть на std/boost ::shared_ptr, там есть перегруженный operator bool().
и обращение к данным контролируется void copy() ....... if ( rhs.m_pHuge )
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

Если честно, то и unique_ptr тоже не особо нужен, так как у нас есть полный контроль за конструктором и деструктором CWorker.
Ну не знаю, если в Visual C++, gcc или clang включить генерацию asm листингов в режиме Release компиляции и поизучать их немного, то можно увидеть что operator* и operator-> они инлайнят чисто и никакого оверхеда не создают. При этом от всех возможных неприятных кейсов связанных с исключениями в конструкторах unique_ptr/make_unique защищают.
2B OR NOT(2B) = FF
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Din666 писал(а):и обращение к данным контролируется void copy() ....... if ( rhs.m_pHuge )
У этого if нет else.

Смотри, вот пример:

Код: Выделить всё

CWorker w1;
w1.Process();

CWorker w2;
w1 = w2;
Так как w2 внутри содержит nullptr, то после присваивания в w1 тоже должен быть nullptr, а по факту у нас там остаётся указатель на экземпляр CHuge.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Absurd писал(а):Ну не знаю, если в Visual C++, gcc или clang включить генерацию asm листингов в режиме Release компиляции и поизучать их немного, то можно увидеть что operator* и operator-> они инлайнят чисто и никакого оверхеда не создают. При этом от всех возможных неприятных кейсов связанных с исключениями в конструкторах unique_ptr/make_unique защищают.
Я не говорю, что использование std::unique_ptr излишне. Оно здесь более, чем приемлемо. Но можно и не "заморачиваться", так как, на самом деле, тело конструктора настолько тривиально, что при всём желании в нём не добьёшься partial initialization, от которого нас бы спас умный поинтер :)

А вот использование std::shared_ptr действительно будет кривоватым. Контекст применение не тот, как следствие есть ненужный оверхед.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Din666
Сообщения: 52
Зарегистрирован: 17 июл 2015, 13:25
Откуда: Moscow
Контактная информация:

Romeo писал(а):У этого if нет else.
ота фак же, точно ))))) else m_pHuge.reset (); )))))))
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Совершенно верно. Итого, вот честный вариант:

Код: Выделить всё

class CWorker
{
public:
   CWorker() = default;

   CWorker(const CWorker& rhs)
   {
      assign(rhs);
   }

   CWorker & operator=(const CWorker& rhs)
   {
      if (this != &rhs)
      {
         assign(rhs);
      }
      return *this;
   }

   void Process()
   {
      if (!m_pHuge)
      {
         m_pHuge.reset(new CHuge);
      }
      m_pHuge->DoAction();
   }

private:
   inline void assign(const CWorker& rhs)
   {
      m_pHuge.reset(rhs.m_pHuge ? new CHuge(rhs.m_pHuge.get()) : nullptr); 
   }

   std::unique_ptr<CHuge> m_pHuge;
};
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Din666
Сообщения: 52
Зарегистрирован: 17 июл 2015, 13:25
Откуда: Moscow
Контактная информация:

и таки все равно неправильно )))

если принять что ифейс CHuge скорее всего такой: struct CHuge {void DoAction(){}};

no matching function for call to ‘CHuge::CHuge(std::unique_ptr<CHuge>: :p ointer)’
m_pHuge.reset(rhs.m_pHuge ? new CHuge(rhs.m_pHuge.get()) : nullptr);
note: candidate expects 0 arguments, 1 provided
note: constexpr CHuge::CHuge(const CHuge&)
note: no known conversion for argument 1 from ‘std::unique_ptr<CHuge>: :p ointer {aka CHuge*}’ to ‘const CHuge&’
note: constexpr CHuge::CHuge(CHuge&&)
note: no known conversion for argument 1 from ‘std::unique_ptr<CHuge>: :p ointer {aka CHuge*}’ to ‘CHuge&&’

надо m_pHuge.reset(rhs.m_pHuge ? new CHuge(*rhs.m_pHuge) : nullptr);

и почему в конструкторе копирования отсутствует проверка selfcopy, а если CWorker worker1(worker1); - не знаю наскока это глупо - но собирается )))))
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Извиняюсь, отправил не скомпилив, так как по графику должны были вот-вот отключить свет. Я ведь из Крыма. Действительно закралась ошибка (забыл разыменование). Теперь она исправлена.

По поводу проверки селфкопи в конструкторе копирования скажу, что обычно она в нём не делается, так как нужно быть очень глупым программистом, чтобы написать указанную строку. Ко всему прочему, большинство компиляторов её попросту не скомпилируют, так как мы пытаемся инициализовать объект самим собой, то есть ещё неинициализиваронным объектом.

Код: Выделить всё

#include <memory>

/* testing data section begin */

class CHuge
{
public:
   void DoAction(){}
};

const_expr bool g_bSomeCondition1 = true;
const_expr bool g_bSomeCondition2 = true;
const_expr bool g_bSomeCondition3 = true;

/* testing data section end */

class CWorker
{
public:
   CWorker() = default;

   CWorker(const CWorker& rhs)
   {
      assign(rhs);
   }

   CWorker& operator=(const CWorker& rhs)
   {
      if (this != &rhs)
      {
         assign(rhs);
      }
      return *this;
   }

   void Process()
   {
      if (!m_pHuge)
      {
         m_pHuge.reset(new CHuge);
      }
      m_pHuge->DoAction();
   }

private:
   inline void assign(const CWorker& rhs)
   {
      m_pHuge.reset(rhs.m_pHuge ? new CHuge(*rhs.m_pHuge) : nullptr); 
   }

   std::unique_ptr<CHuge> m_pHuge;
};

int main()
{
   CWorker worker1;
   if (g_bSomeCondition1)
   {
      worker1.Process();
   }

   CWorker worker2;
   if (g_bSomeCondition2)
   {
      worker2 = worker1;
   }

   if (g_bSomeCondition3)
   {
      worker1.Process();      
      worker2.Process();
   }

   return 0;
}
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Din666
Сообщения: 52
Зарегистрирован: 17 июл 2015, 13:25
Откуда: Moscow
Контактная информация:

ok. осталась последняя мелочь и код станет идеальным )). А зачем делать лишний вызов метода get() ...... *rhs.m_pHuge.get(), если можно просто *rhs.m_pHuge разименовать.
А какже: 'не надо умных указателей, надо все вручную писать, если мы "имеем полный контроль"' ? ))
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

По этому поводу я уже писал выше. Я был резко против лишь shared_ptr, а против unique_ptr ничего не имел против :)

Написать на сырых поинтерах можно. Более того, это было бы даже более поучительно, чем использовать unique_ptr, ведь задача больше обучающая, чем реальная... Но стало лень после того, как понял, что по-хорошему помимо конструктора, деструктора, придётся заимплементить ещё и move-конструктор (в main он не используется, но для полной картинки необходим).
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Ответить