Скотт Майерс - Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ
- Название:Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ
- Автор:
- Жанр:
- Издательство:Литагент «ДМК»233a80b4-1212-102e-b479-a360f6b39df7
- Год:2006
- Город:Москва
- ISBN:5-94074-304-8
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Скотт Майерс - Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ краткое содержание
Эта книга представляет собой перевод третьего издания американского бестселлера Effective C++ и является руководством по грамотному использованию языка C++. Она поможет сделать ваши программы более понятными, простыми в сопровождении и эффективными. Помимо материала, описывающего общую стратегию проектирования, книга включает в себя главы по программированию с применением шаблонов и по управлению ресурсами, а также множество советов, которые позволят усовершенствовать ваши программы и сделать работу более интересной и творческой. Книга также включает новый материал по принципам обработки исключений, паттернам проектирования и библиотечным средствам.
Издание ориентировано на программистов, знакомых с основами C++ и имеющих навыки его практического применения.
Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Что ж, это корректное и желательное поведение. В конец концов, вы хотите, чтобы все ваши объекты были надежно инициализированы и уничтожены. И все же было бы неплохо найти способ пропустить все эти вызовы конструкторов и деструкторов. Способ есть! Это – передача по ссылке на константу:
bool validateStudent(const Student& s);
Этот способ гораздо эффективнее: не вызываются никакие конструкторы и деструкторы, поскольку не создаются никакие новые объекты. Квалификатор const в измененном объявлении параметра важен. Исходная версия validateStudent принимала параметр Student по значению, вызвавший ее знает о том, что он защищен от любых изменений, которые функция может внести в переданный ей объект; validateStudent сможет модифицировать только его копию. Теперь же, когда Student передается по ссылке, необходимо объявить его const, поскольку в противном случае вызывающая программа должна побеспокоиться о том, чтобы validateStudent не вносила изменений в переданный ей объект.
Передача параметров по ссылке также позволяет избежать проблемы « срезки » (slicing). Когда объект производного класса передается (по значению) как объект базового класса, вызывается конструктор копирования базового класса, а те части, которые принадлежат производному, «срезаются». У вас остается только простой объект базового класса – что вполне естественно, так как его создал конструктор базового класса. Это почти всегда не то, что вам нужно. Например, предположим, что вы работаете с набором классов для реализации графической оконной системы:
class Window {
public
...
std::string name() const; // возвращает имя окна
virtual void display() const; // рисует окно и его содержимое
};
class WindwoWithScrollBars: public Window {
public:
...
virtual void display() const;
};
Все объекты класса Window имеют имя, которое вы можете получить посредством функции name, и все окна могут быть отображены, на что указывает наличие функции display. Тот факт, что display – функция виртуальная, говорит о том, что способ отображения простых объектов базового класса Window может отличаться от способа отображения объектов WindowWithScrollBar (см. правила 34 и 36).
Теперь предположим, что вы хотите написать функцию, которая будет печатать имя окна и затем отображать его. Вот неверный способ написания такой функции:
void printNameAndDisplay(Window w) // неправильно! Параметр
{ // может быть «срезан»
std::cout << w.name();
w.display();
}
Посмотрим, что случится, если вызвать эту функцию, передав ей объект WindowWithScrollBar:
WindowWithScrollBar wwsb;
PrintNameAndDisplay(wwsb);
Параметр w будет сконструирован – он передан по значению, помните? – как объект Window, и вся дополнительная информация, которая делает его объектом WindowWithScrollBar, будет срезана. Внутри printNameAndDisplay w всегда будет вести себя как объект класса Window (потому что это и есть объект класса Window), независимо от типа объекта, в действительности переданного функции. В частности, вызов функции display внутри printNameAndDisplay всегда вызовет Window::display и никогда – WindowWithScrollBar::display.
Способ решения проблемы «срезки» – передать w по ссылке на константу:
void printNameAndDisplay(const Window& w) // правильно, параметр
{ // не может быть «срезан»
std::cout << w.name();
w.display();
}
Теперь w ведет себя правильно, какое бы окно он ни представлял в действительности.
Если вы заглянете «под капот» C++, то увидите, что ссылки обычно реализуются как указатели, поэтому передача чего-либо по ссылке обычно означает передачу указателя. В результате объекты встроенного типа (например, int) всегда более эффективно передавать по значению, чем по ссылке. Поэтому для встроенных типов, если у вас есть выбор – передавать по значению или по ссылке на константу, имеет смысл выбрать передачу по значению. Тот же совет касается итераторов и функциональных объектов STL, потому что они специально спроектированы для передачи по значению. Программисты, реализующие итераторы и функциональные объекты, отвечают за то, чтобы обеспечить эффективность передачи их по значению и исключить «срезку». Это пример того, как меняются правила в зависимости от используемой вами части C++ (см. правило 1).
Встроенные типы являются небольшими объектами, поэтому некоторые делают вывод, что все встроенные типы – хорошие кандидаты на передачу по значению, даже если они определены пользователем. Сомнительно. То, что объект небольшой, еще не значит, что вызов его конструктора копирования обойдется дешево. Многие объекты – среди них большинство контейнеров STL – содержат в себе немногим больше обычного указателя, но копирование таких объектов влечет за собой копирование всего, на что они указывают. Это может оказаться очень дорого.
Даже когда маленькие объекты имеют ненакладные конструкторы копирования, все равно они могут оказывать влияние на производительность. Некоторые компиляторы рассматривают встроенные и пользовательские типы по-разному, даже если они имеют одинаковое внутреннее представление. Например, некоторые компиляторы не размещают объекты, состоящие из одного лишь double в регистрах, даже если готовы размещать там значения встроенного типа double. В таких случаях лучше передавать объекты по ссылке, потому что компилятор безусловно готов поместить в регистр указатель (реализующий ссылку).
Другая причина того, почему маленькие пользовательские типы не обязательно хороши для передачи по значению, заключается в том, что их размер подвержен изменениям. Тип, который мал сегодня, может вырасти в будущем, потому что его внутренняя реализация может измениться. Ситуация меняется даже в том случае, если вы переключаетесь на другую реализацию C++. Например, в одних реализациях тип string из стандартной библиотеки в семь раз больше, чем в других.
Вообще говоря, единственные типы, для которых можно предположить, что передача по значению будет недорогой, – это встроенные типы, а также итераторы и функциональные объекты STL. Для всего остального следуйте совету этого правила и передавайте параметры по ссылке на константу вместо передачи по значению.
• Передаче по значению предпочитайте передачу по ссылке на константу. Обычно это более эффективно и позволяет избежать проблемы «срезки».
• Это правило не касается встроенных типов, итераторов и функциональных объектов STL. Для них передача по значению обычно подходит больше.
Правило 21: Не пытайтесь вернуть ссылку, когда должны вернуть объект
Как только программисты осознают проблемы эффективности, связанные с передачей объектов по значению (см. правило 20), они, подобно крестоносцам, преисполняются решимости искоренить зло – передачу по значению – везде, где бы оно ни пряталось. Непреклонные в своем «святом» порыве, они с неизбежностью допускают фатальную ошибку: начинают передавать по ссылке значения несуществующих объектов. А это неправильно.
Читать дальшеИнтервал:
Закладка: