Используйте assert или его эквивалент для документирования внутренних допущений в модуле (т.е. там, где вызываемый и вызывающий код поддерживаются одним и тем же программистом или командой), которые должны всегда выполняться (в противном случае они являются следствием программной ошибки; например, нарушение постусловий функции, обнаруженное вызывающим кодом). (См. также рекомендацию 70.) Убедитесь, что использование assert не приводит к побочным действиям.
69. Определите разумную стратегию обработки ошибок и строго ей следуйте
Еще на ранней стадии проектирования разработайте практичную, последовательную и разумную стратегию обработки ошибок и строго следуйте ей. Убедитесь, что ваша стратегия включает следующее.
• Идентификация: какие условия являются ошибкой.
• Строгость: насколько важна каждая ошибка.
• Обнаружение: какой код отвечает за обнаружение ошибки.
• Распространение: какой механизм используется для описания и распространения уведомления об ошибке в каждом модуле.
• Обработка: какой код отвечает за выполнение действий, связанных с ошибкой.
• Уведомление: каким образом информация об ошибке вносится в журнальный файл или производится уведомление пользователя программы.
Изменяйте механизмы обработки ошибок только в пределах границ модуля.
70. Отличайте ошибки от ситуаций, не являющихся ошибками
Функция представляет собой единицу работы. Таким образом, сбои следует рассматривать либо как ошибки, либо как штатные ситуации, в зависимости от их влияния на функции. В функции f сбой является ошибкой тогда и только тогда, когда он нарушает одно из предусловий f, не позволяет выполнить предусловие вызываемой ею функции, препятствует достижению собственных постусловий f или сохранению инварианта, за поддержку которого отвечает функция f.
В частности, в этой рекомендации мы исключаем внутренние программные ошибки (т.е. те, где за вызывающий и вызываемый код отвечает один и тот же человек или команда, — например, в пределах одного модуля). Они представляют собой отдельную категорию ошибок, для работы с которой используется такое средство, как проверки (см. рекомендацию 68).
71. Проектируйте и пишите безопасный в отношении ошибок код
В каждой функции обеспечивайте наиболее строгую гарантию безопасности, какой только можно добиться без дополнительных затрат со стороны вызывающего кода, не требующего такого уровня гарантии. Всегда обеспечивайте, как минимум, базовую гарантию безопасности.
Убедитесь, что при любых ошибках ваша программа всегда остается в корректном состоянии (в этом и заключается базовая гарантия). Остерегайтесь ошибок, нарушающих инвариант (включая утечки, но не ограничиваясь ими).
Желательно дополнительно гарантировать, что конечное состояние либо является исходным состоянием (в результате отката после происшедшей ошибки), либо корректно вычисленным целевым состоянием (если ошибок не было). Это — строгая гарантия безопасности.
Еще лучше гарантировать, что сбой в процессе операции невозможен. Хотя для большинства функций это невозможно, такую гарантию следует обеспечить для таких функций, как деструкторы и функции освобождения ресурсов. Данная гарантия — гарантия бессбойности.
72. Для уведомления об ошибках следует использовать исключения
Для уведомления об ошибках лучше использовать механизм исключений, а не коды ошибок. Применять коды состояния (например, коды ошибок, переменную errno) следует только тогда, когда нельзя использовать исключения (см. рекомендацию 62), а также для ситуаций, которые не являются ошибками. К другим методам, таким как экстренное завершение программы (или плановое завершение с освобождением ресурсов и т.п. действиями), следует прибегать только в ситуациях, когда восстановление после ошибки невозможно (или не требуется).
73. Генерируйте исключения по значению, перехватывайте — по ссылке
Генерируйте исключения по значению (не через указатель) и перехватывайте их как ссылки (обычно константные). Эта комбинация наилучшим образом соответствует семантике исключений. При повторной генерации перехваченного исключения предпочтительно использовать просто оператор throw;, а не инструкцию throw e;.
74. Уведомляйте об ошибках, обрабатывайте и преобразовывайте их там, где следует
Сообщайте об ошибках в тот момент, когда они обнаружены и идентифицированы как ошибки. Обрабатывайте или преобразовывайте их на самом нижнем уровне, на котором это можно сделать корректно.
75. Избегайте спецификаций исключений
Не пишите спецификаций исключений у ваших функций, если только вас не заставляют это делать внешние обстоятельства (например, код, который вы не можете изменить, уже ввел их; см. исключения к данному разделу).
STL: Контейнеры 76. По умолчанию используйте vector. В противном случае выбирайте контейнер, соответствующий задаче
Очень важно использовать 'правильный контейнер'. Если у вас есть весомые причины выбрать определенный тип контейнера, используйте тот контейнер, который наиболее подходит для вашей задачи.
Если конкретных предпочтений нет, возьмите vector и спокойно работайте, зная, что вы сделали верный выбор.
77. Вместо массивов используйте vector и string
Избегайте реализации абстракции массива посредством массивов в стиле С, арифметики указателей и примитивов управления памятью. Использование vector или string не только сделает проще вашу жизнь, но и позволит написать более безопасную и масштабируемую программу.
78. Используйте vector (и string::c_str) для обмена данными с API на других языках
vector и string::c_str служат шлюзом для сообщения с API на других языках. Однако не полагайтесь на то, что итераторы являются указателями; для получения адреса элемента, на который ссылается vector<T>::iterator iter, используйте выражение &*iter.
79. Храните в контейнерах только значения или интеллектуальные указатели
Храните в контейнерах объекты-значения. Контейнеры полагают, что их содержимое имеет тип значения, включая непосредственно хранящиеся значения, интеллектуальные указатели и итераторы.
80. Предпочитайте push_back другим способам расширения последовательности
Используйте push_back везде, где это возможно. Если для вас не важна позиция вставки нового объекта, лучше всего использовать для добавления элемента в последовательность функцию push_back. Все прочие средства могут оказаться как гораздо менее быстрыми, так и менее понятными.
81. Предпочитайте операции с диапазонами операциям с отдельными элементами
При добавлении элементов в контейнер лучше использовать операции с диапазонами (т.е. функцию insert, которая получает пару итераторов), а не последовательность вызовов функции для вставки одного элемента. Вызов функции для диапазона обычно проще написать, легче читать, и он более эффективен, чем явный цикл (см. также рекомендацию 84).