Читаем Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ полностью

Традиционный способ предотвратить эту ошибку состоит в том, что нужно выполнить проверку совпадения в начале operator=:

Widget&

Widget::operator=(const Widget& rhs) // небезопасная реализация operator=

{

if(this == &rhs) return *this; // проверка совпадения: если

// присваивание самому себе, то

// ничего не делать

delete pb;

pb = new Bitmap(*rhs.pb);

return *this;

}

Это решает проблему, но я уже упоминал, что предыдущая версия оператора присваивания была не только опасна в случае присваивания себе, но и небезопасна в смысле исключений, и последняя опасность остается актуальной во второй версии. В частности, если выражение «new Bitmap» вызовет исключение (либо по причине недостатка свободной памяти, либо исключение возбудит конструктор копирования Bitmap), то Widget также будет содержать указатель на несуществующий Bitmap. Такие указатели – источник неприятностей. Их нельзя безопасно удалить, их даже нельзя разыменовывать. А вот потратить массу времени на отладку, выясняя, откуда они взялись, – это можно.

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

Widget& Widget::operator=(const Widget& rhs)

{

Bitmap *pOrig = pb; // запомнить исходный pb

pb = new Bitmap(*rhs.pb); // установить указатель pb на копию *pb

delete pOrig; // удалить исходный pb

return *this;

}

Теперь, если «new Bitmap» возбудит исключение, то pb (и объект Widget, которому он принадлежит) останется неизменным. Даже без проверки на совпадение здесь обрабатывается присваивание самому себе, потому что мы сделали копию исходного объекта Bitmap, удалили его, а затем направили указатель на сделанную копию. Возможно, это не самый эффективный способ обработать присваивание самому себе, но он работает.

Если вы печетесь об эффективности, то можете вернуть проверку на совпадение в начале функции. Но прежде спросите себя, как часто может происходить присваивание самому себе, потому что выполнение проверки тоже не обходится даром. Это делает код (исходный и объектный) чуть больше, а ветвление несколько снижает скорость исполнения. Эффективность предварительной выборки команд, кэширования и конвейеризации тоже может пострадать.

Альтернативой ручному упорядочиванию предложений в operator= может быть обеспечение и безопасности в смысле исключений, и безопасности присваивания самому себе за счет применения техники «копирования с обменом» («copy and swap»). Она тесно связана с безопасностью в смысле исключений, поэтому рассматривается в правиле 29. Тем не менее это достаточно распространенный способ написания operator=, и на него стоит взглянуть:

class Widget {

...

void swap(Widget& rhs); // обмен данными *this и rhs

... // см. подробности в правиле 29

};

Widget& Widget:: operator=(const Widget& rhs)

{

Widget temp(rhs); // создать копию данных rhs

swap(tmp); // обменять данные *this с копией

return *this;

}

Здесь мы пользуемся тем, что: (1) оператор присваивания можно объявить как принимающим аргумент по значению и (2) передача объекта по значению означает создание копии этого объекта (см. правило 20):

Widget& Widget::operator=(Widget rhs) // rhs – копия переданного объекта

{ // обратите внимание на передачу по

// значению

swap(rhs); // обменять данные *this с копией

return *this;

}

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

Что следует помнить
Перейти на страницу:

Похожие книги

1С: Бухгалтерия 8 с нуля
1С: Бухгалтерия 8 с нуля

Книга содержит полное описание приемов и методов работы с программой 1С:Бухгалтерия 8. Рассматривается автоматизация всех основных участков бухгалтерии: учет наличных и безналичных денежных средств, основных средств и НМА, прихода и расхода товарно-материальных ценностей, зарплаты, производства. Описано, как вводить исходные данные, заполнять справочники и каталоги, работать с первичными документами, проводить их по учету, формировать разнообразные отчеты, выводить данные на печать, настраивать программу и использовать ее сервисные функции. Каждый урок содержит подробное описание рассматриваемой темы с детальным разбором и иллюстрированием всех этапов.Для широкого круга пользователей.

Алексей Анатольевич Гладкий

Программирование, программы, базы данных / Программное обеспечение / Бухучет и аудит / Финансы и бизнес / Книги по IT / Словари и Энциклопедии
1С: Управление торговлей 8.2
1С: Управление торговлей 8.2

Современные торговые предприятия предлагают своим клиентам широчайший ассортимент товаров, который исчисляется тысячами и десятками тысяч наименований. Причем многие позиции могут реализовываться на разных условиях: предоплата, отсрочка платежи, скидка, наценка, объем партии, и т.д. Клиенты зачастую делятся на категории – VIP-клиент, обычный клиент, постоянный клиент, мелкооптовый клиент, и т.д. Товарные позиции могут комплектоваться и разукомплектовываться, многие товары подлежат обязательной сертификации и гигиеническим исследованиям, некондиционные позиции необходимо списывать, на складах периодически должна проводиться инвентаризация, каждая компания должна иметь свою маркетинговую политику и т.д., вообщем – современное торговое предприятие представляет живой организм, находящийся в постоянном движении.Очевидно, что вся эта кипучая деятельность требует автоматизации. Для решения этой задачи существуют специальные программные средства, и в этой книге мы познакомим вам с самым популярным продуктом, предназначенным для автоматизации деятельности торгового предприятия – «1С Управление торговлей», которое реализовано на новейшей технологической платформе версии 1С 8.2.

Алексей Анатольевич Гладкий

Финансы / Программирование, программы, базы данных