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

         

Пользуйтесь правильным алгоритмом сортировкиРезюме


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



Делайте предикаты чистыми функциямиРезюме


Предикат представляет собой функциональный объект, который возвращает ответ да/нет, обычно в виде значения типа bool. Функция является "чистой" в математическом смысле, если ее результат зависит только от ее аргументов (обратите внимание — в данном случае термин "чистая" не имеет никакого отношения к чисто виртуальным функциям).

Не позволяйте предикатам сохранять или обращаться к состоянию так, чтобы это могло влиять на результат работы оператора operator(); при этом понятие состояния включает как данные-члены, так и глобальные состояния. Для предикатов желательно делать оператор operator() константной функцией-членом (см. рекомендацию 15).



В качестве аргументов алгоритмов


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



Корректно пишите функциональные объектыРезюме


Разрабатывайте функциональные объекты так, чтобы их копирование выполнялось как можно эффективнее. Там, где это возможно, делайте их максимально адаптируемыми путем наследования от unary_function или binary_function.



Избегайте явного выбора типов — используйте полиморфизмРезюме


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



Работайте с типами, а не с представлениямиРезюме




Не пытайтесь делать какие-то предположения о том, как именно объекты представлены в памяти. Как именно следует записывать и считывать объекты из памяти — пусть решают типы объектов.



Избегайте reinterpret_castРезюме


Как гласит римская пословица, у лжи короткие ноги. Не пытайтесь использовать reinterpret_cast, чтобы заставить компилятор рассматривать биты объекта одного типа как биты объекта другого типа. Такое действие противоречит безопасности типов.



Избегайте применения static_cast к указателямРезюме


К указателям на динамические объекты не следует применят преобразование static_cast. Используйте безопасные альтернативы — от dynamic_cast до перепроектирования.



Избегайте преобразований, отменяющих constРезюме


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



Не используйте преобразование типов в стиле СРезюме


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



Не применяйте memcpy или memcmp к не-POD типамРезюме


Не работайте рентгеновским аппаратом (см. рекомендацию 91). Не используйте memcpy и memcmp для копирования или сравнения чего-либо структурированного более, чем обычная память.



Не используйте объединения для преобразованийРезюме


Хитрость все равно остается ложью: объединения можно использовать для получения "преобразования типа без преобразования", записывая информацию в один член и считывая из другого. Однако это еще более опасно и менее предсказуемо, чем применение reinterpret_cast (см. рекомендацию 92).



Не используйте неизвестные аргументы (троеточия)Резюме


Наличие троеточий в C++ — опасное наследие С. Избегайте их в своих программах; используйте вместо этого высокоуровневые конструкции и библиотеки C++.



Не используйте недействительные объекты и небезопасные функцииРезюме


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



Не рассматривайте массивы полиморфноРезюме


Полиморфная работа с массивами — большая ошибка. К сожалению, обычно компилятор никак на нее не реагирует. Не попадайтесь в эту ловушку!



Безопасность типов


Если вы лжете компилятору, он будет мстить.
— Генри Спенсер (Henry Spencer)

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


— Алан Перлис (Alan Perlis)

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

Основная идея поддержки корректности типов — всегда считывать информацию в том формате, в котором она была записана. Иногда C++ позволяет легко нарушить это правило; приведенные в этом разделе рекомендации помогут вам избежать подобных ошибок.

В этом разделе мы считаем наиболее значимой рекомендацию 91 — "Работайте с типами, а не с представлениями". Система типов — ваш друг и верный союзник. Воспользуйтесь ее помощью и попытайтесь не злоупотреблять ее доверием.

Стр.188



Безопасность типов


90. Избегайте явного выбора типов — используйте полиморфизм

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

91. Работайте с типами, а не с представлениями

Не пытайтесь делать какие-то предположения о том, как именно объекты представлены в памяти. Как именно следует записывать и считывать объекты из памяти — пусть решают типы объектов.

92. Избегайте reinterpret_cast

Как гласит римская пословица, у лжи короткие ноги. Не пытайтесь использовать reinterpret_cast, чтобы заставить компилятор рассматривать биты объекта одного типа как биты объекта другого типа. Такое действие противоречит безопасности типов.

93. Избегайте применения static_cast к указателям

К указателям на динамические объекты не следует применят преобразование static_cast. Используйте безопасные альтернативы — от dynamic_cast до перепроектирования.

94. Избегайте преобразований, отменяющих const

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

95. Не используйте преобразование типов в стиле С

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

96. Не применяйте memcpy или memcmp к не-POD типам

Не работайте рентгеновским аппаратом (см. рекомендацию 91). Не используйте тетеру и тетстр для копирования или сравнения чего-либо структурированного более, чем обычная память.

97. Не используйте объединения для преобразований

Хитрость все равно остается ложью: объединения можно использовать для получения "преобразования типа без преобразования ", записывая информацию в один член и считывая из другого. Однако это еще более опасно и менее предсказуемо, чем применение reinterpret_cast (см. рекомендацию 92).

98. Не используйте неизвестные аргументы (троеточия)

Наличие троеточий в C++ — опасное наследие С. Избегайте их в своих программах; используйте вместо этого высокоуровневые конструкции и библиотеки C++.

99. Не используйте недействительные объекты и небезопасные функции

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

100. Не рассматривайте массивы полиморфно

Полиморфная работа с массивами — большая ошибка. К сожалению, обычно компилятор никак на нее не реагирует. Не попадайтесь в эту ловушку!

Стр.219



Благодарности


Мы от всей души благодарны редактору серии Бьярну Страуструпу (Bjarne Stroustrup), редакторам Питеру Гордону (Peter Gordon) и Дебби Лафферти (Debbie Lafferty), а также Тирреллу Албаху (Tyrrell Albaugh), Ким Бодихаймер (Kim Boedigheimer), Джону Фуллеру (John Fuller), Бернарду Гаффни (Bernard Gaffney), Курту Джонсону (Curt Johnson), Чанде Лири-Коту (Chanda Leary-Coutu), Чарли Ледди (Charles Leddy), Хитеру Муллэйн (Heather Mullane), Чути Прасертсиху (Chuti Prasertsith), Ларе Вайсонг (Lara Wysong) и всем остальным работникам издательства Addison-Wesley, помогавшим нам в нашей работе над этим проектом. Нам было очень приятно работать с ними.

На идею и оформление книги нас натолкнули несколько источников, включая такие, как [Cline99], [Peters99], а также работы легендарного и широко цитируемого Алана Перлиса (Alan Perlis).

Особая благодарность тем людям, чьи отзывы помогли нам сделать многие части книги намного лучшими, чем они были бы без этих замечаний. Особенно большое влияние на книгу оказали острые комментарии Бьярна Страуструпа. Мы очень хотим поблагодарить за активное участие в обсуждении материала и высказанные замечания таких людей, как Дэйв Абрамс (Dave Abrahams), Маршалл Клайн (Marshall Cline), Кевлин Хенни (Kevlin Henney), Говард Хиннант (Howard Hinnant), Джим Хайслоп (Jim Hyslop), Николаи Джосаттис (Nicolai Josuttis), Йон Калб (Jon Kalb), Макс Хесин (Max Khesin), Стен Липпман (Stan Lippman), Скотт Мейерс (Scott Meyers) и Дэвид Вандевурд (Daveed Vandevoorde). Кроме того, отдельное спасибо хотелось бы сказать Чаку Аллисону (Chuck Allison), Самиру Байяю (Samir Bajaj), Марку Барбуру (Маге Barbour), Тревису Брауну (Travis Brown), Нилу Кумбесу (Nil Coombes), Дамиану Дечеву (Damian Dechev), Стиву Дьюхарсту (Steve Dewhurst), Питеру Димову (Peter Dimov), Аттиле Фехеру (Attila Feher), Алану Гриффитсу (Alan Griffiths), Мичи Хеннингу (Michi Henning), Джеймсу Канзе (James Kanze), Бартошу Милевски (Bartosz Milewski), Мэтту Маркусу (Mart Marcus), Балогу Палу (Balog Pal), Владимиру Прусу (Vladimir Prus), Дэну Саксу (Dan Saks), Люку Вагнеру (Luke Wagner), Мэтью Вильсону (Matthew Wilson) и Леору Золману (Leor Zolman).

Как обычно, все оставшиеся в книге ошибки и недосмотры — на нашей совести, а не на их.

Герб Саттер (Herb Sutter)
Андрей Александреску (Andrei Alexandrescu)

Сиэтл, сентябрь 2004

Стр.13



Функции и операторы


Если ваша процедура имеет десять параметров — вероятно, вы где-то ошиблись.


— Алан Перлис (Alan Perlis)

Функции, включая перегруженные операторы, представляют собой фундаментальные единицы работы. Как вы увидите позже в разделе "Обработка ошибок и исключения" (в частности, в рекомендации 70), это непосредственно влияет на наши рассуждения о корректности и безопасности кода.

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

В этом разделе наиболее важной нам представляется рекомендация 26 — "Сохраняйте естественную семантику перегруженных операторов".

Стр.58



Функции и операторы


25. Передача параметров по значению, (интеллектуальному) указателю или ссылке

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

26. Сохраняйте естественную семантику перегруженных операторов

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

27. Отдавайте предпочтение каноническим формам арифметических операторов и операторов присваивания

Если можно записать а+b, то необходимо, чтобы можно было записать и а+=Ь. При определении бинарных арифметических операторов одновременно предоставляйте и их присваивающие версии, причем делайте это с минимальным дублированием и максимальной эффективностью.

28. Предпочитайте канонический вид ++ и --, и вызов префиксных операторов

Особенность операторов инкремента и декремента состоит в том, что у них есть префиксная и постфиксная формы с немного отличающейся семантикой. Определяйте операторы operator++ и operator— так, чтобы они подражали поведению своих встроенных двойников. Если только вам не требуется исходное значение — используйте префиксные версии операторов.

29. Используйте перегрузку, чтобы избежать неявного преобразования типов

Не преумножайте объекты сверх необходимости (Бритва Оккама): неявное преобразование типов обеспечивает определенное синтаксическое удобство (однако см. рекомендацию 40), но в ситуации, когда допустима оптимизация (см. рекомендацию 8) и желательно избежать создания излишних объектов, можно обеспечить перегруженные функции с сигнатурами, точно соответствующими распространенным типам аргументов, и тем самым избежать преобразования типов.

30. Избегайте перегрузки &&, II и , (запятой)

Мудрость — это знание того, когда надо воздержаться. Встроенные операторы &&, 11 и , (запятая) трактуются компилятором специальным образом. После перегрузки они становятся обычными функциями с весьма отличной семантикой (при этом вы нарушаете рекомендации 26 и 31), а это прямой путь к трудноопределимым ошибкам и ненадежности. Не перегружайте эти операторы без крайней необходимости и глубокого понимания.

31. Не пишите код, который зависит от порядка вычислений аргументов функции

Порядок вычисления аргументов функции не определен, поэтому никогда не полагайтесь на то, что аргументы будут вычисляться в той или иной очередности.



Как пользоваться этой книгой


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

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

Стр.10

Эта книга предназначена для того, чтобы послужить основой для вашего стандарта кодирования и быть в той или иной мере включенной в него. Это — не ultima ratio в стандартах кодирования, и ваша группа может разработать (или прибавить) свои правила, в наибольшей степени подходящие для вашей конкретной группы или для конкретной решаемой задачи, так что вы не должны быть скованы этой книгой по рукам и ногам. Тем не менее, мы надеемся, что эта книга поможет вам сберечь время и усилия при разработке собственного стандарта кодирования, а также будет способствовать повышению его качества и последовательности.

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

И наконец, периодически творчески пересматривайте собственные стандарты с учетом практического опыта, приобретенного всей командой при работе над проектом.



Конструкторы, деструкторы и копирование


Если стандарт привел вас к обрыву, это еще не значит, что вы должны прыгнуть с него.
— Норман Даймонд (Norman Diamond)

О Большой Четверке специальных функций было сказано достаточно, чтобы вы не удивлялись тому, что им посвящен отдельный раздел. Здесь собраны знания и практика, связанные с конструкторами по умолчанию, копирующими конструкторами, копирующим присваиванием и деструкторами.

Одна из причин, по которым при работе с этими функциями следует быть особенно внимательными, заключается в том, что если вы дадите компилятору хотя бы полшанса — он тут же напишет эти функции за вас. Еще одна причина состоит в том, что C++ по умолчанию рассматривает классы как типы-значения, но далеко не все типы именно таковы (см. рекомендацию 32). Надо отчетливо понимать, когда следует писать (или запрещать) эти специальные функции явно, и следовать правилам и рекомендациям из этого раздела — это поможет вам в написании корректного, расширяемого и безопасного кода.

В этом разделе мы считаем наиболее значимой рекомендацию 51 — "Деструкторы, функции освобождения ресурсов и обмена не ошибаются".

Стр.100



Конструкторы, деструкторы и копирование


47. Определяйте и инициализируйте переменные-члены в одном порядке

Переменные-члены всегда инициализируются в том порядке, в котором они объявлены при определении класса; порядок их упоминания в списке инициализации конструктора игнорируется. Убедитесь, что в коде конструктора указан тот же порядок, что и в определении класса.

48. В конструкторах предпочитайте инициализацию присваиванию

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

Стр.212

49. Избегайте вызовов виртуальных функций в конструкторах и деструкторах

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

50. Делайте деструкторы базовых классов открытыми и виртуальными либо защищенными и невиртуальными

Удалять или не удалять — вот в чем вопрос! Если следует обеспечить возможность удаления посредством указателя на базовый класс, то деструктор базового класса должен быть открытым и виртуальным. В противном случае он должен быть защищенным и невиртуальным.

51. Деструкторы, функции освобождения ресурсов и обмена не ошибаются

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

52. Копируйте и ликвидируйте согласованно

Если вы определили одну из следующего списка функций — копирующий конструктор, оператор копирующего присваивания или деструктор — вероятно, вам потребуется определить и обе оставшиеся функции (или по крайней мере одну из них).


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

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

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

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

55. Предпочитайте канонический вид присваивания

При реализации оператора operator= предпочитайте использовать канонический вид — невиртуальный с определенной сигнатурой.

56. Обеспечьте бессбойную функцию обмена

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

Стр.213


Об этой книге


Основными принципами дизайна данной книги являются следующие.

Краткость — сестра таланта. Чем больше стандарт кодирования по размеру, тем больше шансов, что он будет благополучно проигнорирован. Читают и используют обычно короткие стандарты. Длинные разделы, как правило, просто просматривают "по диагонали", короткие статьи обычно удостаиваются внимательного прочтения.

Никакой материал не должен вызывать дискуссий. Эта книга документирует широко используемые стандарты, а не изобретает их. Если некоторая рекомендация не применима во всех ситуациях, то мы так и пишем — "подумайте о применении X" вместо "делайте X". Кроме того, к каждому правилу указаны все общепринятые исключения.

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

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

Например, "не возвращайте указатель/ссылку на локальную переменную" — хороший совет, но он не включен в данную книгу, поскольку практически все компиляторы выдают соответствующее предупреждение, к тому же этот вопрос раскрывается в первом разделе книги.

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

Еще один совет — "не злоупотребляйте goto" — исходя из нашего опыта, и так широко известен всем программистам, так что нет смысла повторяться.

Каждый раздел состоит из следующих частей.

Заглавие. Краткое название раздела, поясняющее, о чем будет идти речь.

Резюме. Краткое изложение сути вопроса.

Стр.12

Обсуждение. Расширенное пояснение рекомендации. Зачастую включает краткое обоснование, но учтите, что полную информацию по данному вопросу следует искать в приведенных ссылках.

Примеры (если таковые имеются). Примеры, демонстрирующие правило или позволяющие лучше его понять и запомнить.

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

Ссылки. В приведенной в этом подразделе литературе по C++ вы найдете более полный анализ рассматриваемого в разделе вопроса.

В каждой части книги имеется "наиболее важный раздел" — обычно это первый раздел части. Однако иногда это правило нарушалось в связи с необходимостью последовательного и связного изложения материала.



Обработка ошибок и исключения


Обработка ошибок — сложная задача, при решении которой программисту требуется вся помощь, которая только может быть предоставлена.


— Бьярн Страуструп (Bjarne Stroustrup),

[Stroustrup94] §16.2

Имеется три способа написать программу без ошибок; но работает только третий способ.


— Алан Перлис (Alan Perlis)

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

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

В этом разделе мы считаем наиболее значимой рекомендацию 69 — "Определите разумную стратегию обработки ошибок и строго ей следуйте".

Стр.144



Обработка ошибок и исключения


68.Широко применяйте assert для документирования внутренних допущений и инвариантов

Используйте assert или его эквивалент для документирования внутренних допущений в модуле (т.е. там, где вызываемый и вызывающий код поддерживаются одним и тем же программистом или командой), которые должны всегда выполняться (в противном случае они являются следствием программной ошибки; например, нарушение постусловий функции, обнаруженное вызывающим кодом). (См. также рекомендацию 70.) Убедитесь, что использование assert не приводит к побочным действиям.

69. Определите разумную стратегию обработки ошибок и строго ей следуйте

Еще на ранней стадии проектирования разработайте практичную, последовательную и разумную стратегию обработки ошибок и строго следуйте ей. Убедитесь, что ваша стратегия включает следующее.

Идентификация: какие условия являются ошибкой.

Строгость: насколько важна каждая ошибка.

Обнаружение: какой код отвечает за обнаружение ошибки.

Распространение: какой механизм используется для описания и распространения уведомления об ошибке в каждом модуле.

Обработка: какой код отвечает за выполнение действий, связанных с ошибкой.

Уведомление: каким образом информация об ошибке вносится в журнальный файл или производится уведомление пользователя программы.

Изменяйте механизмы обработки ошибок только в пределах границ модуля.

Стр.215

70. Отличайте ошибки от ситуаций, не являющихся ошибками

Функция представляет собой единицу работы. Таким образом, сбои следует рассматривать либо как ошибки, либо как штатные ситуации, в зависимости от их влияния на функции. В функции/сбой является ошибкой тогда и только тогда, когда он нарушает одно из предусловий/, не позволяет выполнить предусловие вызываемой ею функции, препятствует достижению собственных постусловий f или сохранению инварианта, за поддержку которого отвечает функция/.

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


71. Проектируйте и пишите безопасный в отношении ошибок код

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

Убедитесь, что при любых ошибках ваша программа всегда остается в корректном состоянии (в этом и заключается базовая гарантия). Остерегайтесь ошибок, нарушающих инвариант (включая утечки, но не ограничиваясь ими).

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

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

72. Для уведомления об ошибках следует использовать исключения

Для уведомления об ошибках лучше использовать механизм исключений, а не коды ошибок. Применять коды состояния (например, коды ошибок, переменную еггпо) следует только тогда, когда нельзя использовать исключения (см. рекомендацию 62), а также для ситуаций, которые не являются ошибками. К другим методам, таким как экстренное завершение программы (или плановое завершение с освобождением ресурсов и т.п. действиями), следует прибегать только в ситуациях, когда восстановление после ошибки невозможно (или не требуется).

73. Генерируйте исключения по значению, перехватывайте — по ссылке

Генерируйте исключения по значению (не через указатель) и перехватывайте их как ссылки (обычно константные). Эта комбинация наилучшим образом соответствует семантике исключений. При повторной генерации перехваченного исключения предпочтительно использовать просто оператор throw;, а не инструкцию throw e;.

74. Уведомляйте об ошибках, обрабатывайте и преобразовывайте их там, где следует

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

Стр.216

75. Избегайте спецификаций исключений

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


От издательского дома "Вильямс"


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

Мы ждем ваших комментариев и надеемся на них. Вы можете прислать нам бумажное или электронное письмо либо просто посетить наш Web-сервер и оставить свои замечания там. Одним словом, любым удобным для вас способом дайте нам знать, нравится ли вам эта книга, а также выскажите свое мнение о том, как сделать наши книги более интересными для вас.

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

E-mail: info@williamspublishing.com

WWW: http://www.wiliamspublishing.com

Информация для писем из

России: 115419, Москва, а/я 783

Украины: 03150, Киев, а/я 152

Стр.220



делайте одинаковые вещи одинаковыми способами.


Идите проторенной дорогой — делайте одинаковые вещи одинаковыми способами. Накапливайте идиомы. Стандартизируйте. Единственное отличие между вами и Шекспиром — в количестве используемых идиом, а не в размере словаря.

— Алан Перлис (Alan Perlis) [выделено нами]
Лучшее в стандарте то, что он предоставляет богатый выбор.

— Приписывается разным людям
Мы бы хотели, чтобы эта книга стала основой для стандартов кодирования, используемых вашей командой, по двум основным причинам.
Стандарты кодирования должны отражать лучший опыт проб и ошибок всего сообщества программистов. В них должны содержаться проверенные идиомы, основанные на опыте и твердом понимании языка. В частности, стандарт кодирования должен основываться на исчерпывающем анализе литературы по разработке программного обеспечения, и объединять воедино правила, рекомендации и наилучшие практические решения, которые в противном случае оказываются разбросанными по многочисленным источникам.
Природа не терпит пустоты. Если вы не разработаете набор правил, то это сделает кто-то другой. Такие "самопальные" стандарты, как правило, грешат тем, что включают нежелательные для стандарта требования; например, многие из них, по сути, заставляют программистов использовать C++ просто как улучшенный С.
Множество таких плохих стандартов кодирования разработаны людьми, которые недостаточно хорошо понимают язык программирования C++ или пытаются чрезмерно детализировать его применение. Плохой стандарт кодирования быстро теряет кредит доверия, и в результате несогласие или неприятие программистами части его положений распространяется на весь стандарт целиком, перечеркивая содержащиеся в нем различные положительные советы и рекомендации. И это — в лучшем случае, потому что в худшем случае такой стандарт и его выполнение могут быть навязаны руководством.

Предметный указатель


#

#include, 45

А

Abelson, Harold, 25


ADL, см. Поиск, зависящий от аргумента assert, 45; 112; 144


auto_ptr, 168

В

Bell, Gordon, 25


Bentley, Jon, 25; 29


binary_search, 179

С

Cargill, Tom, 92


const, 42; 142; 194


  У входных параметров функций, 58


CVS, 20

D

delete, 95


Dependency inversion principle, 54; 77


deque, 164; 171


Diamond, Norman, 99


dynamic_cast, 41; 193

E

equal_range, 179


errno, 154


explicit, 85; 110

F

find,179


fmd_if, 179


for_each, 28

I

inline, 30

К

Knuth, Donald, 23; 29

L

list, 164


lower_bound, 179

M

map, 164


McConnell, Steve, 25; 144


memcmp, 197


memcpy, 197


mutable, 42

N

new, 95; 97


  Размещающий, 98


nth_element, 180

P

partial_sort, 180


partition, 180


Perlis, Alan, 23; 39; 57; 117; 143; 187


Plain Old Data, 190


POD, 190


push_back, 28; 169

R

RAII, 37


realloc, 24


reinterpret_cast, 192


return, 17

Стр.221

s

Schwarz, Jerry, 127


set, 164


shared_ptr, 125; 168


sort, 180


Spencer, Henry, 187


stable_partition, 180


stable_sort, 180


static_cast, 41; 193


string, 167; 171


Stroustrup, Bjarne, 69; 133; 143; 163; 173


struct, 33


Sussman, Gerald Jay, 25


swap, 114; 140

U

upper_bound, 179


using, 119; 122

V

vector, 164; 166; 167; 171


volatile, 49

A

Абельсон, Гарольд, 25


Абстрактный интерфейс, 77; 188


Агрегаты значений, 88


Алгоритмы STL, 173


Аргумент по умолчанию, 81


Атомарные операции, 34

Б

Белл, Гордон,25


Бентли, Ион, 25; 29

В

Венгерская запись, 15


Взаимоблокировка, 34; 36


Вложенность блоков, 50


Внешнее связывание, 32


Время жизни, 47; 200

Г

Гарантии безопасности, 151


Глобальные объекты, 32

Д

Даймонд, Норман, 99


Декремент


  Префиксная и постфиксная формы, 62


Дескриптор, 89


Деструктор, 106;151


  Виртуальный, 76; 104


Динамическое связывание, 134


Дружба, 73

З

Зависимости определений, 53


Зависимые имена, 55; 138


Заголовочный файл, 16; 55


  Защита от множественного включения, 56


Закон Второго Шанса, 78

И

Идиома


  Erase-remove, 171



  Pimpl,42;73;87;91; 114; 186

  RAII, 17; 37; 108; 109

  Бессбойного принятия работы, 155

  Выделение ресурса есть инициализация,

    См. RAII

  Индексного контейнера, 180

  Клонирования, 111

  Копирующего присваивания, 107

  Присваивания через обмен, 114

   Реализации оператора через присваивающую версию, 60

  Реализации постфиксного оператора через префиксный, 62

  Устранения излишней емкости контейнера, 171

Имя
  Зависимое, 138

Инвариант, 148

Инициализация, 17; 32; 48; 100; 101

  Порядок, 52

Инкапсуляция, 83; 87

Инкремент

  Префиксная и постфиксная формы, 62

Интеллектуальный указатель, 38; 168

Интерфейс, 130

  Абстрактный, 77

  Неявный, 134

  Открытый, 83; 118

Исключение, 71; 106; 128; 146; 154; 158

  Генерация, 158

  Повторная генерация, 158

  Преобразование, 159

Стр.222

Итератор, 142

  Недействительный, 175

К

Каргилл, Том, 92

Класс, 69

  Базовый, 70; 74; 75; 84; 105

  Вспомогательный, 71

  Инициализация, 100; 101

  Исключения, 71

  Класс-значение, 70

  Минимальный, 72

  Множественное наследование, 78

  Монолитный, 72

  Производный, 81

  Пустой базовый, 74; 78

  Свойств, 70

  Стратегии, 70; 80; 105

Кнут, Дональд, 23; 29

Код состояния, 154

Командная работа, 21; 131

Комментарий, 14

Композиция, 73

Конструктор, 155

  Неявно преобразующий, 85

Копирование, 109

Копирующее присваивание, 152

Копирующий конструктор, 37

Л

Лямбда-функция, 177

М

Мак-Коннелл, Стив, 25; 144

Макрос, 44

Массив, 201

Масштабируемость, 27; 31; 32

Минимизация зависимостей, 33

Многопоточность, 34

  Взаимоблокировка, 34

  Внешняя блокировка, 35

  Внутренняя блокировка, 35




  Условия гонки, 34

Множественное наследование, 78

Модульность, 77

Н

Наследование, 73; 75

  Открытое, 79

Недействительные объекты, 200

Неиспользуемый параметр функции, 17

О

Обобщенность кода, 94; 142

Объединение, 198

Оператор, 155

  ++, --, 62

  <<, >>, 119

  Присваивания, 113

Ошибка, 151; 154

  Времени выполнения, 146

Обработка, 159

  При работе с STL, 174

П

Память, 125

Перегрузка операторов, 25; 59

  &&, || и , , 65

Переносимость, 34

Перлис, Алан, 23; 39; 57; 117; 143; 187

Повторная генерация исключения, 158

Повторное использование, 80

Поиск

   Зависящий от аргументов, 118; 120; 136

  Имен, 73; 75; 78

  Кенига, 118; 120

Полиморфизм, 74; 110; 158; 188; 201

  Динамический, 80; 134

  Статический, 78; 134

Порядок вычисления аргументов функции, 67

Порядок инициализации, 32

Постконструктор, 102

Постусловие, 148

Потоки выполнения, 34

Правило

  Одного определения, 124

Предикат, 182

Предусловие, 148

Преждевременная оптимизация, 25; 29

Преждевременная пессимизация, 31

Преобразование типов, 64; 85

Принцип

  Инверсии зависимостей, 54; 77

  Интерфейса, 118

  Открытости-Закрытости, 188

  Подстановки Лисков, 79

Присваивание, 37

  Копирующее, 113

  Самому себе, 113; 152

Пространство имен, 122

Стр.223

Р

Работа с ресурсами, 37

Разработка библиотеки, 30; 31; 33; 36; 59; 66;130; 136

Распределение памяти, 125; 167

Рецензирование кода, 21

С

Сассман, Джеральд, 25

Сборка программы, 19

Связность, 94

Связывание, 73; 126

  Динамическое, 134

  Статическое, 135

Сериализация, 32; 34

Система управления версиями, 20

Сложность алгоритма, 27

Совместно используемые данные, 32

Сокрытие данных, 33; 87; 89; 197

Сокрытие имен, 82

Спенсер, Генри, 187

Специализация шаблона функции, 140

Спецификации исключений, 160




Срезка, 76; 110

Статическое связывание, 135

Стиль проектирования, 23

Страуструп, Бьярн, 69; 133; 143; 163;173

Т

Табуляция, 15

Транзакция, 106; 151

У

Указатель на реализацию, 91

Условия гонки, 34

Утечка, 38

Ф

Функциональный объект, 184; 186

Функция, 50

  Аргумент по умолчанию, 81

  Виртуальная, 83; 102

  Встраиваемая, 127

  Инвариант, 148

  Неиспользуемый параметр, 17

  Обмена, 107; 114; 151

  Освобождения ресурсов, 107; 151

  Перегрузка, 85; 140

   Передача параметров по значению, 43; 58

  Передача параметров по ссылке, 58

  Передача параметров посредством указателей, 58

  Перекрытие, 81

  Порядок вычисления аргументов, 67

  Постусловие, 148

  Предусловие, 148

  С переменным количеством аргументов, >199

  Шаблон, 140

Функция обратного вызова, 36

Ш

Шаблон, 134

  Точка настройки, 136

  Функции, 140

Шаблон проектирования

  Acyclic Visitor, 54

  Command, 54; 135

  Nonvirtual Interface, 83; 104; 111

  Observer, 176

  Singleton, 52

  Template Method, 83; 104

  Visitor, 54; 135

Шварц, Джерри, 127

Э

Эффективность алгоритма, 27; 169

Научно-популярное издание

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

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

Литературный редактор  С.Г. Татаренко

Верстка  О.В. Мишутина

Художественный редактор  Е/Л. Дынник

Корректор  Л.А. Гордиенко

Издательский дом "Вильямс". 101509, Москва, ул. Лесная, д. 43, стр. 1.

Подписано в печать 22.07.2005. Формат 70X100/16.
Гарнитура Times. Печать офсетная.
Усл. печ.л. 19,35. Уч.-изд. л. 13,2.
Тираж 3000 экз. Заказ № 2182.
Отпечатано с диапозитивов в ФГУП "Печатный двор"
Министерства РФ по делам печати,
телерадиовещания и средств массовых коммуникаций.
197110, Санкт-Петербург, Чкаловский пр., 15.


Проектирование классов и наследование


Наиболее важный аспект разработки программного обеспечения — ясно понимать, что именно вы пытаетесь построить.


— Бьярн Страуструп (Bjarne Stroustrup)

Какого вида классы предпочитает разрабатывать и строить ваша команда? Почему?

Интересно, что большинство рекомендаций данного раздела вызваны в первую очередь вопросами зависимостей. Например, наследование— вторая по силе взаимосвязь, которую можно выразить в C++ (первая — отношение дружбы), и такую сильную связь надо использовать очень осторожно и продуманно.

В этом разделе мы сконцентрируем внимание на ключевых вопросах проектирования классов — как сделать это правильно, как не допустить ошибку, избежать ловушек, и в особенности — как управлять зависимостями.

В следующем разделе мы обратимся к Большой Четверке специальных функций — конструктору по умолчанию, копирующему конструктору, копирующему присваиванию и деструктору.

В этом разделе мы считаем самой важной рекомендацию 33 — "Предпочитайте минимальные классы монолитным".

Стр.70



Проектирование классов и наследование


32.Ясно представляйте, какой вид класса вы создаете

Существует большое количество различных видов классов, и следует знать, какой именно класс вы создаете.

Стр.210

33. Предпочитайте минимальные классы монолитным

Разделяй и властвуй: небольшие классы легче писать, тестировать и использовать. Они также применимы в большем количестве ситуаций. Предпочитайте такие небольшие классы, которые воплощают простые концепции, классам, пытающимся реализовать как несколько концепций, так и сложные концепции (см. рекомендации 5 и 6).

34. Предпочитайте композицию наследованию

Избегайте "налога на наследство": наследование— вторая по силе после отношения дружбы взаимосвязь, которую можно выразить в C++. Сильные связи нежелательны, и их следует избегать везде, где только можно. Таким образом, следует предпочитать композицию наследованию, кроме случаев, когда вы точно знаете, что делаете и какие преимущества дает наследование в вашем проекте.

35. Избегайте наследования от классов, которые не спроектированы для этой цели

Классы, предназначенные для автономного использования, подчиняются правилам проектирования, отличным от правил для базовых классов (см. рекомендацию 32). Использование автономных классов в качестве базовых является серьезной ошибкой проектирования и его следует избегать. Для добавления специфического поведения предпочтительно вместо функций-членов добавлять обычные функции (см. рекомендацию 44). Для того чтобы добавить состояние, вместо наследования следует использовать композицию (см. рекомендацию 34). Избегайте наследования от конкретных базовых классов.

36. Предпочитайте предоставление абстрактных интерфейсов

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

37. Открытое наследование означает заменимость. Наследовать надо


не для повторного использования, а чтобы быть повторно использованным

Открытое наследование позволяет указателю или ссылке на базовый класс в действительности обращаться к объекту некоторого производного класса без изменения существующего кода и нарушения его корректности.

Не применяйте открытое наследование для того, чтобы повторно использовать код (находящийся в базовом классе); открытое наследование необходимо для того, чтобы быть повторно использованным (существующим кодом, который полиморфно использует объекты базового класса).

38. Практикуйте безопасное перекрытие

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

39. Виртуальные функции стоит делать неоткрытыми, а открытые — невиртуальными

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

Стр.211

40. Избегайте возможностей неявного преобразования типов

Не все изменения прогрессивны: неявные преобразования зачастую приносят больше вреда, чем пользы. Дважды подумайте перед тем, как предоставить возможность неявного преобразования к типу и из типа, который вы определяете, и предпочитайте полагаться на явные преобразования (используйте конструкторы, объявленные как explicit, и именованные функции преобразования типов).

41. Делайте данные-члены закрытыми (кроме случая агрегатов в стиле структур С)

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



42.  Не допускайте вмешательства во внутренние дела

Избегайте возврата дескрипторов внутренних данных, управляемых вашим классом, чтобы клиенты не могли неконтролируемо изменять состояние вашего объекта, как своего собственного.

43. Разумно пользуйтесь идиомой Pimpl

C++ делает закрытые члены недоступными, но не невидимыми. Там, где это оправдывается получаемыми преимуществами, следует подумать об истинной невидимости, достигаемой применением идиомы Pimpl (указателя на реализацию) для реализации брандмауэров компилятора и повышения сокрытия информации (см. рекомендации Пи 41).

44. Предпочитайте функции, которые не являются ни членами, ни друзьями

Там, где это возможно, предпочтительно делать функции не членами и не друзьями классов.

45. new и delete всегда должны разрабатываться вместе

Каждая перегрузка void* operator new(parms) в классе должна сопровождаться соответствующей перегрузкой оператора void operator delete(void*, parms), где parms — список типов дополнительных параметров (первый из которых всегда std::size_t). To же относится и к операторам для массивов new[] и delete[].

46. При наличии пользовательского new следует предоставлять все стандартные типы этого оператора

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


Пространства имен и модули


Системы имеют подсистемы, которые в свою очередь состоят из подсистем и так до бесконечности — именно поэтому мы всегда движемся сверху вниз.


— Алан Перлис (Alan Perlis)

Пространство имен — очень важный инструмент для управления именами и снижения количества коллизий имен. То же относится и к модулям, которые, помимо этого, представляют собой инструментарий для работы с версиями. Мы определим модуль как отдельный компонент программы, содержащий тесно связанные между собой ее элементы (см. рекомендацию 5) и поддерживаемый одним и тем же программистом или группой; обычно модуль всегда компилируется одним и тем же компилятором с использованием одних и тех же опций. Модули имеются на разных уровнях детализации в широком диапазоне размеров. С одной стороны, модуль может быть минимального размера, представляя собой отдельный объектный файл, содержащий только один класс; с другой стороны, он может быть, например, отдельной динамической библиотекой, генерируемой из множества исходных файлов, содержимое которых образует подсистему внутри приложения большего размера или выпускается отдельно. Модуль может даже представлять собой огромную библиотеку, состоящую из множества небольших модулей (статических или динамических библиотек), содержащих тысячи разных типов. Несмотря на то, что такие библиотеки в стандарте C++ не упоминаются, программисты постоянно создают и используют библиотеки, и хорошо продуманная модуля-ризация является фундаментальной частью успешного управления зависимостями (см., например, рекомендацию 11).

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

В этом разделе мы считаем наиболее значимой рекомендацию 58 — "Храните типы и функции в разных пространствах имен, если только они не предназначены для совместной работы".

Стр.118



Пространства имен и модули


57.Храните типы и их свободный интерфейс в одном пространстве имен

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

58. Храните типы и функции в разных пространствах имен, если только они не предназначены для совместной работы

Оберегайте ваши типы от непреднамеренного поиска, зависящего от аргументов (argument-dependent lookup — ADL, известный также как поиск Кёнига); однако преднамеренный поиск должен завершаться успешно. Этого можно добиться путем размещения типов в своих собственных пространствах имен (вместе с непосредственно связанными с ними свободными функциями; см. рекомендацию 57). Избегайте помещения типов в те же пространства имен, что и шаблоны функций или операторов).

59. Не используйте using для пространств имен в заголовочных файлах или перед директивой #include

Директива using для пространств имен создана для вашего удобства, а не для головной боли других. Никогда не используйте объявления или директивы using перед директивой #include.

Вывод: не используйте директивы using для пространств имен или using-объявления в заголовочных файлах. Вместо этого полностью квалифицируйте все имена. (Второе правило следует из первого, поскольку заголовочные файлы не могут знать, какие другие директивы #include могут появиться в тексте после них.)

60. Избегайте выделения и освобождения памяти в разных модулях

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


61. Не определяйте в заголовочном файле объекты со связыванием

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

62. Не позволяйте исключениям пересекать границы модулей

Не бросайте камни в соседский огород — поскольку нет повсеместно распространенного бинарного стандарта для обработки исключений C++, не позволяйте исключениям пересекать распространяться между двумя частями кода, если только вы не в состоянии контролировать, каким компилятором и с какими опциями скомпилированы обе эти части кода. В противном случае может оказаться, что модули не поддерживают совместимые реализации распространения исключений. Обычно это правило сводится к следующему: не позволяйте исключениям пересекать границы модулей/подсистем.

63. Используйте достаточно переносимые типы в интерфейсах модулей

Не позволяйте типам появляться во внешнем интерфейсе модуля, если только вы не уверены в том, что все пользователи смогут корректно их понять и работать с ними. Используйте наивысший уровень абстракции, который в состоянии понять клиентский код.

Стр.214


Резюме из резюмеВопросы организации и стратегии


0.Не мелочитесь, или Что не следует стандартизировать

Скажем кратко: не мелочитесь.

1. Компилируйте без замечаний при максимальном уровне предупреждений

Следует серьезно относиться к предупреждениям компилятора и использовать максимальный уровень вывода предупреждений вашим компилятором. Компиляция должна выполняться без каких-либо предупреждений. Вы должны понимать все выдаваемые предупреждения и устранять их путем изменения кода, а не снижения уровня вывода предупреждений.

2. Используйте автоматические системы сборки программ

Нажимайте на одну (единственную) кнопку: используйте полностью автоматизированные ("в одно действие") системы, которые собирают весь проект без вмешательства пользователя.

3. Используйте систему контроля версий

Как гласит китайская пословица, плохие чернила лучше хорошей памяти: используйте системы управления версиями. Не оставляйте файлы без присмотра на долгий срок. Проверяйте их всякий раз после того, как обновленные модули проходят тесты на работоспособность. Убедитесь, что внесенные обновления не препятствуют корректной сборке программы.

4. Одна голова хорошо, а две — лучше

Регулярно просматривайте код всей командой. Чем больше глаз — тем выше качество кода. Покажите ваш код другим и познакомьтесь с их кодом — это принесет пользу всем.



Шаблоны и обобщенность


Место для вашей цитаты.


— Бьярн Страуструп (Bjarne Stroustrup),

[Stroustrup00] §13

Аналогично: место для вашего введения.

В этом разделе мы считаем наиболее значимой рекомендацию 64 — "Разумно сочетайте статический и динамический полиморфизм".

Стр.134



Шаблоны и обобщенность


64. Разумно сочетайте статический и динамический полиморфизм

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

65. Выполняйте настройку явно и преднамеренно

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

66. Не специализируйте шаблоны функций

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

67. Пишите максимально обобщенный код

Используйте для реализации функциональности наиболее обобщенные и абстрактные средства.



Список литературы


Примечание: для удобства читателей весь список литературы доступен по адресу http://www.gotw.ca/publications/c++cs/bibliography.htm

Ссылки, выделенные подчеркиванием (например, [Abrahams96] ), представляют собой гиперссылки в приведенной выше странице.

[Abelson96]

Abelson H. and Sussman G. J. Structure and Interpretation of Computer Programs (2nd Edition) (MIT Press, 1996).

[Abrahams96]

Abrahams D. Exception Safety in STLport. STLport website, 1996.

[Abrahams01a]

Abrahams D. Exception Safety in Generic Components, in Jazayeri M., Loos R., Musser D. (eds.), Generic Programming: International Seminar on Generic Programming, Dagstuhl Castle, Germany, April/May 1998, Selected Papers, Lecture Notes in Computer Science 1766 (Springer, 2001).

[Abrahams01b]

Abrahams D. Error and Exception Handling. [Boost] website, 2001.

[Alexandrescu00a]

Alexandrescu A. Traits: The else-if-then of Types. C++ Report, 12(4), April 2000.

[Alexandrescu00b]

Alexandrescu A. Traits on Steroids. C++ Report, 12(6), June 2000.

[Alexandrescu00c]

Alexandrescu A. and Marginean P. Change the Way You Write Exception-Safe Code—Forever. C/C++ Users Journal, 18(12), December 2000.

[Alexandrescu01]

Alexandrescu A. Modern C++ Design. Addison-Wesley, 2001.

Перевод: Александреску А. Современное проектирование на C++. Серия C+ +

In-Depth, т.З. — М.: Издательский дом "Вильямс", 2002.

[Alexandrescu01a]

Alexandrescu A. A Policy-Based basic_string Implementation. C/C++ Users Journal, 19(6), June 2001.

[Alexandrescu02a]

Alexandrescu A. Multithreading and the C++ Type System. InformJT website, February 2002.

[Alexandrescu02b]

Alexandrescu A. "Discriminated Unions (I)," "... (II)," and "... (III)". C/C++ Users Journal, 20(4,6,8), April/June/August 2002.

[Alexandrescu03a]

Alexandrescu A. Move Constructors. C/C++ Users Journal, 21(2), February 2003.

[Alexandrescu03b]

Alexandrescu A. Assertions. C/C++ Users Journal, 21(4), April 2003.

[Alexandrescu03c]

Alexandrescu A. and Marginean P. Enforcements. C/C++ Users Journal, 21(6), June 2003.

[Alexandrescu03d]

Alexandrescu A. and Held D. Smart Pointers Reloaded. C/C++ Users Journal, 21(10), October 2003.

[Alexandrescu04]

Alexandrescu A. Lock-Free Data Structures. C/C++ Users Journal, 22(10), October 2004.

[Allison98]

Allison С. С & C++ Code Capsules. Prentice Hall, 1998.

[Austern99]

Austern M. H. Generic Programming and the STL. Addison-Wesley, 1999.

[Barton94]

Barton J. and Nackman L. Scientific and Engineering C++. Addison-Wesley, 1994.

[Bentley00]

Bentley J. Programming Pearls (2nd Edition). Addison-Wesley, 2000. Перевод: Бентли Дж. Жемчужины программирования. Второе издание. — СПб.: Питер, 2002.

[BetterSCM]

Web-узел Better SCM Initiative.

[Boost]

C++ Boost.

[BoostLRG]

Boost Library Requirements and Guidelines. (Web-узел Boost).

[Brooks95]

Brooks F. The Mythical Man-Month. Addison-Wesley, 1975; reprinted with corrections in 1995.

[Butenhof97]

Butenhof D. Programming with POSIX Threads. Addison-Wesley, 1997.

[Cargill92]

Cargill T. C++ Programming Style. Addison-Wesley, 1992.

<
Стр.203

[С90]

ISO/IEC 9899:1990(E), Programming Languages — С ( ISO C90 and ANSI C89 Standard).

[C99]

ISO/IEC 9899:1999(E), Programming Languages — С (revised ISO and ANSI C99 Standard).

[С++98]

ISO/IEC 14882:1998(E), Programming Languages—C++ (ISO and ANSI C++ Standard).

[С++03]

ISO/IEC 14882:2003(E), Programming Languages—C++ (updated ISO and ANSI C++ Standard including the contents of [C++98] plus errata corrections).

[C++TR104]

ISO/EEC JTC1/SC22/WG21/N1711. (Draft) Technical Report on Standard Library Extensions (ISO C++ committee working document, November 2004). Близкий к окончанию черновик с расширениями стандартной библиотеки C++, включая shared_ptr.

[Cline99]

Cline M., Lomow G., and Girou M. C++ FAQs (2ndEdition). Addison-Wesley, 1999.

[Constantine95]

Constantine L. Constantine on Peopleware. Yourdon Press, 1995.

[Coplien92]

Coplien J. Advanced C++ Programming Styles and Idioms. Addison-Wesley, 1992.

[Cormen01]

Cormen Т., Leiserson C, Rivest R., Stein C. Introduction to Algorithms (2nd Edition). MIT Press, 2001.

Перевод: Кормен Т., Лейзерсон Ч., Ривест Р., Стейн К. Введение в алгоритмы. 2-е изд. — М.: Издательский дом "Вильямс" (в печати)

[CVS]

Web-узел CVS.

[Cowan01]

Cowan С, Barringer ML, Beattie S., and Kroah-Hartman G. FormatGuard: Automatic Protection From printf Format String Vulnerabilities. Proceedings of the 2001 VSENIX Security Symposium, August 2001, Washington, D.C.

[Dewhurst03]

Dewhurst S. C++ Gotchas. Addison-Wesley, 2003.

[Dinkumware-Safe]

Dinkumware Unabridged Library documentation (Web-узел Dinkumware).

[Ellis90]

Ellis M. and Stroustrup B. The Annotated C++ Reference Manual. Addison-Wesley, 1990.

Перевод: Эллис М., Страуструп Б. Справочное руководство по языку программирования C++ с комментариями. — М.: Мир, 1992.

[Gamma95]

Gamma E., Helm R., Johnson R., and Vlissides J. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995.

Перевод: Гамма Э., Хелм Р., Джонсон Р., Влиссидес Дж. Приемы объектно-ориентированного проектирования. Паттерны проектирования. — СПб.: Питер, 2001.

[GnuMake]

Gnu make (Web-узел Gnu).

[GotW]

Sutter H. Guru of the Week column.

[Henney00]

Henney K. C++ Patterns: Executing Around Sequences (EuroPLoP 2000 proceedings).

[Henney01]

Henney K. C++ Patterns: Reference Accounting (EuroPLoP 2001 proceedings).

[Henney02a]

Henney K. Stringing Things Along. Application Development Advisor, July-August 2002.

[Henney02b]

Henney K. The Next Best String. Application Development Advisor, October 2002.

[Henricson97]

Henricson M. and Nyquist E. Industrial Strength C++. Prentice Hall,1997.

[Horstmann95]

Horstmann С S. Safe STL., 1995.

[Josuttis99]

Josuttis N. The C++ Standard Library. Addison-Wesley, 1999.

Перевод: Джосьютис Н. C++. Стандартная библиотека. — СПб.: Питер (в печати).

[Keffer95]

Keffer Т. Rogue Wave C++ Design, Implementation, and Style Guide. Rogue Wave Software, 1995.

[Kernighan99]

Kernighan B. and Pike R. The Practice of Programming. Addison-Wesley, 1999.

[Knuth89]

Knuth D. The Errors of TeX. Software—Practice & Experience, 19(7), July 1989.

[Knuth97a]

Knuth D. The Art of Computer Programming, Volume 1: Fundamental Algorithms, 3rd Edition. Addison-Wesley, 1997.

Перевод: Кнут Д. Искусство программирования, том I. Основные алгоритмы, 3-е изд. — М.: Издательский дом "Вильямс", 2000.

<


Стр.204

[Knuth97b]

Knuth D. The Art of Computer Programming, Volume 2: Seminumerical Algorithms, 3rd Edition. Addison-Wesley, 1997.

Перевод: Кнут Д. Искусство программирования, том 1. Получисленные алгоритмы, 3-е изд. — М.: Издательский дом "Вильямс", 2000.

[Knuth98]

Knuth D. The Art of Computer Programming, Volume 3: Sorting and Searching, 2nd Edition. Addison-Wesley, 1998.

Перевод: Кнут Д. Искусство программирования, том 1. Сортировка и поиск, 2-е изд. — М.: Издательский дом "Вильямс", 2000.

[Koenig97]

Koenig A. and Moo В. Ruminations on C++. Addison-Wesley, 1997.

[Lakos96]

Lakos J. Large-Scale C++ Software Design. Addison-Wesley, 1996.

[Liskov88]

Liskov B. Data Abstraction and Hierarchy. SIGPLAN Notices, 23(5), May 1988.

[Martin96a]

Martin R. C. The Dependency Inversion Principle. C++ Report, 8(5), May 1996.

[Martin96b]

Martin R. С Granularity. C++ Report, 8(9), October 1996.

[Martin96c]

Martin R. С The Open-Closed Principle. C++ Report, 8(1), January 1996.

[Martin98]

Martin R. C, Riehle D., Buschmann F. (eds.). Pattern Languages of Program Design 3. Addison-Wesley, 1998.

[Martin00]

Martin R. C. Abstract Classes and Pure Virtual Functions in Martin R. C. (ed.), More C++ Gems. Cambridge University Press, 2000.

[McConnell93]

McConnell S. Code Complete. Microsoft Press, 1993.

[Metrowerks]

Metrowerks.

[Meyer00]

Meyer B. Object-Oriented Software Construction, 2nd Edition. Prentice Hall, 2000.

[Meyers96]

Meyers S. More Effective C++. Addison-Wesley, 1996.

Перевод: Мейерс С. Наиболее эффективное использование C++. 35 новых рекомендаций по улучшению ваших программ и проектов. — М.: ДМК Пресс, 2000.

[Meyers97]

Meyers S. Effective C++, 2nd Edition. Addison-Wesley, 1997.

Перевод: Мейерс С. Эффективное использование C++. 50 рекомендаций по улучшению ваших программ и проектов. — М.: ДМК Пресс, 2000.

[Meyers00]

Meyers S. How Non-Member Functions Improve Encapsulation. C/C++ Users Journal, 18(2), February 2000.

[Meyers01]

Meyers S. Effective STL Addison-Wesley, 2001.

Перевод: Мейерс С. Эффективное использование STL. — СПб.: Питер, 2002.

[Meyers04]

Meyers S. and Alexandrescu A. C++ and the Perils of Double-Checked Locking, Part 1 and ...Part 2. Dr. Dobb's Journal, 29(7,8), July and August 2004.

[Milewski01]

Milewski B. C++ In Action. Addison-Wesley, 2001.

[Miller56]

Miller G. A. The Magical Number Seven, Plus or Minus Two: Some Limits on Our Capacity for Processing Information. The Psychological Review, 1956, vol. 63.

[MozillaCRFAQ]

Frequently Asked Questions About mozilla.org 's Code Review Process (Web-узел Mozilla).

[Murray93]

Murray R. C++ Strategies and Tactics. Addison-Wesley, 1993.

[Musser01]

Musser D. R., Derge G. J., and Saini A. STL Tutorial and Reference Guide, 2nd Edition. Addison-Wesley, 2001.

[Parnas02]

Parnas D. The Secret History of Information Hiding. Software Pioneers: Contributions To Software Engineering, Springer-Verlag, New York, 2002.

[Peters99]

Peters T. The Zen of Python. Сотр.lang.python, June 1999.

[Piwowarski82]

Piwowarski P. A Nesting Level Complexity Measure. ACM SIGPLAN Notices, 9/1982.

[Saks99]

Saks D. Thinking Deeply, Thinking Deeper, and Thinking Even Deeper. C/C++ Users Journal, 17(4,5,6), April, May, and June 1999.

[Schmidt01]

Schmidt D., Stal M., Rohnert H., Buschmann F. Pattern-Oriented Software Architecture, Volume 2: Patterns for Concurrent and Networked Objects, Wiley, 2001.

<


Стр.205

[SeamonkeyCR]

Seamonkey Code Reviewer's Guide (Web-узел Mozilla).

[Sedgewick98]

Sedgewick R. Algorithms in C++, Parts 1-4: Fundamentals, Data Structure, Sorting, Searching, 3rd Edition. Addison-Wesley, 1998.

[STLport-Debug]

Fomitchev B. STLport: Debug Mode (Web-узел STLport).

[Stroustrup94]

Stroustrup B. The Design and Evolution of C++. Addison-Wesley, 1994.

Перевод: Страуструп Б. Дизайн и эволюция языка C++. — М.: ДМК Пресс, 2000.

[Stroustrup00]

Stroustrup В. The C++ Programming Language (Special 3rd Edition). Addison-Wesley, 2000.

Перевод: Страуструп Б. Язык программирования C++. Специальное издание. — М.: Бином, 2001.

[Sutter99]

Sutter H. ACID Programming. Guru of the Week #6].

[Sutter00]

Sutter H. Exceptional C++. Addison-Wesley, 2000.

Перевод: Саттер Г. Решение сложных задач на C++. Серия C++ In-Depth, т.4. — М.: Издательский дом "Вильямс", 2002.

[Sutter02]

Sutter H. More Exceptional C++. Addison-Wesley, 2002.

Перевод: Саттер Г. Решение сложных задач на C++. Серия C++ In-Depth, т.4. — М.: Издательский дом "Вильямс", 2002.

[Sutter03]

Sutter H. Generalizing Observer. C/C++ Users Journal, 21(9), September 2003.

[Sutter04]

Sutter H. Exceptional C++ Style. Addison-Wesley, 2004.

Перевод: Саттер Г. Новые сложные задачи на C++. — М.: Издательский дом "Вильямс" (в печати).

[Sutter04a]

Sutter H. Function Types. C/C++ Users Journal, 22(7), July 2004.

[Sutter04b]

Sutter H. When and How To Use Exceptions. C/C++ Users Journal, 22(8), August 2004.

[Sutter04c]

Sutter H. Just Enough' Thread Safety. C/C++ Users Journal, 22(9), September 2004.

[Sutter04d]

Sutter H. How to Provide (or Avoid) Points of Customiza-tion in Templates. C/C+ + Users Journal, 22(11), November 2004.

[SuttHysl01]

Sutter H. and Hyslop J. Hungarian wartHogs. C/C++ Users Journal, 19(11), November 2001.

[SuttHysl02]

Sutter H. and Hyslop J. A Midsummer Night's Madness. C/C++ Users Journal, 20(8), August 2002.

[SuttHysl03]

Sutter H. and Hyslop J. Sharing Causes Contention. C/C++ Users Journal, 21(4), April 2003.

[SuttHysl04a]

Sutter H. and Hyslop J. Getting Abstractions. C/C++ Users Journal, 22(6), June 2004.

[SuttHysl04b]

Sutter H. and Hyslop J. Collecting Shared Objects. C/C++ Users Journal, 22(8), August 2004.

[Taligent94]

Taligent's Guide to Designing Programs. Addison-Wesley, 1994.

[Tsai01]

Tsai T. and Singh N. Libsafe 2.0: Detection of Format String Vulnerability Exploits. Avaya Labs, March 2001.

[Vandevoorde03]

Vandevoorde D. and Josuttis N. C++ Templates. Addison-Wesley, 2003.

Перевод: Вандевурд Д., Джосаттис Н. Шаблоны C++. Справочник разработчика. — М.: Издательский дом "Вильямс", 2003.

[Webber03]

Webber А. В. Modern Programming Languages: A Practical Introduction. Franklin, Beedle & Associates, 2003.

Стр.206


Стандарты кодирования и вы


Хорошие стандарты кодирования могут принести немалую выгоду с различных точек зрения.

Повышение качества кода. Работа в соответствии со стандартом приводит к однотипному решению одинаковых задач, что повышает ясность кода и упрощает его сопровождение.

Повышение скорости разработки. Разработчику не приходится решать все задачи и принимать решения "с нуля".

Повышение уровня взаимодействия в команде. Наличие стандарта позволяет уменьшить разногласия в команде и устранить ненужные дебаты по мелким вопросам, облегчает понимание и поддержку чужого кода членами команды.

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

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

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

Стандарты кодирования, приведенные в этой книге, представляют собой набор рекомендаций по написанию высококачественного кода на C++. В них сконцентрирован богатый коллективный опыт всего сообщества программистов на C++. Многие из этих знаний разбросаны по частям по самым разным книгам, но не меньшее количество знаний передается изустно. Мы постарались собрать разрозненные сведения в одной книге в виде коллекции ясных, компактных правил с пояснениями, простых для понимания и следования им.


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

Стр.11

нормы. Это служит хорошим фундаментом для дальнейшего усовершенствования и повышения квалификации. Это не преувеличение и не красивые слова — перед тем, как начать писать стихи, надо владеть словарным запасом и знать грамматику. Мы надеемся, что наша книга упростит для вас путь к поэзии программирования.

Эта книга предназначена для программистов всех уровней.

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

Для программистов среднего и высокого уровня при описании каждого правила приводится список ссылок, который позволит вам углубленно изучить заинтересовавший вас вопрос, проведя поиск корней правила в системе типов, грамматике и объектной модели C++.

Каким бы ни был ваш уровень как программиста — вероятно, вы работаете над сложным проектом не в одиночку, а в команде. Именно в этом случае разработка стандартов кодирования окупается сполна. Вы можете использовать их для того, чтобы подтянуть всю свою команду к одному, более высокому уровню, и обеспечить повышение качества разрабатываемого кода.


Стандарты программирования на C++101 правило и рекомендация


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

Издательский дом "Вильямс"

Москва • Санкт-Петербург • Киев

2005

ББК 32.973.26-018.2.75
С21
УДК 681.3.07

Издательский дом "Вильямс"

Зав. редакцией С.Н. Тригуб

Перевод с английского и редакция канд. техн. наук И.В. Красикова

По общим вопросам обращайтесь в Издательский дом "Вильямс" по адресу:
info@williamspublishing.com, http://www.williamspublishing.com


115419, Москва, а/я 783; 03150, Киев, а/я 152

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

Стандарты программирования на C++.: Пер. с англ. — М.: Издательский дом "Вильямс", 2005.— 224 с. :ил.— Парал. тит. англ.

ISBN 5-8459-0859-0 (рус.)

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

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

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

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

ББК 32.973.26-018.2.75

Все названия программных продуктов являются зарегистрированными торговыми марками соответствующих фирм.

Никакая часть настоящего издания ни в каких целях не может быть воспроизведена в какой бы то ни было форме и какими бы то ни было средствами, будь то электронные или механические, включая фотокопирование и запись на магнитный носитель, если на это нет письменного разрешения издательства Addison-Wesley Publishing Company. Inc.

Authorized translation from the English language edition published by Prentice Hall, Copyright © 2005

All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher.

Russian language edition was published by Williams Publishing House according to the Agreement with R&l Enterprises International, Copyright © 2005

© Издательский дом "Вильямс", 2005
© Pearson Education, Inc., 2005

ISBN 5-8459-0859-0 (рус.)
ISBN 0-321-11358-6 (англ.)

Стр.5



Стиль кодирования


Константа для одного является переменной для другого.
— Алан Перлис (Alan Perlis)

В этом разделе мы перейдем от вопросов проектирования к вопросам, которые появляются в основном при реальном кодировании.

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

В этом разделе мы считаем наиболее важной рекомендацию 14: "Предпочитайте ошибки компиляции и компоновки ошибкам времени выполнения".

Стр.40



Стиль кодирования


14.Предпочитайте ошибки компиляции и компоновки ошибкам времени выполнения

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

15. Активно используйте const

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

Стр.208

C++. Не выполняйте преобразований типов с отбрасыванием const кроме как при вызове некорректной с точки зрения употребления const функции (см. рекомендацию 94).

16. Избегайте макросов

Макрос — самый неприятный инструмент С и C++, оборотень, скрывающийся под личиной функции, кот, гуляющий сам по себе и не обращающий никакого внимания на границы ваших областей видимости. Берегитесь его!

17. Избегайте магических чисел

Избегайте использования в коде литеральных констант наподобие 42 или 3.1415926. Такие константы не самоочевидны и усложняют сопровождение кода, поскольку вносят в него трудноопределимый вид дублирования. Используйте вместо них символьные имена и выражения наподобие width*aspectRatio.

18. Объявляйте переменные как можно локальнее

Избегайте "раздувания" областей видимости. Переменных должно быть как можно меньше, а время их жизни — как можно короче. Эта рекомендация по сути является частным случаем рекомендации 10.

19. Всегда инициализируйте переменные


Неинициализированные переменные—распространенный источник ошибок в программах на С и C++. Избегайте их, выработав привычку очищать память перед ее использованием; инициализируйте переменные при их определении.

20. Избегайте длинных функций и глубокой вложенности

Краткость — сестра таланта. Чересчур длинные функции и чрезмерно вложенные блоки кода зачастую препятствуют реализации принципа "одна функция — одна задача" (см. рекомендацию 5), и обычно эта проблема решается лучшим разделением задачи на отдельные части.

21. Избегайте зависимостей инициализаций между единицами компиляции

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

22. Минимизируйте зависимости определений и избегайте циклических зависимостей

Избегайте излишних зависимостей. Не включайте при помощи директивы #include определения там, где достаточно предварительного объявления.

Избегайте взаимозависимостей. Циклические зависимости возникают, когда два модуля непосредственно или опосредованно зависят друг от друга. Модуль представляет собой обособленную единицу; взаимозависимые модули не являются полностью отдельными модулями, будучи по сути частями одного большего модуля. Таким образом, циклические зависимости являются противниками модульности и представляют угрозу большим проектам. Избегайте их.

23. Делайте заголовочные файлы самодостаточными

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

24. Используйте только внутреннюю, но не внешнюю защиту директивы #include

Предотвращайте непреднамеренное множественное включение ваших заголовочных файлов директивой #include, используя в них защиту с уникальными именами.

Стр.209


Стиль проектирования


Дураки игнорируют сложности. Прагматики терпят их. Некоторые ухитряются их избегать. И только гении устраняют их.


— Алан Перлис (Alan Perlis)

Я также знал, но забыл афоризм Хоара о том, что преждевременная оптимизация— корень всех зол в программировании.


— Дональд Кнут (Donald Knuth),
The Errors of TeX

[Knuth89]

Очень сложно отделить стиль проектирования от стиля кодирования. Мы попытались поместить в очередном разделе те вопросы, которые обычно проявляются, когда вы начинаете писать реальный код.

В этом разделе внимание уделяется принципам и практическим вопросам, область применения которых больше, чем просто отдельный класс или функция. Классические примеры — баланс между простотой и ясностью (рекомендация 6), избежание преждевременной оптимизации (рекомендация 8) и пессимизации (рекомендация 9). Эти рекомендации применимы не только на уровне кодирования отдельной функции, но и к большим областям — проектированию классов и модулей или к решениям с далеко идущими последствиями по поводу архитектуры приложений. (Они применимы также для всех программистов. Если вы считаете иначе — еще раз прочтите приведенную выше цитату Кнута и даже заучите ее на память.)

Многие из прочих рекомендаций этого и следующего разделов имеют дело с управлением зависимостями — краеугольным камнем проектирования программного обеспечения и часто повторяющейся в данной книге темой. Остановитесь и задумайтесь над произвольной методикой проектирования программного обеспечения — любой хорошей методикой. Какую бы методику вы ни выбрали, так или иначе вы столкнетесь с ослаблением зависимостей. Наследование? Разрабатываемый код должен делать производный класс как можно менее зависимым от базового класса. Минимизация использования глобальных переменных? Снижает дальнодействующие зависимости, осуществляемые посредством широко видимых разным частям программы данных. Абстракция? Устраняет зависимости между кодом, который управляет концепциями, и кодом, который их реализует. Сокрытие информации? Делает код клиента менее зависимым от деталей реализации. Забота об управлении зависимостями отражается в устранении совместного использования состояния (рекомендация 10), применении сокрытия информации (рекомендация 11) и прочем.

В этом разделе самой важной нам кажется рекомендация 6: " Главное — корректность, простота и ясность".

Стр.24



Стиль проектирования


5. Один объект — одна задача

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

6. Главное — корректность, простота и ясность

Корректность лучше быстроты. Простота лучше сложности. Ясность лучше хитроумия. Безопасность лучше ненадежности (см. рекомендации 83 и 99).

7. Кодирование с учетом масштабируемости

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

Стр.207

8. Не оптимизируйте преждевременно

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

9. Не пессимизируйте преждевременно

То, что просто для вас, — просто и для кода. При прочих равных условиях, в особенности — сложности и удобочитаемости кода, ряд эффективных шаблонов проектирования и идиом кодирования должны естественным образом "стекать с кончиков ваших пальцев" и быть не сложнее в написании, чем их пессимизированные альтернативы. Это не преждевременная оптимизация, а избежание излишней пессимизации.

10. Минимизируйте глобальные и совместно используемые данные

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

11. Сокрытие информации

Не выпускайте внутреннюю информацию за пределы объекта, обеспечивающего абстракцию.

12. Кодирование параллельных вычислений

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

13. Ресурсы должны быть во владении объектов

Не работайте вручную, если у вас есть мощные инструменты. Идиома C++ "выделение ресурса есть инициализация" (resource acquisition is initialization — RAII) представляет собой мощный инструмент для корректной работы с ресурсами. RAII позволяет компилятору автоматически обеспечить строгую гарантию того, что в других языках надо делать вручную. При выделении ресурса передайте его объекту-владельцу. Никогда не выделяйте несколько ресурсов в одной инструкции.



STL: алгоритмы


Предпочитайте алгоритмы циклам.


— Бьярн Страуструп (Bjarne Stroustrup),

[Stroustrup00] §18.12

Алгоритмы представляют собой циклы — только они лучше циклов. Алгоритмы — это "шаблоны" циклов, с добавлением дополнительной семантики по сравнению с простыми for и do. Конечно, начав использовать алгоритмы, вы начнете использовать и функциональные объекты и предикаты; корректно пишите их и широко используйте в своих программах.

В этом разделе мы считаем наиболее значимой рекомендацию 83 — "Используйте отладочную реализацию STL".

Стр.174



STL: алгоритмы


83.Используйте отладочную реализацию STL

Безопасность превыше всего (см. рекомендацию 6). Используйте отладочную реализацию STL, даже если она имеется только для одного из ваших компиляторов, и даже если она используется только для отладочного тестирования.

84. Предпочитайте вызовы алгоритмов самостоятельно разрабатываемым циклам

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

При вызове алгоритма подумайте о написании собственного функционального объекта, который инкапсулирует всю необходимую логику. Избегайте объединения связывателей параметров и простых функциональных объектов (например, bind2nd, plus), что обычно снижает ясность кода. Подумайте об использовании лямбда-библиотеки [Boost] , которая автоматизирует задачу написания функциональных объектов.

85. Пользуйтесь правильным алгоритмом поиска

Данная рекомендация применима к поиску определенного значения в диапазоне. При поиске в неотсортированном диапазоне используйте алгоритмы find/find_if или count/count_if. Для поиска в отсортированном диапазоне выберите lower_bound, upper_bound, equal_range или (реже) binary_search. (Вопреки своему имени, binary_search обычно — неверный выбор.)

86. Пользуйтесь правильным алгоритмом сортировки

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

87. Делайте предикаты чистыми функциями

Предикат представляет собой функциональный объект, который возвращает ответ да/ нет, обычно в виде значения типа bool. Функция является "чистой" в математическом смысле, если ее результат зависит только от ее аргументов (обратите внимание — в данном случае термин "чистая" не имеет никакого отношения к чисто виртуальным функциям).


Не позволяйте предикатам сохранять или обращаться к состоянию так, чтобы это могло влиять на результат работы оператора operator(); при этом понятие состояния включает как данные-члены, так и глобальные состояния. Для предикатов желательно делать оператор operator() константной функцией-членом (см. рекомендацию 15).

88. В качестве аргументов алгоритмов и компараторов лучше использовать функциональные объекты, а не функции

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

89. Корректно пишите функциональные объекты

Разрабатывайте функциональные объекты так, чтобы их копирование выполнялось как можно эффективнее. Там, где это возможно, делайте их максимально адаптируемыми путем наследования от unary_function или binary_function.

Стр.218


STL: контейнеры


Если вам нужен контейнер, по умолчанию используйте vector.


— Бьярн Страуструп (Bjarne Stroustrup),

[Stroustrup00] §17.7

Мы знаем, что вы уже предпочитаете использовать стандартные контейнеры вместо написанных вручную. Но какой именно контейнер следует использовать? Что должно (и что не должно) храниться в контейнерах и почему? Как следует заполнять контейнеры? Какие важные идиомы надо знать?

В этом разделе имеются ответы на все перечисленные (и не только) вопросы. И не случайно, что три первые рекомендации в нем содержат слова "Используйте vector...".

В этом разделе мы считаем наиболее значимой рекомендацию 79 — "Храните в контейнерах только значения или интеллектуальные указатели". К ней мы добавим — если даже вы не планируете применять [Boost] и [C++TR104] для иных целей, все равно воспользуйтесь их интеллектуальным указателем shared_ptr.

Стр.164



STL: Контейнеры


76. По умолчанию используйте vector. В противном случае выбирайте контейнер, соответствующий задаче

Очень важно использовать "правильный контейнер". Если у вас есть весомые причины выбрать определенный тип контейнера, используйте тот контейнер, который наиболее подходит для вашей задачи.

Если конкретных предпочтений нет, возьмите vector и спокойно работайте, зная, что вы сделали верный выбор.

77. Вместо массивов используйте vector и string

Избегайте реализации абстракции массива посредством массивов в стиле С, арифметики указателей и примитивов управления памятью. Использование vector или string не только сделает проще вашу жизнь, но и позволит написать более безопасную и масштабируемую программу.

78. Используйте vector (и string::c_str) для обмена данными с API на других языках

vector и string::с_str служат шлюзом для сообщения с API на других языках. Однако не полагайтесь на то, что итераторы являются указателями; для получения адреса элемента, на который ссылается vector<T>:.iterator iter, используйте выражение &*iter.

79. Храните в контейнерах только значения или интеллектуальные указатели

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

80. Предпочитайте push_back другим способам расширения последовательности

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

81. Предпочитайте операции с диапазонами операциям с отдельными элементами

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

82. Используйте подходящие идиомы для реального уменьшения емкости контейнера и удаления элементов

Для того чтобы действительно избавиться от излишней емкости контейнера, воспользуйтесь трюком с использованием обмена, а для реального удаления элементов из контейнера — идиомой erase-remove.

Стр.217



Вопросы организации и стратегии


Если бы строители строили здания так же, как программисты пишут программы,— то первый же залетевший дятел разрушил бы всю цивилизацию.
— Джеральд Вайнберг (Gerald Weinberg)

Следуя великой традиции С и C++, мы начинаем отсчет с нуля. Главный совет — под номером 0 — говорит о том, что основной советчик по поводу стандартов кодирования — наши чувства и ощущения.

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

В этом разделе книги наиболее важной мы считаем нулевую рекомендацию — "Не мелочитесь, или Что не следует стандартизировать".

Стр.14