- •Герб Саттер
 - •Как пользоваться этой книгой
 - •Стандарты кодирования и вы
 - •Об этой книге
 - •Благодарности
 - •Вопросы организации и стратегии
 - •0. Не мелочитесь, или Что не следует стандартизировать Резюме
 - •Обсуждение
 - •Примеры
 - •1. Компилируйте без замечаний при максимальном уровне предупреждений Резюме
 - •Обсуждение
 - •Примеры
 - •Исключения
 - •2. Используйте автоматические системы сборки программ Резюме
 - •Обсуждение
 - •3. Используйте систему контроля версий Резюме
 - •Обсуждение
 - •Исключения
 - •Стиль проектирования
 - •5. Один объект — одна задача Резюме
 - •Обсуждение
 - •Примеры
 - •6. Главное — корректность, простота и ясность Резюме
 - •Обсуждение
 - •Примеры
 - •7. Кодирование с учетом масштабируемости Резюме
 - •Обсуждение
 - •8. Не оптимизируйте преждевременно Резюме
 - •Обсуждение
 - •Примеры
 - •Исключения
 - •10. Минимизируйте глобальные и совместно используемые данные Резюме
 - •Обсуждение
 - •Исключения
 - •11. Сокрытие информации Резюме
 - •Обсуждение
 - •Исключения
 - •13. Ресурсы должны быть во владении объектов Резюме
 - •Обсуждение
 - •Исключения
 - •Стиль кодирования
 - •14. Предпочитайте ошибки компиляции и компоновки ошибкам времени выполнения Резюме
 - •Обсуждение
 - •Примеры
 - •Исключения
 - •Примеры
 - •16. Избегайте макросов Резюме
 - •Обсуждение
 - •Примеры
 - •Исключения
 - •17. Избегайте магических чисел Резюме
 - •Обсуждение
 - •Примеры
 - •18. Объявляйте переменные как можно локальнее Резюме
 - •Обсуждение
 - •Исключения
 - •Примеры
 - •Исключения
 - •Исключения
 - •22. Минимизируйте зависимости определений и избегайте циклических зависимостей Резюме
 - •Обсуждение
 - •Исключения
 - •Примеры
 - •24. Используйте только внутреннюю, но не внешнюю защиту директивы #include Резюме
 - •Обсуждение
 - •Исключения
 - •Функции и операторы
 - •25. Передача параметров по значению, (интеллектуальному) указателю или ссылке Резюме
 - •Обсуждение
 - •26. Сохраняйте естественную семантику перегруженных операторов Резюме
 - •Обсуждение
 - •Исключения
 - •27. Отдавайте предпочтение каноническим формам арифметических операторов и операторов присваивания Резюме
 - •Обсуждение
 - •Примеры
 - •Исключения
 - •30. Избегайте перегрузки &&, || и , (запятой) Резюме
 - •Обсуждение
 - •Примеры
 - •Исключения
 - •Проектирование классов и наследование
 - •32. Ясно представляйте, какой вид класса вы создаете Резюме
 - •Обсуждение
 - •33. Предпочитайте минимальные классы монолитным Резюме
 - •Обсуждение
 - •34. Предпочитайте композицию наследованию Резюме
 - •Обсуждение
 - •Исключения
 - •35. Избегайте наследования от классов, которые не спроектированы для этой цели Резюме
 - •Обсуждение
 - •36. Предпочитайте предоставление абстрактных интерфейсов Резюме
 - •Обсуждение
 - •Примеры
 - •Исключения
 - •Исключения
 - •Примеры
 - •39. Виртуальные функции стоит делать неоткрытыми, а открытые — невиртуальными Резюме
 - •Обсуждение
 - •Исключения
 - •Примеры
 - •Исключения
 - •41. Делайте данные-члены закрытыми (кроме случая агрегатов в стиле структур с) Резюме
 - •Обсуждение
 - •Примеры
 - •Исключения
 - •42. Не допускайте вмешательства во внутренние дела Резюме
 - •Обсуждение
 - •Исключения
 - •43. Разумно пользуйтесь идиомой Pimpl Резюме
 - •Обсуждение
 - •Исключения
 - •Примеры
 - •Исключения
 - •Конструкторы, деструкторы и копирование
 - •47. Определяйте и инициализируйте переменные-члены в одном порядке Резюме
 - •Обсуждение
 - •48. В конструкторах предпочитайте инициализацию присваиванию Резюме
 - •Обсуждение
 - •Исключения
 - •Примеры
 - •50. Делайте деструкторы базовых классов открытыми и виртуальными либо защищенными и невиртуальными Резюме
 - •Обсуждение
 - •Примеры
 - •Исключения
 - •51. Деструкторы, функции освобождения ресурсов и обмена не ошибаются Резюме
 - •Обсуждение
 - •52. Копируйте и ликвидируйте согласованно Резюме
 - •Обсуждение
 - •Исключения
 - •53. Явно разрешайте или запрещайте копирование Резюме
 - •Обсуждение
 - •54. Избегайте срезки. Подумайте об использовании в базовом классе клонирования вместо копирования Резюме
 - •Обсуждение
 - •Исключения
 - •56. Обеспечьте бессбойную функцию обмена Резюме
 - •Обсуждение
 - •Исключения
 - •Пространства имен и модули
 - •57. Храните типы и их свободный интерфейс в одном пространстве имен Резюме
 - •Обсуждение
 - •Примеры
 - •58. Храните типы и функции в разных пространствах имен, если только они не предназначены для совместной работы Резюме
 - •Обсуждение
 - •59. Не используйте using для пространств имен в заголовочных файлах или перед директивой #include Резюме
 - •Обсуждение
 - •Исключения
 - •60. Избегайте выделения и освобождения памяти в разных модулях Резюме
 - •Обсуждение
 - •61. Не определяйте в заголовочном файле объекты со связыванием Резюме
 - •Обсуждение
 - •Исключения
 - •62. Не позволяйте исключениям пересекать границы модулей Резюме
 - •Обсуждение
 - •63. Используйте достаточно переносимые типы в интерфейсах модулей Резюме
 - •Обсуждение
 - •Примеры
 - •65. Выполняйте настройку явно и преднамеренно Резюме
 - •Обсуждение
 - •66. Не специализируйте шаблоны функций Резюме
 - •Обсуждение
 - •67. Пишите максимально обобщенный код Резюме
 - •Обсуждение
 - •Исключения
 - •Обработка ошибок и исключения
 - •68. Широко применяйте assert для документирования внутренних допущений и инвариантов Резюме
 - •Обсуждение
 - •Примеры
 - •69. Определите разумную стратегию обработки ошибок и строго ей следуйте Резюме
 - •Обсуждение
 - •70. Отличайте ошибки от ситуаций, не являющихся ошибками Резюме
 - •Обсуждение
 - •Примеры
 - •71. Проектируйте и пишите безопасный в отношении ошибок код Резюме
 - •Обсуждение
 - •Примеры
 - •72. Для уведомления об ошибках следует использовать исключения Резюме
 - •Обсуждение
 - •Примеры
 - •Исключения
 - •73. Генерируйте исключения по значению, перехватывайте — по ссылке Резюме
 - •Обсуждение
 - •Примеры
 - •Исключения
 - •Исключения
 - •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. Не используйте объединения для преобразований Резюме
 - •Обсуждение
 - •Исключения
 - •99. Не используйте недействительные объекты и небезопасные функции Резюме
 - •Обсуждение
 - •100. Не рассматривайте массивы полиморфно Резюме
 - •Обсуждение
 - •Список литературы
 - •Резюме из резюме
 - •8. Не оптимизируйте преждевременно
 - •15. Активно используйте const
 - •23. Делайте заголовочные файлы самодостаточными
 - •36. Предпочитайте предоставление абстрактных интерфейсов
 - •61. Не определяйте в заголовочном файле объекты со связыванием
 - •62. Не позволяйте исключениям пересекать границы модулей
 - •63. Используйте достаточно переносимые типы в интерфейсах модулей
 - •70. Отличайте ошибки от ситуаций, не являющихся ошибками
 - •71. Проектируйте и пишите безопасный в отношении ошибок код
 - •72. Для уведомления об ошибках следует использовать исключения
 - •73. Генерируйте исключения по значению, перехватывайте — по ссылке
 - •85. Пользуйтесь правильным алгоритмом поиска
 - •86. Пользуйтесь правильным алгоритмом сортировки
 - •87. Делайте предикаты чистыми функциями
 - •88. В качестве аргументов алгоритмов и компараторов лучше использовать функциональные объекты, а не функции
 
Примеры
Пример. Передача инстанцирования шаблона макросу. Макросы понимают в достаточной мере только круглые и квадратные скобки. В С++, однако, определена новая конструкция с угловыми скобками, используемая в шаблонах. Макросы не могут корректно обработать эту ситуацию, так что вызов
macro(Foo<int, double>)
макрос воспринимает так, будто ему переданы два аргумента, а именно Foo<int и doublе>, в то время как в действительности эта конструкция представляет собой единый объект С++.
Исключения
Макросы остаются единственно возможным решением для некоторых важных задач, таких как защита директивы #include (см. рекомендацию 24), использование директив #ifdef и #if defined для условной компиляции и реализация assert (см. рекомендацию 68).
При условной компиляции (например, системно-зависимых частей) избегайте разброса по всему тексту директив #ifdef. Вместо этого лучше организовать код таким образом, чтобы использование макросов обеспечивало возможность альтернативных реализаций одного общего интерфейса, который затем будет использоваться в программе.
Можно (но осторожно) использовать макросы вместо большого количества копирований и вставок близких фрагментов кода.
Заметим, что [C99] и [Boost] включают соответственно умеренные и радикальные расширения препроцессоров.
Ссылки
[Boost] • [С99] • [Dewhurst03] §25-28 • [Lakos96] §2.3.4 • [Meyers96] §1 • [Stroustrup94] §3.3.1 • [Stroustrup00] §1.6.1, §7.8 • [Sutter02] §34-35 • [Sutter04] §31 • [Sutter04a]
17. Избегайте магических чисел Резюме
Избегайте использования в коде литеральных констант наподобие 42 или 3.1415926. Такие константы не самоочевидны и усложняют сопровождение кода, поскольку вносят в него трудноопределимый вид дублирования. Используйте вместо них символьные имена и выражения наподобие width*aspectRatiо.
Обсуждение
Имена добавляют информацию и вводят единую точку сопровождения; в отличие от них дублированные по всей программе обычные числа анонимны и трудно сопровождаемы. Константы должны быть перечислениями или const-значениями, с соответствующими областями видимости и именами.
Одно число 42 может не быть тем же числом 42, что и другое. Что еще хуже, программист может выполнять какие-то вычисления "в уме" (например: "Вот это 84 — просто удвоенное 42, которое было пятью строками ранее"), что совершенно запутывает код и делает последующую замену 42 другой константой источником огромного количества ошибок.
Лучше заменять такие жестко кодированные величины символьными константами. Строки лучше хранить отдельно от кода (например, в отдельном .срр-файле или файле ресурса), что позволит непрограммистам просмотреть и обновить их, снижая количество дубликатов и помогая в интернационализации вашей программы.
Примеры
Пример 1. Важные константы из предметной области на уровне пространств имен.
const size_t PAGE_SIZE = 8192,
WORDS_PER_PAGE = PAGE_SIZE / sizeof(int),
INFO_BITS_PER_PAGE = 32 * CHAR_BIT;
Пример 2. Константы, специфичные для данного класса. Вы можете определить статические интегральные константы в определении класса; константы других типов требуют отдельного определения или применения коротких функций.
// Файл widget.h
class Widget {
// Значение указано в объявлении
static const int defaultWidth = 400;
// Значение указано в определении
static const double defaultPercent;
static const char* Name() { return "widget"; }
};
// Файл widget.cpp
// Значение указано в определении
const double widget::defaultPercent = 66.67;
// Требуется объявление
const int widget::defaultWidth;
Ссылки
[Dewhurst03] §2 • [Kernighan99] §1.5 • [Stroustrup00] §4.8, §5.4
