Литмир - Электронная Библиотека
A
A

  return dd;

}

Здесь использовано ключевое слово

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<Date> birthdays(10);

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

vector<Date> 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
, и выдаст сообщение об ошибке.

 

Программирование. Принципы и практика использования C++ Исправленное издание - _001.png
 Решить эту проблему можно, разделив операции над классом, на модифицирующие и немодифицирующие. Это не только помогает понять суть класса, но и имеет очень важное практическое значение: операции, которые не модифицируют объект, можно применять к константным объектам. Рассмотрим пример.

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

 

Программирование. Принципы и практика использования C++ Исправленное издание - _001.png
 Разрабатывая минимальный (хотя и полный) интерфейс, мы вынуждены оставлять за бортом много полезных операций. Функцию, которая могла бы быть просто, элегантно и эффективно реализована как самостоятельная функция (т.е. не функция-член), следует реализовать за пределами класса. Таким образом, функция не сможет повредить данные, хранящиеся в объекте класса. Предотвращение доступа к данным является важным фактором, поскольку обычные методы поиска ошибок “вращаются вокруг типичных подозрительных мест”; иначе говоря, если с классом что-то не так, мы в первую очередь проверяем функции, имеющие прямой доступ к его представлению: одна из них обязательно является причиной ошибки. Если таких функций десяток, нам будет намного проще работать, чем если их будет пятьдесят.

134
{"b":"847443","o":1}