Шрифт:
Интервал:
Закладка:
Такие размышления приводят к выводу, что представление типа Date, за исключением открытых функций-членов, должно быть недоступным для пользователей. Итак, получаем первое сокращение.
// простой типа Date (управление доступом)
class Date {
int y, m, d; // год, месяц, день
public:
Date(int y, int m, int d); // проверка и инициализация даты
void add_day(int n); // увеличение объекта типа Date на n дней
int month() { return m; }
int day() { return d; }
int year() { return y; }
};
Этот класс можно использовать следующим образом:
Date birthday(1970, 12, 30); // OK
birthday.m = 14; // ошибка: Date::m — закрытый член
cout << birthday.month() << endl; // доступ к переменной m

В качестве альтернативы можно проверять корректность объекта при каждой попытке его использования или просто надеяться на то, что никто никогда не создаст ни одного некорректного значения. Опыт показывает, что такие надежды могут привести к “очень хорошим” программам. Однако создание таких программ, которые иногда выдают ошибочные результаты, а порой вообще приводят к аварийному отказу, не принесет вам профессионального признания. Мы предпочитаем писать программы, корректность которых можно продемонстрировать.

9.4.4. Определение функций-членов
До сих пор мы смотрели на класс Date с точки зрения разработчика интерфейса и пользователя. Однако рано или поздно нам придется реализовать его функции-члены. Во-первых, выделим подмножество класса Date, чтобы согласовать его с общепринятым стилем организации открытого интерфейса.
// простой класс Date (детали реализации будут рассмотрены позднее)
class Date {
public:
Date(int y, int m, int d); // проверка и инициализация даты
void add_day(int n); // увеличивает объект класса Date на n дней
int month();
// ...
private:
int y, m, d; // лет, месяцев, дней
};
Открытый интерфейс разрабатывают в первую очередь, поскольку именно он интересует большинство людей. В принципе пользователю не обязательно знать детали реализации. На самом же деле люди, как правило, любопытны и хотят знать, насколько разумна реализация класса и какие приемы использовал ее автор, чтобы научиться у него чему-нибудь. И все же, если реализацию класса создавали не мы, то большую часть времени будем работать с его открытым интерфейсом. Компилятору безразличен порядок следования членов класса; он обрабатывает объявления в любом порядке, в котором мы их укажем.
Определяя члены за пределами класса, мы должны указать, какому классу они принадлежат. Для этого используется обозначение имя_класса::имя_члена.
Date::Date(int yy, int mm, int dd)// конструктор
:y(yy), m(mm), d(dd) // примечание: инициализация члена
{
}
void Date::add_day(int n)
{
// ...
}
int month() // Ой: мы забыли про класс Date::
{
return m; // не функция-член, к переменной m доступа нет
}
Обозначение :y(yy), m(mm), d(dd) указывает на то, как инициализируются члены. Оно называется списком инициализации. Мы могли бы написать эквивалентный фрагмент кода.
Date::Date(int yy, int mm, int dd) // конструктор
{
y = yy;
m = mm;
d = dd;
}
Однако сначала нам следовало бы инициализировать члены их значениями, заданными по умолчанию, и лишь потом присваивать им новые значения. Кроме того, в этом случае не исключена возможность того, что мы случайно используем член класса до его инициализации. Обозначение :y(yy), m(mm), d(dd) точнее отражает наши намерения. Разница между этими фрагментами точно такая же, как между двумя примерами, приведенными ниже. Рассмотрим первый из них.
int x; // сначала определяем переменную x
// ...
x = 2; // потом присваиваем ей значение
Второй пример выглядит так:
int x = 2; // определяем и немедленно инициализируем двойкой
Для полноты картины укажем еще один способ инициализации с помощью синтаксической конструкции, напоминающей аргументы функции в скобках.
int x(2); // инициализируем двойкой
Date sunday(2009,8,29); // инициализируем объект Sunday
// триадой (2009,8,29)
Функцию-член класса можно также определить в определении класса.
// простой класс Date (детали реализации будут рассмотрены позднее)
class Date {
public:
Date(int yy, int mm, int dd)
:y(yy), m(mm), d(dd)
{
// ...
}
void add_day(int n)
{
// ...
}
int month() { return m; }
// ...
private:
int y, m, d; // год, месяц, день
};
Во-первых, отметим, что теперь объявление класса стало больше и запутаннее. В данном примере код конструктора и функции add_day() могут содержать десятки строк. Это в несколько раз увеличивает размер объявления класса и затрудняет поиск интерфейса среди деталей реализации. Итак, мы не рекомендуем определять большие функции в объявлении класса. Тем не менее посмотрите на определение функции month(). Оно проще и короче,