Некоторые условия не могут быть проверены в процессе компиляции и требуют проверки времени выполнения. В таком случае для обнаружения внутренних программных ошибок следует использовать assert
(см. рекомендацию 68) и следовать советам из остальной части раздела, посвященного обработке ошибок, для прочих ошибок времени выполнения, таких как ошибки, зависящие от данных (см. рекомендации с 69 по 75).
15. Активно используйте const
const
— ваш друг: неизменяемые значения проще понимать, отслеживать и мотивировать, т.е. там, где это целесообразно, лучше использовать константы вместо переменных. Сделайте const
описанием по умолчанию при определении значения — это безопасно, проверяемо во время компиляции (см. рекомендацию 14) и интегрируемо с системой типов С++. Не выполняйте преобразований типов с отбрасыванием const
кроме как при вызове некорректной с точки зрения употребления const
функции (см. рекомендацию 94).
Константы упрощают код, поскольку вам достаточно только один раз взглянуть на ее определение, чтобы знать, чему она равна везде. Рассмотрим такой код:
void Fun(vector<int>& v) {
// ...
const size_t len = v.size();
// ... и еще 30 строк ...
}
Увидев такое определение len
, вы получаете надежную информацию о семантике этой константы в пределах области ее видимости (в предположении, что код не устраняет ее константность, чего он делать не должен, как вы узнаете далее): это информация о длине v в определенной точке программы. Взглянув на одну строку, вы получили всю необходимую информацию для всей области видимости. Если бы переменная len
не была определена как const
, она могла бы быть позже изменена — непосредственно или косвенно.
Заметим, что описание const
не является глубоким. Чтобы понять что имеется в виду, рассмотрим класс С
, который имеет член типа X*
. В объекте С
, который является константой, член X*
также является константой, но сам объект X
, на который он указывает, константой не является (см. [Saks99]).
Логическую константность следует реализовывать с использованием членов, описанных как mutable
. Когда константная функция-член класса оправданно требует модификации переменной-члена (т.е. когда эта переменная не влияет на наблюдаемое состояние объекта, например, если это кэшированные данные), объявите эту переменную-член как mutable
. Заметим, что если все закрытые члены скрыты с использованием идиомы Pimpl (см. рекомендацию 43), описание mutable
не является необходимым ни для кэшированной информации, ни для неизменного указателя на нее.
Модификатор const
напоминает вирусное заболевание — появившись в вашем коде один раз, он приведет к необходимости соответствующего изменения сигнатур функций, которые еще не являются корректными в плане использования const
. Это как раз не ошибка, а хорошее свойство, существенно увеличивающее мощь модификатора const
, который еще не так давно был достаточно заброшен, а его возможности не вполне поняты и оценены. Переделка существующего кода для его корректности в плане использования const
требует усилий, но они стоят того и даже позволяют выявить скрытые ошибки.
Корректное применение const
дает отличные результаты и повышает эффективность. Чрезвычайно важно правильно и последовательно использовать модификатор const
в ваших программах. Понимание того, как и где изменяется состояние программы, особенно необходимо, а модификатор const
по сути документирует непосредственно в коде программы, где именно компилятор может помочь вам в этом. Правильное употребление const
поможет вам лучше разобраться с вопросами проектирования и сделать ваш код более надежным и безопасным. Если вы выяснили, что некоторую функцию-член невозможно сделать константной, значит, вы более детально разобрались с тем, как, где и почему эта функция модифицирует состояние объекта. Кроме того, вы сможете понять, какие члены-данные объединяют физическую и логическую константность (см. приведенные ниже примеры).
Никогда не прибегайте к преобразованию константного типа в неконстантный, кроме случаев вызова функции, некорректной в плане использования модификатора const
(не модифицирующей параметр, который тем не менее описан как неконстантный), а также такого редкого случая, как способ замены mutable в старом компиляторе, не поддерживающем эту возможность.
void Fun(int x);
void Fun(const int x); // Объявление той же самой функции:
// const здесь игнорируется
Во втором объявлении модификатор const
избыточен. Мы рекомендуем объявлять функции без таких высокоуровневых модификаторов const
, чтобы тот, кто читает ваши заголовочные файлы, не был дезориентирован. Однако использование такого модификатора имеет значение в
void Fun(const int x) { // определение функции Fun
// ...
++x; // Ошибка: нельзя изменять константное значение
// ...
}
16. Избегайте макросов
Макрос — самый неприятный инструмент С и С++, оборотень, скрывающийся под личиной функции, кот, гуляющий сам по себе и не обращающий никакого внимания на границы ваших областей видимости. Берегитесь его!