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
по отношению к члену класса гарантирует, что в программе существует только одна копия его значения, а не по одной копии на каждый объект данного класса.
9.7.2. Копирование
Мы всегда должны создавать объекты, иначе говоря, всегда предусматривать инициализацию и конструкторы. Вероятно, это самые важные члены класса: для того чтобы написать их, необходимо решить, как инициализировать объект и что значит корректность его значений (т.е. определить инвариант). Уже даже размышления об инициализации помогут вам избежать ошибок.
Затем необходимо решить, можно ли копировать объекты и как это делать? Для класса Date
или перечисления Month
ответ очевиден: копирование необходимо, и его смысл тривиален: просто копируются все члены класса. Фактически это предусмотрено по умолчанию. Если не указано ничего другого, компьютер сделает именно это. Например, если перечисление Date
используется для инициализации или стоит в правой части оператора присваивания, то все его члены будут скопированы.