Читаем Программирование. Принципы и практика использования C++ Исправленное издание полностью

Здесь использовано ключевое слово static, чтобы переменная dd создавалась только один раз, а не каждый раз при очередном вызове функции default_date. Инициализация этой переменной происходит при первом вызове функции default_date. С помощью функции default_date легко определить конструктор, заданный по умолчанию, для класса Date.


Date::Date

     :y(default_date.year),

      m(default_date.month),

      d(default_date.day)

}


Обратите внимание на то, что конструктор по умолчанию не обязан проверять значение, заданное по умолчанию; конструктор, создавший объект, вызвавший функцию default_date, уже сделал это. Имея конструктор для класса Date по умолчанию, мы можем создать векторы объектов класса Date.


vector birthdays(10);


Без конструктора по умолчанию мы были бы вынуждены сделать это явно.


vector birthdays(10,default_date);

9.7.4. Константные функции-члены

Некоторые переменные должны изменяться, потому они так и называются, а некоторые — нет; иначе говоря, существуют переменные, которые не изменяются. Обычно их называют константами, и для них используется ключевое слово const. Рассмотрим пример.


void some_function(Date& d, const Date& start_of_term)

{

  int a = d.day;             // OK

  int b = start_of_term.day; // должно бы правильно (почему ?)

  d.add_day(3);                // отлично

  start_of_term.add_day(3);    // ошибка

}


Здесь подразумевается, что переменная 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. Члены и вспомогательные функции

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже