Система Orphus

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

101 правило и рекомендация

Авторы: Герб Саттер
Андрей Александреску
Издательство: Вильямс, 2005
224 страницы

Материал предоставил: Алексей Кирюшкин
Найти в магазинах
Купить в Books.Ru
Купить в Болеро (198 руб.)

Аннотация

Содержание
Предисловие
Комментарии

Аннотация

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

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

Содержание

Предисловие

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

0. Не мелочитесь, или Что не следует стандартизировать
1. Компилируйте без замечаний при максимальном уровне предупреждений
2. Используйте автоматические системы сборки программ
3. Используйте систему контроля версий
4. Одна голова хорошо, а две - лучше

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

5. Один объект - одна задача
6. Главное - корректность, простота и ясность
7. Кодирование с учетом масштабируемости
8. Не оптимизируйте преждевременно
9. Не пессимизируйте преждевременно
10. Минимизируйте глобальные и совместно используемые данные
11. Сокрытие информации
12. Кодирование параллельных вычислений
13. Ресурсы должны быть во владении объектов

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

14. Предпочитайте ошибки компиляции и компоновки ошибкам времени выполнения
15. Активно используйте const
16. Избегайте макросов
17. Избегайте магических чисел
18. Объявляйте переменные как можно локальнее
19. Всегда инициализируйте переменные
20. Избегайте длинных функций и глубокой вложенности
21. Избегайте зависимостей инициализаций между единицами компиляции
22. Минимизируйте зависимости определений и избегайте циклических зависимостей
23. Делайте заголовочные файлы самодостаточными
24. Используйте только внутреннюю, но не внешнюю защиту директивы #include

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

25. Передача параметров по значению, (интеллектуальному) указателю или ссылке
26. Сохраняйте естественную семантику перегруженных операторов
27. Отдавайте предпочтение каноническим формам арифметических операторов и операторов присваивания
28. Предпочитайте канонический вид ++ и -, и вызов префиксных операторов
29. Используйте перегрузку, чтобы избежать неявного преобразования типов
30. Избегайте перегрузки &&, || и , (запятой)
31. Не пишите код, который зависит от порядка вычислений аргументов функции

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

32. Ясно представляйте, какой вид класса вы создаете
33. Предпочитайте минимальные классы монолитным
34. Предпочитайте композицию наследованию
35. Избегайте наследования от классов, которые не спроектированы для этой цели
36. Предпочитайте предоставление абстрактных интерфейсов
37. Открытое наследование означает заменимость. Наследовать надо не для повторного использования, а чтобы быть повторно использованным
38. Практикуйте безопасное перекрытие
39. Виртуальные функции стоит делать неоткрытыми, а открытые - невиртуальными
40. Избегайте возможностей неявного преобразования типов
41. Делайте данные-члены закрытыми (кроме случая агрегатов в стиле структур С)
42. Не допускайте вмешательства во внутренние дела
43. Разумно пользуйтесь идиомой Pimpl
44. Предпочитайте функции, которые не являются ни членами, ни друзьями
45. new и delete всегда должны разрабатываться вместе
46. При наличии пользовательского new следует предоставлять все стандартные типы этого оператора

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

47. Определяйте и инициализируйте переменные-члены в одном порядке
48. В конструкторах предпочитайте инициализацию присваиванию
49. Избегайте вызовов виртуальных функций в конструкторах и деструкторах
50. Делайте деструкторы базовых классов открытыми и виртуальными либо защищенными и невиртуальными
51. Деструкторы, функции освобождения ресурсов и обмена не ошибаются
52. Копируйте и ликвидируйте согласованно
53. Явно разрешайте или запрещайте копирование
54. Избегайте срезки. Подумайте об использовании в базовом классе клонирования вместо копирования
55. Предпочитайте канонический вид присваивания
56. Обеспечьте бессбойную функцию обмена

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

57. Храните типы и их свободный интерфейс в одном пространстве имен
58. Храните типы и функции в разных пространствах имен, если только они не предназначены для совместной работы
59. Не используйте using для пространств имен в заголовочных файлах или перед директивой #include
60. Избегайте выделения и освобождения памяти в разных модулях
61. Не определяйте в заголовочном файле объекты со связыванием
62. Не позволяйте исключениям пересекать границы модулей
63. Используйте достаточно переносимые типы в интерфейсах модулей

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

64. Разумно сочетайте статический и динамический полиморфизм
65. Выполняйте настройку явно и преднамеренно
66. Не специализируйте шаблоны функций
67. Пишите максимально обобщенный код

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

68. Широко применяйте assert для документирования внутренних допущений и инвариантов
69. Определите разумную стратегию обработки ошибок и строго ей следуйте
70. Отличайте ошибки от ситуаций, не являющихся ошибками
71. Проектируйте и пишите безопасный в отношении ошибок код
72. Для уведомления об ошибках следует использовать исключения
73. Генерируйте исключения по значению, перехватывайте - по ссылке
74. Уведомляйте об ошибках, обрабатывайте и преобразовывайте их там, где следует
75. Избегайте спецификаций исключений

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

76. По умолчанию используйте vector. В противном случае выбирайте контейнер, соответствующий задаче
77. Вместо массивов используйте vector и string
78. Используйте vector (и string::c_str) для обмена данными с API на других языках
79. Храните в контейнерах только значения или интеллектуальные указатели
80. Предпочитайте push_back другим способам расширения последовательности
81. Предпочитайте операции с диапазонами операциям с отдельными элементами
82. Используйте подходящие идиомы для реального уменьшения емкости контейнера и удаления элементов

STL: алгоритмы

83. Используйте отладочную реализацию STL
84. Предпочитайте вызовы алгоритмов самостоятельно разрабатываемым циклам
85. Пользуйтесь правильным алгоритмом поиска
86. Пользуйтесь правильным алгоритмом сортировки
87. Делайте предикаты чистыми функциями
88. В качестве аргументов алгоритмов и компараторов лучше использовать функциональные объекты, а не функции
89. Корректно пишите функциональные объекты

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

90. Избегайте явного выбора типов - используйте полиморфизм
91. Работайте с типами, а не с представлениями
92. Избегайте reinterpret_cast
93. Избегайте применения static_cast к указателям
94. Избегайте преобразований, отменяющих const
95. Не используйте преобразование типов в стиле С
96. Не применяйте memcpy или memcmp к не-POD типам
97. Не используйте объединения для преобразований
98. Не используйте неизвестные аргументы (троеточия)
99. Не используйте недействительные объекты и небезопасные функции
100. Не рассматривайте массивы полиморфно

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

Предисловие

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

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

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

Лучшее в стандарте то, что он предоставляет богатый выбор.

- Приписывается разным людям

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Об этой книге

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

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

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

Комментарии

Алексей Кирюшкин

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

Если в отношении assert авторы не испытывают никаких сомнений ("Трудно переоценить всю мощь assert" - Рекомендация 68. Широко применяйте assert для документирования внутренних допущений и инвариантов), то с оптимизацией все не так однозначно.

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

Под преждевременной пессимизацией авторы понимают, например:

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

Что касается pro и contra исключений я обратил внимание на один аргумент "за", может и звучавший при обсуждении этого вопроса в наших форумах, но явно затерявшийся на фоне рассуждений о негативном влиянии исключений на производительность, а именно:

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

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