Стандарты программирования на С++. 101 правило и рекомендация | страница 97



для реализации копирующего присваивания посредством копирующего конструктора. Приведенная далее реализация оператора operator= обеспечивает строгую гарантию (см. рекомендацию 71), хотя и ценой создания дополнительного объекта, что может оказаться неприемлемым, если имеется более эффективный способ выполнения безопасного присваивания объектов типа T:

>T& T::operator=(const T& other) { // Вариант 1 (традиционный)

> T temp(other);

> swap(temp);

> return *this;

>}


>T& T::operator=(T temp) { // Вариант 2 (см. рекомендацию 27)

> swap(temp);              // Обратите внимание на передачу

> return *this;            // temp по значению

>}

Но что если тип >U не имеет бессбойной функции обмена, как в случае многих существующих классов, но вам требуется поддержка функции обмена для типа >T? Не все потеряно.

• Если копирующий конструктор и оператор копирующего присваивания >U не дают сбоев, то с объектами типа >U вполне справится >std::swap.

• Если копирующий конструктор >U может давать сбой, вы можете хранить (интеллектуальный) указатель на >U вместо непосредственного члена. Указатели легко обмениваются. Следствием их применения являются дополнительные расходы на одно динамическое выделение памяти и дополнительную косвенность при обращении, но если вы храните все такие члены в едином Pimpl-объекте, то для всех закрытых членов дополнительные расходы вы понесете только один раз (см. рекомендацию 43).

Никогда не пользуйтесь трюком реализации копирующего присваивания посредством копирующего конструирования с использованием непосредственного вызова деструктора и размещающего new, несмотря на то, что такой трюк регулярно "всплывает" в форумах, посвященных С++ (см. также рекомендацию 99). Так что никогда не пишите:

>T& T::operator=(const T& rhs) { // Плохо: анти-идиома

> if (this != &rhs) {

>  this->~T();                   // плохая методика!

>  new(this) T(rhs);             // (см. [Sutter00] §41)

> }

> return *this;

>}

Если объекты вашего типа можно обменять более эффективным способом, чем грубое присваивание, желательно предоставить функцию обмена, не являющуюся членом, в том же пространстве имен, где находится и ваш тип (см. рекомендацию 57). Кроме того, подумайте о специализации >std::swap для ваших собственных нешаблонных типов:

>namespace std {

>template<> void swap(MyType& lhs, MyType& rhs) {

> lhs.swap(rhs); // Для объектов MyType используется

>} // MyType::swap

>}

Стандарт не позволяет вам сделать это, если >MyType сам является шаблонным классом. К счастью, иметь такую специализацию хорошо, но не обязательно; основная методика состоит в обеспечении функции