Когда должен я определять свою собственную копию ctor и оператор присваивания

Я читаю эффективный C++ в Объекте 5, он упомянул два случая, в которых я должен определить оператор присваивания копии сам. Случай является классом, которые содержат константу и ссылочных участников.

Я пишу для выяснения, каковы общее правило или случай, в котором я должен определить своего собственного конструктора копии и оператор присваивания?

Я также хотел бы знать, когда я должен определить своего собственного конструктора и деструктор.

Большое спасибо!

5
задан skydoor 27 February 2010 в 15:57
поделиться

2 ответа

Вы должны создать свой собственный конструктор копирования и оператор присваивания (и обычно конструктор по умолчанию), когда:

  • Вы хотите, чтобы ваш объект был скопирован или назначен, или поместить в стандартный контейнер, такой как вектор
  • Конструктор копирования и оператор присваивания по умолчанию не будут работать правильно.

Рассмотрим следующий код:

class A; // defined elsewhere
class B {
private:
    A *my_very_own_a;
};

Если вы разрешите конструктору автоматического копирования скопировать B , он скопирует значение указателя A * , чтобы копия указывала на тот же экземпляр A , что и оригинал. Но частью конструкции этого класса является то, что каждый B имеет свой собственный A , поэтому конструктор автоматического копирования нарушил этот контракт. Поэтому вам нужно написать свой собственный конструктор копирования, который создаст новый A для нового B , на который он будет указывать.

Однако рассмотрим этот случай:

class A; // defined elsewhere
class B {
private:
    A *shared_reference_to_a;
};

Здесь каждый B содержит указатель на A , но контракт класса не требует уникального A ] для каждого B . Так что конструктор автоматического копирования вполне может сделать здесь то, что нужно.

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

Примером первой ситуации может быть B == диалоговое окно, A == кнопка. Если вы создаете копию диалогового окна, вероятно, ему также потребуется копия всего содержимого.

Примером второго может быть B == диалоговое окно, A == ссылка на диспетчер окон. Если вы копируете диалоговое окно, копия, вероятно, существует в том же диспетчере окон, что и оригинал.

5
ответ дан 14 December 2019 в 04:36
поделиться

Конструктор копирования по умолчанию часто заставляет два объекта «указывать» на общий участок памяти. Это связано с тем, что все, что делает конструктор копирования по умолчанию, - это присваивание элементов. Так что, если у вас есть член-указатель, это может вызвать у вас проблемы

    CClass::CClass(const CClass& src)
    {
       // these two point to the same place in memory now
       // you really aught to allocate new memory
       m_ptr = src.m_ptr;

       // not a big deal for members that aren't "pointing" elsewhere
       // this copy works great
       m_int = src.m_int;
    }

По сути, вы не хотите попадать в ситуацию, когда два экземпляра «указывают» на одно и то же место в памяти. Это может произойти, когда у вас есть указатели в качестве членов, которые указывают на динамически выделяемую память. Это также может происходить со ссылками, относящимися к чему-то за пределами самого класса. Это может произойти с дескрипторами файлов, где вам может потребоваться дублировать / повторно открыть новый файл.

Фактически, последний случай, вероятно, легче всего осмыслить. Представьте, что у ваших двух объектов C ++ есть файл журнала. Без правильно работающего конструктора копирования они оба могут вести журнал в одном файле. Что будет, если в деструкторе классов закрыть файл? Что ж, тогда файл журнала второго объекта C ++ будет закрыт из-под него. Одним из решений может быть создание конструктора копирования, который создает новый файл для вновь созданного экземпляра. Поэтому, когда первый объект C ++ уходит, он не берет с собой файл журнала второго объекта C ++. У них есть 2 независимых файла журнала, они не могут мешать друг другу.

Этот пример можно распространить на динамическую память. Без конструктора копирования у вас будет просто 2 указателя, указывающих на один и тот же адрес. Если деструктор одного удаляет эту память, другой может не осознавать, что память, на которую он указывает, исчезла.Не менее важно то, что вы, вероятно, не хотите, чтобы один объект записывался в член другого объекта :).

3
ответ дан 14 December 2019 в 04:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: