Читаем Программирование полностью

Date dx2(Date::mar, 4, 1998);  // ошибка: 2-й аргумент не имеет

                               // тип Month

Date dx3(1998, Date::mar, 30); // OK

Этот код решает много проблем. Обратите внимание на квалификатор Date перечисления mar: Date::mar. Тем самым мы указываем, что это перечисление mar из класса Date. Это не эквивалентно обозначению Date.mar, поскольку Date — это не объект, а тип, а mar — не член класса, а символическая константа из перечисления, объявленного в классе. Обозначение :: используется после имени класса (или пространства имен; см. раздел 8.7), а . (точка) — после имени объекта.

  Когда есть выбор, ошибки следует выявлять на этапе компиляции, а не на этапе выполнения программы. Мы предпочитаем, чтобы ошибки вылавливал компилятор, а не искать, в каком месте кода возникла ошибка. Кроме того, для выявления ошибок на этапе компиляции не требуется писать и выполнять специальный код для проверки.

А нельзя ли подобным образом выявить путаницу между днем месяца и годом? Можно, но решение этой проблемы будет не таким элегантным, как для типа Month; помимо всего прочего, возможно, что мы имели в виду именно четвертый год. Даже если мы ограничимся современной эпохой, в перечисление придется включать слишком много лет.

Вероятно, было бы лучше всего (не вникая в предназначение класса Date) написать следующий код:

class Year { // год в диапазоне [min:max)

  static const int min = 1800;

  static const int max = 2200;

public:

  class Invalid { };

  Year(int x) : y(x) { if (x

  int year() { return y; }

private:

  int y;

};

class Date {

public:

  enum Month {

    jan=1, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec

  };

  Date(Year y, Month m, int d); // проверка даты и инициализация

  // ...

private:

  Year y;

  Month m;

  int d; // день

};

Теперь получаем фрагмент кода.

Date dx1(Year(1998),4,3);          // ошибка: 2-й аргумент — не Month

Date dx2(Year(1998),4,Date::mar);  // ошибка: 2-й аргумент — не Month

Date dx2(4, Date::mar,Year(1998)); // ошибка: 1-й аргумент — не Year

Date dx2(Date::mar,4,Year(1998));  // ошибка: 2-й аргумент — не Month

Date dx3(Year(1998),Date::mar,30); // OK

Следующая фатальная и неожиданная ошибка выявится только на этапе выполнения программы.

Date dx2(Year(4),Date::mar,1998); // ошибка на этапе выполнения:

                                  // Year::Invalid

Стоило ли выполнять дополнительную работу и вводить обозначения для лет? Естественно, это зависит от того, какие задачи вы собираетесь решать с помощью типа Date, но в данном случае мы сомневаемся в этом и не хотели бы создавать отдельный класс Year.

  Когда мы программируем, то всегда устанавливаем критерии качества для данного приложения. Как правило, мы не можем позволить себе роскошь очень долго искать идеальное решение, если уже нашли достаточно хорошее. Втягиваясь в поиски наилучшего решения, мы настолько запутаем программу, что она станет хуже, чем первоначальный вариант. Как сказал Вольтер: “Лучшее — враг хорошего”.

Обратите внимание на слова static const в определениях переменных min и max. Они позволяют нам определить символические константы для целых типов в классах. Использование модификатора static по отношению к члену класса гарантирует, что в программе существует только одна копия его значения, а не по одной копии на каждый объект данного класса.

<p id="AutBody_Root169"><strong>9.7.2. Копирование</strong></p>

Мы всегда должны создавать объекты, иначе говоря, всегда предусматривать инициализацию и конструкторы. Вероятно, это самые важные члены класса: для того чтобы написать их, необходимо решить, как инициализировать объект и что значит корректность его значений (т.е. определить инвариант). Уже даже размышления об инициализации помогут вам избежать ошибок.

Затем необходимо решить, можно ли копировать объекты и как это делать? Для класса Date или перечисления Month ответ очевиден: копирование необходимо, и его смысл тривиален: просто копируются все члены класса. Фактически это предусмотрено по умолчанию. Если не указано ничего другого, компьютер сделает именно это. Например, если перечисление Date используется для инициализации или стоит в правой части оператора присваивания, то все его члены будут скопированы.

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