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

В данном случае нет необходимости скрывать данные, на которых основана структура Date, — это предположение будет использовано во всех вариантах этой структуры на протяжении всей главы. Итак, теперь у нас есть объекты типа Date; что с ними можно делать? Все что угодно, в том смысле, что мы можем получить доступ ко всем членам объекта today (и другим объектам типа Date), а также читать и записывать их по своему усмотрению. Загвоздка заключается в том, что все это не совсем удобно. Все, что мы хотим делать с объектами типа Date, можно выразить через чтение и запись их членов. Рассмотрим пример.


// установить текущую дату 24 декабря 2005 года

today.y = 2005;

today.m = 24;

today.d = 12;


Этот способ утомителен и уязвим для ошибок. Вы заметили ошибку? Все, что является утомительным, уязвимо для ошибок! Например, ответьте, имеет ли смысл следующий код?


Date x;

x.y = –3;

x.m = 13;

x.d = 32;


Вероятно нет, и никто не стал бы писать такую чушь — или стал? А что вы скажете о таком коде?


Date y;

y.y = 2000;

y.m = 2;

y.d = 29;


Был ли двухтысячный год високосным? Вы уверены?

Итак, нам нужны вспомогательные функции, которые выполняли бы для нас самые общие операции. В этом случае нам не придется повторять один и тот же код, а также находить и исправлять одни и те же ошибки снова и снова. Практически для любого типа самыми общими операциями являются инициализация и присваивание. Для типа Date к общим операциям относится также увеличение значения объекта Date. Итак, напишем следующий код:


// вспомогательные функции:

void init_day(Date& dd, int y, int m, int d)

{

  // проверяет, является ли (y,m,d) правильной датой

  // если да, то инициализирует объект dd

}


void add_day(Date& dd, int n)

{

  // увеличивает объект dd на n дней

}


Попробуем использовать объект типа Date.


void f

{

  Date today;

  init_day(today, 12, 24, 2005); // Ой! (в 12-м году не было

                                 // 2005-го дня)

  add_day(today,1);

}


  Во-первых, отметим полезность таких “операций” — здесь они реализованы в виде вспомогательных функций. Проверка корректности даты довольно сложна и утомительна, поэтому, если бы мы не написали соответствующую функцию раз и навсегда, то скорее всего пропустили бы этот код и получили неправильную программу. Если мы определяем тип, то всегда хотим выполнять над его объектами какие-то операции. Точное количество и вид этих операций может изменяться. Точный вид реализации этих операций (в виде функций, функций-членов или операторов) также изменяется, но как только мы решили создать собственный тип, мы должны спросить себя: “Какие операции с этим типом можно выполнять?”

9.4.2. Функции-члены и конструкторы

Мы предусмотрели функцию инициализации для типа Date, которая проверяет корректность его объектов. Однако функции проверки приносят мало пользы, если мы не можем их использовать. Например, допустим, что мы определили для типа Date оператор вывода << (раздел 9.8):


void f

{

  Date today;

  // ...

  cout << today << '\n'; // использовать объект today

  // ...

  init_day(today,2008,3,30);

  // ...

  Date tomorrow;

  tomorrow.y = today.y;

  tomorrow.m = today.m;

  tomorrow.d = today.d+1;   // добавляем единицу к объекту today

  cout << tomorrow << '\n'; // используем объект tomorrow

}


Здесь мы “забыли” немедленно инициализировать объект today, и до вызова функции init_day этот объект будет иметь неопределенное значение. Кроме того, “кто-то” решил, что вызывать функцию add_day лишняя потеря времени (или просто не знал о ее существовании), и создал объект tomorrow вручную. Это плохой и даже очень плохой код. Вероятно, в большинстве случае эта программа будет работать, но даже самые небольшие изменения приведут к серьезным ошибкам. Например, отсутствие инициализации объекта типа Date приведет к выводу на экран так называемого “мусора”, а прибавление единицы к члену d вообще представляет собой мину с часовым механизмом: когда объект today окажется последним днем месяца, его увеличение на единицу приведет к появлению неправильной даты. Хуже всего в этом очень плохом коде то, что он не выглядит плохим.

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