Программирование. Принципы и практика использования C++ Исправленное издание, стр. 132
class Year { // год в диапазоне [min:max) static const int min = 1800; static const int max = 2200;public: class Invalid { }; Year(int x) : y(x) { if (x<min || max<=x) throw Invalid(); } 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-й аргумент — не MonthDate dx2(Year(1998),4,Date::mar); // ошибка: 2-й аргумент — не MonthDate dx2(4, Date::mar,Year(1998)); // ошибка: 1-й аргумент — не YearDate dx2(Date::mar,4,Year(1998)); // ошибка: 2-й аргумент — не MonthDate dx3(Year(1998),Date::mar,30); // OKСледующая фатальная и неожиданная ошибка выявится только на этапе выполнения программы.
Date dx2(Year(4),Date::mar,1998); // ошибка на этапе выполнения: // Year::InvalidСтоило ли выполнять дополнительную работу и вводить обозначения для лет? Естественно, это зависит от того, какие задачи вы собираетесь решать с помощью типа Date, но в данном случае мы сомневаемся в этом и не хотели бы создавать отдельный класс
Year
Обратите внимание на слова
static constminmaxstatic9.7.2. Копирование
Мы всегда должны создавать объекты, иначе говоря, всегда предусматривать инициализацию и конструкторы. Вероятно, это самые важные члены класса: для того чтобы написать их, необходимо решить, как инициализировать объект и что значит корректность его значений (т.е. определить инвариант). Уже даже размышления об инициализации помогут вам избежать ошибок.
Затем необходимо решить, можно ли копировать объекты и как это делать? Для класса
DateMonthDateDate holiday(1978, Date::jul, 4); // инициализацияDate d2 = holiday;Date d3 = Date(1978, Date::jul, 4);holiday = Date(1978, Date::dec, 24); // присваиваниеd3 = holiday;Обозначение
Date(1978, Date::dec, 24)cout << Date(1978, Date::dec, 24);В данном случае конструктор класса действует почти как литерал. Это часто удобнее, чем сначала создавать переменную или константу, а затем использовать ее лишь один раз.
А если нас не устраивает копирование по умолчанию? В таком случае мы можем либо определить свое собственное копирование (см. раздел 18.2), либо создать конструктор копирования и закрытый оператор копирующего присваивания (см. раздел 14.2.4).
9.7.3. Конструкторы по умолчанию
Неинициализированные переменные могут быть источником серьезных ошибок. Для того чтобы решить эту проблему, в языке С++ предусмотрено понятие конструктора, гарантирующее, что каждый объект класса будет инициализирован. Например, мы объявили конструктор
Date::Date(int,Month,int)DateDate d1; // ошибка: нет инициализацииDate d2(1998); // ошибка: слишком мало аргументовDate d3(1,2,3,4); // ошибка: слишком много аргументовDate d4(1,"jan",2); // ошибка: неправильный тип аргументаDate d5(1,Date::jan,2); // OK: используется конструктор с тремя // аргументамиDate d6 = d5; // OK: используется копирующий конструкторОбратите внимание на то, что, даже несмотря на то, что мы определили конструктор для класса
DateDate