Шрифт:
Этот синтаксис не так страшен, как может показаться. Если слово const появляется слева от звездочки, константным является то, на что указывает указатель; если справа, то сам указатель является константным. Наконец, если же слово const появляется с обеих сторон, то константно и то, и другое.
Когда то, на что указывается, – константа, некоторые программисты ставят const перед идентификатором типа. Другие – после идентификатора типа, но перед звездочкой. Семантической разницы здесь нет, поэтому следующие функции принимают параметр одного и того же типа:
Поскольку в реальном коде встречаются обе формы, следует привыкать и к той, и к другой.
Итераторы STL смоделированы на основе указателей, поэтому iterator ведет себя почти как указатель T*. Объявление const-итератора подобно объявлению const-указателя (то есть записи T* const): итератор не может начать указывать на что-то другое, но то, на что он указывает, может быть модифицировано. Если вы хотите иметь итератор, который указывал бы на нечто, что запрещено модифицировать (то есть STL-аналог указателя const T*), то вам понадобится константный итератор:
Некоторые из наиболее интересных применений const связаны с объявлениями функций. В этом случае const может относиться к возвращаемому функцией значению, к отдельным параметрам, а для функций-членов – еще и к функции в целом.
Если указать в объявлении функции, что она возвращает константное значение, то можно уменьшить количество ошибок в клиентских программах, не снижая уровня безопасности и эффективности. Например, рассмотрим объявление функции operator* для рациональных чисел, введенное в правиле 24:
Многие программисты удивятся, впервые увидев такое объявление. Почему результат функции operator* должен быть константным объектом? Потому что в противном случае пользователь получил бы возможность делать вещи, которые иначе как надругательством над здравым смыслом не назовешь:
Я не знаю, с какой стати программисту пришло бы в голову присваивать значение произведению двух чисел, но могу точно сказать, что иногда такое может случиться по недосмотру. Достаточно простой опечатки (при условии, что тип может быть преобразован к bool):
Такой код был бы совершенно некорректным, если бы a и b имели встроенный тип. Одним из критериев качества пользовательских типов является совместимость со встроенными (см. также правило 18), а возможность присваивания значения результату произведения двух объектов представляется мне весьма далекой от совместимости. Если же объявить, что operator* возвращает константное значение, то такая ситуация станет невозможной. Вот почему Так Следует Поступать.
В отношении аргументов с модификатором const трудно сказать что-то новое; они ведут себя как локальные константные const-объекты. Всюду, где возможно, добавляйте этот модификатор. Если модифицировать аргумент или локальный объект нет необходимости, объявите его как const. Вам всего-то придется набрать шесть символов, зато это предотвратит досадные ошибки типа «хотел напечатать ==, а нечаянно напечатал =» (к чему это приводит, мы только что видели).
Константные функции-члены