C++. Бархатный путь. Часть 2 - Конструкторы. Основные свойства
ОГЛАВЛЕНИЕ
Конструкторы. Основные свойства
Сначала несколько форм Бэкуса-Наура.
Объявление ::= ОбъявлениеФункции
::= ОпределениеФункции
::= *****
ОбъявлениеФункции ::=
[СписокСпецификаторовОбъявления]
Описатель
[СпецификацияИсключения];
ОпределениеФункции ::=
[СписокСпецификаторовОбъявления]
Описатель
[ctorИнициализатор]
[СпецификацияИсключения]
ТелоФункции
Описатель ::= Описатель ([СписокОбъявленийПараметров])
::= dИмя
dИмя ::= ИмяКласса
Используя это множество БНФ, можно строить объявления весьма странного вида:
ОбъявлениеФункции ::=
Описатель; ::=
Описатель (); ::=
dИмя (); ::=
ComplexType ();
Объявление… без спецификатора объявления.
ОпределениеФункции ::=
Описатель ТелоФункции ::=
Описатель () {} ::=
dИмя () {} ::=
ComplexType () {}
А это определение. Оно построено в соответствии с правилами построения функций. Не важно, что у него в теле нет ни одного оператора! Важно, что у него нет спецификатора объявления.
Именно так и выглядит конструктор, альтернативный тому, который строится транслятором без участия программиста. Множество операторов (возможно пустое), оформленное в виде блока, с заголовком специального вида (ни слова о возвращаемых значениях) - нечто подобное функции-члену. Подобным образом организованная и весьма напоминающая своим синтаксисом обыкновенную функцию последовательность операторов и отвечает за создание объектов данного класса.
Отметим одно очень важное обстоятельство. Имя конструктора всегда совпадает с именем класса, членом которого является объявляемый конструктор. Ни одна функция-член класса не может называться именем класса. Ни одна функция-член класса не может быть объявлена и определена без спецификатора объявления. Характерное имя и отсутствие спецификации объявления отличает конструктор от функций-членов класса.
Отсутствие спецификаторов объявления означает, что конструктор не имеет абсолютно никакого отношения к вызову и возвращению значений. Конструктор не является функцией.
Так что объявления функций-членов класса ComplexType
void ComplexType();
ComplexType ComplexType();
не являются объявлениями конструктора. Для транслятора это всего лишь некорректные объявления функций-членов с пустыми списками параметров. Подобные объявления в классе ComplexType воспринимаются транслятором как ошибки.
А вот построенное нами объявление действительно является объявлением конструктора:
ComplexType();
И наше определение действительно является определением конструктора:
ComplexType(){}
Это ничего, что конструктор такой простой, зато он от начала и до конца правильный!
Как известно, в классе может быть не объявлено ни одного конструктора. В таком случае транслятор без участия программиста самостоятельно строит стандартный конструктор. Не существует классов без конструкторов, хотя классы с автоматически создаваемыми конструкторами, как ни странно, называются классами без конструкторов.
В классе может быть объявлено (и определено) несколько конструкторов. Их объявления должны различаться списками параметров. Такие конструкторы по аналогии с функциями называются перегруженными (или совместно используемыми). Транслятор различает перегруженные конструкторы по спискам параметров. В этом смысле конструктор не отличается от обычной функции-члена класса:
ComplexType(double rePar, double imPar); /* Объявление… */
ComplexType(double rePar, double imPar){/*…*/} /*Определение…*/
И ещё один вариант конструктора для класса ComplexType - на этот раз с одним параметром (его помощью, например, можно задавать значение мнимой части):
ComplexType(double imPar); /* Объявление… */
ComplexType(double imPar){/*…*/} /*Определение…*/
Здесь мы сознательно опять оставили пустыми тела конструкторов. Необходимо сначала выяснить, какие операторы могут, а какие не могут располагаться в конструкторе.
Отсутствие спецификации возвращаемого значения не означает запрета на использование оператора return в теле конструктора. В конце концов, это всего лишь оператор перехода. Но использование этого оператора в сочетании с выражением, задающим возвращаемое значение, например,
return NULL;
либо
return MyVal;
либо
return 125;
и т.д., недопустимо. Возвращаемое значение специфицируется по типу, а как раз про тип возвращаемого конструктором значения в объявлении конструктора ничего и не сказано. Поэтому то, что обычно называется выражением явного вызова конструктора, вызовом, по сути, не является.