Использование для обеспечения инициализации объекта класса функций вроде set_date() (установить дату) неэлегантно и чревато ошибками. Поскольку нигде не утверждается, что обект должен быть инициализирован, то программист может забыть это сделать, или (что приводит, как правило, к столь же рарушительным последствиям) сделать это дважды. Есть более хроший подход: дать возможность программисту описать функцию, явно предназначенную для инициализации объектов. Поскольку такая функция конструирует значения данного типа, она назывется конструктором. Конструктор распознается по тому, что имеет то же имя, что и сам класс. Например:
class date (* // ... date(int, int, int); *);
Когда класс имеет конструктор, все объекты этого класса будут инициализироваться. Если для конструктора нужны парметры, они должны даваться:
date today = date(23,6,1983); date xmas(25,12,0); // сокращенная форма // (xmas – рождество) date my_burthday; // недопустимо,опущена инициализация
Часто бывает хорошо обеспечить несколько способов иницализации объекта класса. Это можно сделать, задав несколько конструкторов. Например:
class date (* int month, day, year; public: // ... date(int, int, int); // день месяц год date(char*); // дата в строковом представлении date(int); // день, месяц и год сегодняшние date(); // дата по умолчанию: сегодня *);
Конструкторы подчиняются тем же правилам относительно типов параметров, что и перегруженные функции (#4.6.7). Если конструкторы существенно различаются по типам своих парамеров, то компилятор при каждом использовании может выбрать правильный:
date today(4); date july4(«Июль 4, 1983»); date guy(«5 Ноя»); date now; // инициализируется по умолчанию
Заметьте, что функции члены могут быть перегружены без явного использования ключевого слова overload. Поскольку поный список функций членов находится в описании класса и как правило короткий, то нет никакой серьезной причины требовать использования слова overload для предотвращения случайного повторного использования имени.
Размножение конструкторов в примере с date типично. При разработке класса всегда есть соблазн обеспечить «все», покольку кажется проще обеспечить какое-нибудь средство просто на случай, что оно кому-то понадобится или потому, что оно изящно выглядит, чем решить, что же нужно на самом деле. Поледнее требует больших размышлений, но обычно приводит к программам, которые меньше по размеру и более понятны. Один из способов сократить число родственных функций – использвать параметры со значением по умолчанию, пример. В случае date для каждого параметра можно задать значение по умолчнию, интерпретируемое как «по умолчанию принимать: today» (сегодня).
class date (* int month, day, year; public: // ... date(int d =0, int m =0, int y =0); date(char*); // дата в строковом представлении *);
date::date(int d, int m, int y) (* day = d ? d : today.day; month = m ? m : today.month; year = y ? y : today.year; // проверка, что дата допустимая // ... *)
Когда используется значение параметра, указывающее «брать по умолчанию», выбранное значение должно лежать вне множества возможных значений параметра. Для дня day и месяца mounth ясно, что это так, но для года year выбор нуля неочвиден. К счастью, в европейском календаре нет нулевого года . Сразу после 1 г. до н.э. (year==-1) идет 1 г. н.э. (year==1), но для реальной программы это может оказаться слишком тонко.
Объект класса без конструкторов можно инициализировать путем присваивания ему другого объекта этого класса. Это моно делать и тогда, когда конструкторы описаны. Например:
date d = today; // инициализация посредством присваивания
По существу, имеется конструктор по умолчанию, опредленный как побитовая копия объекта того же класса. Если для класса X такой конструктор по умолчанию нежелателен, его моно переопределить конструктором с именем X(X amp;). Это будет осуждаться в #6.6.