напоминает вирусное заболевание — появившись в вашем коде один раз, он приведет к необходимости соответствующего изменения сигнатур функций, которые еще не являются корректными в плане использования
const
. Это как раз не ошибка, а хорошее свойство, существенно увеличивающее мощь модификатора
const
, который еще не так давно был достаточно заброшен, а его возможности не вполне поняты и оценены. Переделка существующего кода для его корректности в плане использования
const
требует усилий, но они стоят того и даже позволяют выявить скрытые ошибки.
Корректное применение
const
дает отличные результаты и повышает эффективность. Чрезвычайно важно правильно и последовательно использовать модификатор
const
в ваших программах. Понимание того, как и где изменяется состояние программы, особенно необходимо, а модификатор
const
по сути документирует непосредственно в коде программы, где именно компилятор может помочь вам в этом. Правильное употребление
const
поможет вам лучше разобраться с вопросами проектирования и сделать ваш код более надежным и безопасным. Если вы выяснили, что некоторую функцию-член невозможно сделать константной, значит, вы более детально разобрались с тем, как, где и почему эта функция модифицирует состояние объекта. Кроме того, вы сможете понять, какие члены-данные объединяют физическую и логическую константность (см. приведенные ниже примеры).
Никогда не прибегайте к преобразованию константного типа в неконстантный, кроме случаев вызова функции, некорректной в плане использования модификатора
const
(не модифицирующей параметр, который тем не менее описан как неконстантный), а также такого редкого случая, как способ замены mutable в старом компиляторе, не поддерживающем эту возможность.
Примеры
Пример. Избегайте
const
в объявлениях функций, принимающих параметры по значению. Два следующих объявления абсолютно эквивалентны:
void Fun(int x);
void Fun(const int x); // Объявление той же самой функции:
// const здесь игнорируется
Во втором объявлении модификатор
const
избыточен. Мы рекомендуем объявлять функции без таких высокоуровневых модификаторов
const
, чтобы тот, кто читает ваши заголовочные файлы, не был дезориентирован. Однако использование такого модификатора имеет значение в определении функции и его применение может быть оправдано с точки зрения обнаружения непреднамеренного изменения переданного параметра:
void Fun(const int x) { // определение функции Fun
// ...
++x; // Ошибка: нельзя изменять константное значение
Макрос — самый неприятный инструмент С и С++, оборотень, скрывающийся под личиной функции, кот, гуляющий сам по себе и не обращающий никакого внимания на границы ваших областей видимости. Берегитесь его!
Обсуждение
Трудно найти язык, достаточно красочный, чтобы выразить все, что хочется сказать о макросах. Но тем не менее приведем несколько цитат.
Макросы по многим причинам — весьма неприятная
вещь, которая может стать попросту опасной. В первую очередь это связано с тем, что макросы — средство замены текста, действующее во время обработки исходного текста препроцессором, т.е. еще до того, как начнется какая-либо проверка синтаксиса и семантики. — [Sutter04] §31
Мне не нравится большинство видов препроцессоров и макросов. Одна из целей С++ — сделать препроцессор С излишним (§4.4, §18), поскольку я считаю его большой ошибкой
— [Stroustrup94] §3.3.1.
Макросы почти никогда не являются необходимыми в С++. Используйте
const
(§5.4) или
enum
(§4.8) для определения явных констант [см. рекомендацию 15],
inline
(§7.1.1) для того, чтобы избежать накладных расходов на вызов функции [но см. рекомендацию 8],
template
(глава 13) для определения семейств функций и типов [см. рекомендации с 64 по 67], и
namespace
(§8.2) для того, чтобы избежать конфликтов имен [см. рекомендации с 57 по 59].— [Stroustrup00] §1.6.1
Первое правило по применению макросов гласит: не используйте их до тех пор, пока у вас не будет другого выхода. Практически любой макрос свидетельствует о несовершенстве языка программирования, программы или программиста.
— [Stroustrup00] §7.8
Основная проблема с макросами С++ заключается в том, что они выглядят гораздо привлекательнее, чем являются таковыми на самом деле. Макросы игнорируют области видимости, игнорируют прочие возможности и правила языка, и заменяют все символы, которые переопределяют при помощи директивы
#define
, до самого конца файла. Применение макросов внешне походит на имя или вызов функции, но не имеет с ними ничего общего. Макросы "негигиеничны", в том смысле, что они могут быть раскрыты неожиданно, причем превратиться в зависимости от контекста их использования в самые разные конструкции. Подстановка текста, выполняемая макросами, делает написание хотя бы в небольшой степени "приличного" макроса смесью искусства и черной магии.
Программисты, которые полагают, что тяжелее всего расшифровать ошибки, связанные с шаблонами, вероятно, просто никогда не имели дела с плохо написанными или неверно использованными макросами. Шаблоны являются частью системы типов С++, и тем самым позволяют компилятору куда лучше справляться с ними, чем с макросами, которые имеют мало общего с языком программирования. Хуже того, в отличие от шаблонов неверные макросы могут быть раскрыты в нечто, что в силу чистой случайности скомпилируется, не имея при этом никакого смысла. И наконец, ошибка в макросе обнаруживается только после того, как макрос раскрывается, а не при его определении.
Даже в тех редких случаях, где применение макросов оправданно (см. подраздел, посвященный исключениям), нельзя даже подумать о том, чтобы написать макрос, который является распространенным словом или аббревиатурой. Для всех макросов как можно скорее применяйте директиву
#undef
, всегда давая им необычные уродливые имена в верхнем регистре, избегая при этом размещения их в заголовочных файлах.
Примеры
Пример. Передача инстанцирования шаблона макросу. Макросы понимают в достаточной мере только круглые и квадратные скобки. В С++, однако, определена новая конструкция с угловыми скобками, используемая в шаблонах. Макросы не могут корректно обработать эту ситуацию, так что вызов