Читаем Стандарты программирования на С++ полностью

Распространенной ошибкой (и не только среди новичков) является игнорирование семантики копирования и присваивания при определении класса. Это характерно для маленьких вспомогательных классов, таких как предназначенные для поддержки идиомы RAII (см. рекомендацию 13).

Убедитесь, что ваш класс предоставляет осмысленное копирование (или не предоставляет его вовсе). Вот возможные варианты.

• Явное запрещение обеих функций. Если копирование для вашего типа лишено смысла, запретите как копирующее конструирование, так и копирующее присваивание, объявив их как закрытые нереализованные функции:

class T { // ...

private:                 // Делаем T некопируемым

 T(const T&);            // Функция не реализована

 T& operator=(const T&); // Функция не реализована

};

• Явное написание обеих функций. Если для объектов T предусмотрены копирование и копирующее присваивание, но корректное копирующее поведение отличается от поведения сгенерированных компилятором версий, то следует явно написать обе функции и сделать их не закрытыми.

• Использование сгенерированных компилятором версий, предпочтительно с явным комментарием. В противном случае, если копирование имеет смысл и поведение по умолчанию корректно, эти функции можно не объявлять самостоятельно и позволить компилятору самому сгенерировать их. Следует явно комментировать корректность поведения по умолчанию, чтобы читатели вашего кода знали, что вы преднамеренно не объявили данные функции.

Заметим, что запрещение копирования и копирующего присваивания означает, что вы не можете поместить объекты T в стандартные контейнеры. Это не обязательно плохо; очень может быть, что вы в любом случае не захотите хранить такие объекты в контейнерах. (Тем не менее, вы все равно можете поместить эти объекты в контейнер, если будете хранить их посредством интеллектуальных указателей; см. рекомендацию 79).

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

Ссылки

[Dewhurst03] §88 • [Meyers97] §11 • [Stroustrup00] §11.2.2

<p>54. Избегайте срезки. Подумайте об использовании в базовом классе клонирования вместо копирования</p>Резюме

Срезка объектов происходит автоматически, невидимо и может приводить к полному разрушению чудесного полиморфного дизайна. Подумайте о полном запрете копирующего конструктора и копирующего присваивания в базовых классах. Вместо них можно использовать виртуальную функцию-член Clone, если пользователям вашего класса необходимо получать полиморфные (полные, глубокие) копии.

Обсуждение

Когда вы строите иерархию классов, обычно она предназначена для получения полиморфного поведения. Вы хотите, чтобы объекты, будучи созданными, сохраняли свой тип и идентичность. Эта цель вступает в конфликт с обычной семантикой копирования объектов в C++, поскольку копирующий конструктор не является виртуальным и не может быть сделан таковым. Рассмотрим следующий пример:

class B { /* ... */ };

class D : public B { /* ... */ };

// Оп! Получение объекта по значению

void Transmogrify(B obj);

void Transubstantiate(B& obj) { // Все нормально -

                                // передача по ссылке

 Transmogrify(obj);             // Плохо! Срезка объекта!

 // ...

}

D d;

Transubstantiate(d);

Программист намерен работать с объектами В и производных классов полиморфно. Однако, по ошибке (или усталости — к тому же и кофе закончился…) программист или просто забыл написать & в сигнатуре Transmogrify, или собирался создать копию, но сделал это неверно. Код компилируется без ошибок, но когда функция Transmogrify вызывается с передачей ей объекта D, он мутирует в объект B. Это связано с тем, что передача по значению приводит к вызову B::В(const B&), т.е. копирующего конструктора В, параметр которого const B& представляет собой автоматически преобразованную ссылку на d. Что приводит к полной потере динамического, полиморфного поведения, из-за которого в первую очередь и используется наследование.

Перейти на страницу:

Все книги серии C++ In-Depth

Стандарты программирования на С++
Стандарты программирования на С++

Эта книга поможет новичку стать профессионалом, так как в ней представлен сконцентрированный лучший опыт программистов на С++, обобщенный двумя экспертами мирового класса.Начинающий программист найдет в ней простые и понятные рекомендации для ежедневного использования, подкрепленные примерами их конкретного применения на практике.Опытные программисты найдут в ней советы и новые рекомендации, которые можно сразу же принять на вооружение. Программисты-профессионалы могут использовать эту книгу как основу для разработки собственных стандартов кодирования, как для себя лично, так и для группы, которой они руководят.Конечно, книга рассчитана в первую очередь на профессиональных программистов с глубокими знаниями языка, однако она будет полезна любому, кто захочет углубить свои знания в данной области.

Андрей Александреску , Герб Саттер

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

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

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

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

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

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

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

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

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