Вход/Регистрация
Стандарты программирования на С++. 101 правило и рекомендация
вернуться

Александреску Андрей

Шрифт:

В редких случаях классы, имеющие члены странных типов (например, ссылки,

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

Ссылки

[Cline99] §30.01-14 • [Koenig97] §4 • [Stroustrup00] §5.5, §10.4 • [SuttHysl04b]

53. Явно разрешайте или запрещайте копирование

Резюме

Копируйте со знанием дела: тщательно выбирайте между использованием сгенерированных компилятором копирующего конструктора и оператора присваивания, написанием собственных версий или явным запрещением обоих, если копирование не должно быть разрешено.

Обсуждение

Распространенной ошибкой (и не только среди новичков) является игнорирование семантики копирования и присваивания при определении класса. Это характерно для маленьких вспомогательных классов, таких как предназначенные для поддержки идиомы 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

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

Резюме

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

Clone
, если пользователям вашего класса необходимо получать полиморфные (полные, глубокие) копии.

Обсуждение

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

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

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

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

void Transmogrify(B obj);

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

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

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

 // ...

}

  • Читать дальше
  • 1
  • ...
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: