Поскольку конструктор копирования
MyClass(const MyClass&);
и оператор = перегрузка
MyClass& operator = (const MyClass&);
имеют практически одинаковый код, одинаковый параметр и отличаются только при возврате, возможно ли иметь общую функцию для них обоих?
c++
variable-assignment
copy-constructor
c++-faq
MPelletier
источник
источник
Ответы:
Да. Есть два распространенных варианта. Один из них, который обычно не рекомендуется, - это
operator=
явный вызов конструктора копирования:MyClass(const MyClass& other) { operator=(other); }
Однако предоставление товара
operator=
- это проблема, когда дело касается старого состояния и проблем, возникающих из-за самостоятельного назначения. Кроме того, все элементы и базы сначала инициализируются по умолчанию, даже если они должны быть назначены fromother
. Это может быть не действительным даже для всех членов и баз, и даже там, где это допустимо, оно семантически избыточно и может быть практически дорогостоящим.Все более популярным решением становится реализация
operator=
с использованием конструктора копирования и метода подкачки.MyClass& operator=(const MyClass& other) { MyClass tmp(other); swap(tmp); return *this; }
или даже:
MyClass& operator=(MyClass other) { swap(other); return *this; }
swap
Функция , как правило , просто напишите как это просто обменивает собственность внутренностей и не должно очистить существующее состояние или выделять новые ресурсы.Преимущества идиомы копирования и обмена заключаются в том, что она автоматически обеспечивает безопасное самоназначение и, при условии, что операция обмена не выполняется, также строго безопасна в отношении исключений.
Для обеспечения строгой защиты от исключений «рукописный» оператор присваивания обычно должен выделить копию новых ресурсов перед отменой выделения старых ресурсов правопреемника, чтобы в случае возникновения исключения при распределении новых ресурсов старое состояние все еще могло быть возвращено в . Все это бесплатно с копированием и заменой, но, как правило, более сложное и, следовательно, подверженное ошибкам выполнение с нуля.
Единственное, о чем следует быть осторожным, - это убедиться, что метод подкачки является истинным свопом, а не методом по умолчанию,
std::swap
который использует сам конструктор копирования и оператор присваивания.Обычно используется поэлементный
swap
.std::swap
работает, и для всех основных типов и типов указателей гарантируется отсутствие выброса. Большинство интеллектуальных указателей также можно поменять местами с гарантией отсутствия выброса.источник
operator=
с помощью ctor копирования на самом деле довольно плохо, потому что он сначала инициализирует все значения по умолчанию, просто чтобы сразу же переопределить их значениями другого объекта.assign
в некоторых случаях (для легких классов) разумно иметь функцию- член, используемую как объектом копирования, так и оператором присваивания. В других случаях (ресурсоемкие / использующие случаи, дескриптор / тело) копирование / своп - это, конечно, путь.Конструктор копирования выполняет первоначальную инициализацию объектов, которые раньше были необработанной памятью. Оператор присваивания OTOH заменяет существующие значения новыми. Чаще, чем никогда, это связано с отказом от старых ресурсов (например, памяти) и выделением новых.
Если между ними есть сходство, то это то, что оператор присваивания выполняет уничтожение и копирование. Некоторые разработчики фактически реализовывали назначение путем уничтожения на месте с последующим копированием размещения. Однако это очень плохая идея. (Что, если это оператор присваивания базового класса, который вызывается во время присваивания производного класса?)
То, что обычно считается канонической идиомой в настоящее время, используется,
swap
как предложил Чарльз:MyClass& operator=(MyClass other) { swap(other); return *this; }
Здесь используется копирующее построение (обратите внимание, что
other
оно копируется) и разрушение (оно разрушается в конце функции) - и оно также использует их в правильном порядке: построение (может потерпеть неудачу) до разрушения (не должно терпеть неудачу).источник
swap
быть объявленvirtual
?Что-то меня беспокоит:
MyClass& operator=(const MyClass& other) { MyClass tmp(other); swap(tmp); return *this; }
Во-первых, чтение слова «обмен», когда мой разум думает «скопировать», раздражает мой здравый смысл. Кроме того, я сомневаюсь в цели этого причудливого трюка. Да, любые исключения при создании новых (скопированных) ресурсов должны происходить до свопа, что кажется безопасным способом убедиться, что все новые данные заполнены, прежде чем запускать их.
Хорошо. Итак, как насчет исключений, которые происходят после обмена? (когда старые ресурсы разрушаются, когда временный объект выходит за пределы области видимости) С точки зрения пользователя назначения, операция завершилась неудачно, за исключением того, что это не так. У этого есть огромный побочный эффект: копия действительно произошла. Не удалось очистить только некоторые ресурсы. Состояние целевого объекта было изменено, хотя снаружи кажется, что операция завершилась неудачно.
Итак, предлагаю вместо «свопа» сделать более естественный «перенос»:
MyClass& operator=(const MyClass& other) { MyClass tmp(other); transfer(tmp); return *this; }
По-прежнему существует создание временного объекта, но следующее немедленное действие - освободить все текущие ресурсы места назначения перед перемещением (и присвоением NULL, чтобы они не освобождались дважды) ресурсов источника в него.
Вместо {строить, перемещать, разрушать} я предлагаю {строить, разрушать, перемещать}. Ход, который является наиболее опасным действием, делается последним после того, как все остальное было решено.
Да, отказ от разрушения является проблемой в любой схеме. Данные либо повреждены (скопированы, когда вы не думали, что это было), либо потеряны (освобождаются, когда вы не думали, что это было). Лучше потерянное, чем испорченное. Нет данных лучше плохих.
Перенос вместо свопа. Во всяком случае, это мое предложение.
источник
First, reading the word "swap" when my mind is thinking "copy" irritates
-> Как писатель библиотеки, вы обычно знакомы с общепринятыми методами (копирование + замена), и суть в том, чтоmy mind
. Ваш разум фактически скрыт за публичным интерфейсом. В этом суть повторно используемого кода.