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

Тут можно читать онлайн Скотт Майерс - Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ - бесплатно ознакомительный отрывок. Жанр: comp-programming, издательство Литагент «ДМК»233a80b4-1212-102e-b479-a360f6b39df7, год 2006. Здесь Вы можете читать ознакомительный отрывок из книги онлайн без регистрации и SMS на сайте лучшей интернет библиотеки ЛибКинг или прочесть краткое содержание (суть), предисловие и аннотацию. Так же сможете купить и скачать торрент в электронном формате fb2, найти и слушать аудиокнигу на русском языке или узнать сколько частей в серии и всего страниц в публикации. Читателям доступно смотреть обложку, картинки, описание и отзывы (комментарии) о произведении.
  • Название:
    Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ
  • Автор:
  • Жанр:
  • Издательство:
    Литагент «ДМК»233a80b4-1212-102e-b479-a360f6b39df7
  • Год:
    2006
  • Город:
    Москва
  • ISBN:
    5-94074-304-8
  • Рейтинг:
    3.6/5. Голосов: 101
  • Избранное:
    Добавить в избранное
  • Отзывы:
  • Ваша оценка:
    • 80
    • 1
    • 2
    • 3
    • 4
    • 5

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

Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ - описание и краткое содержание, автор Скотт Майерс, читайте бесплатно онлайн на сайте электронной библиотеки LibKing.Ru

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

Издание ориентировано на программистов, знакомых с основами C++ и имеющих навыки его практического применения.

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

Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ - читать книгу онлайн бесплатно (ознакомительный отрывок), автор Скотт Майерс
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать

Widget&

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

{

delete pb; // прекратить использование текущего

// объекта Bitmap

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

// Bitmap, указанной в правой части

return *this; // см. правило 10

}

Проблема состоит в том, что внутри operator= *this (чему присваивается значение) и rhs (что присваивается) могут оказаться одним и тем же объектом. Если это случится, то delete уничтожит не только Bitmap, принадлежащий текущему объекту, но и Bitmap, принадлежащий объекту в правой части. По завершении работы этой функции Widget, который не должен был бы измениться в процессе присваивания самому себе, содержит указатель на удаленный объект!

Традиционный способ предотвратить эту ошибку состоит в том, что нужно выполнить проверку совпадения в начале 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;

}

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

Что следует помнить

• Убедитесь, что operator= правильно ведет себя, когда объект присваивается самому себе. Для этого можно сравнить адреса исходного и целевого объектов, аккуратно упорядочить предложения или применить идиому копирования обменом.

• Убедитесь, что все функции, оперирующие более чем одним объектом, ведут себя корректно при совпадении двух или более объектов.

Правило 12: Копируйте все части объекта

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

Объявляя собственные копирующие функции, вы сообщаете компилятору, что реализация по умолчанию вам чем-то не нравится. Компилятор «обижается» и мстит оригинальным образом: он не сообщает, если в вашей реализации что-то неправильно.

Рассмотрим класс, представляющий заказчиков, в котором копирующие функции написаны вручную таким образом, что их вызовы протоколируются:

void logCall(const std::string& funcName); // делает запись в протокол

class Customer {

public:

...

Customer(const Customer& rhs);

Customer& operator=(const Customer& rhs);

...

private:

std::string name;

};

Customer::Customer(const Customer& rhs)

: name(rhs.name) // копировать данные rhs

{

logCall(“Конструктор копирования Customer”);

}

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

{

Читать дальше
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать


Скотт Майерс читать все книги автора по порядку

Скотт Майерс - все книги автора в одном месте читать по порядку полные версии на сайте онлайн библиотеки LibKing.




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


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


Понравилась книга? Поделитесь впечатлениями - оставьте Ваш отзыв или расскажите друзьям

Напишите свой комментарий
x