Здесь подразумевается, что переменная d
будет изменяться, а переменная start_of_term
— нет; другими словами, функция some_function()
не может изменить переменную start_of_term
. Откуда компилятору это известно? Дело в том, что мы сообщили ему об этом, объявив переменную start_of_term
константой (const
). Однако почему же с помощью функции day()
можно прочитать переменную day
из объекта start_of_term
? В соответствии с предыдущим определением класса Date
функция start_of_term.day()
считается ошибкой, поскольку компилятор не знает, что функция day()
не изменяет свой объект класса Date
. Об этом в программе нигде не сказано, поэтому компилятор предполагает, что функция day()
может модифицировать свой объект класса Date
, и выдаст сообщение об ошибке.
class Date {
public:
// ...
int day() const; // константный член: не может изменять
// объект
Month month() const; // константный член: не может изменять
// объект
int year() const; // константный член: не может изменять
// объект
void add_day(int n); // неконстантный член: может изменять
// объект
void add_month(int n); // неконстантный член: может изменять
// объект
void add_year(int n); // неконстантный член: может изменять
// объект
private:
int y; // год
Month m;
int d; // день месяца
};
Date d(2000, Date::jan, 20);
const Date cd(2001, Date::feb, 21);
cout << d.day() << " — " << cd.day() << endl; // OK
d.add_day(1); // OK
cd.add_day(1); // ошибка: cd — константа
Ключевое слово const
в объявлении функции-члена стоит сразу после списка аргументов, чтобы обозначить, что эту функцию-член можно вызывать для константных объектов. Как только мы объявили функцию-член константной, компилятор берет с нас обещание не модифицировать объект. Рассмотрим пример.
int Date::day() const
{
++d; // ошибка: попытка изменить объект в константной
// функции - члене
return d;
}
Естественно, как правило, мы не собираемся мошенничать. В основном компилятор обеспечивает защиту от несчастных случаев, что очень полезно при разработке сложных программ.
9.7.5. Члены и вспомогательные функции
Пятьдесят функций для класса Date
! Возможно, вы думаете, что мы шутим. Вовсе нет: несколько лет назад я делал обзор нескольких коммерческих библиотек для работы с календарем и обнаружил в них множество функций вроде next_Sunday()
, next_workday()
и т.д. Пятьдесят — это совсем не невероятное число для класса, разработанного для удобства пользователей, а не для удобства его проектирования, реализации и сопровождения.
Отметим также, что если представление изменяется, то переписать достаточно только функции, которые имеют к ней прямой доступ. Это вторая важная практическая причина для минимизации интерфейса. Разрабатывая класс Date
, мы могли решить, что дату лучше представлять в виде целого числа дней, прошедших с 1 января 1900 года, а не в виде тройки (год, месяц, день). В этом случае нам придется изменить только функции-члены.
Рассмотрим несколько примеров