C++. Бархатный путь. Часть 2 - ctorИнициализатор
ОГЛАВЛЕНИЕ
ctorИнициализатор
Как известно, объявление данного-члена класса не допускает инициализации, а для того, чтобы константный член класса в процессе создания объекта всё же мог получить требуемое значение, в C++ используется так называемый ctorИнициализатор (именно так называется эта конструкция в справочном руководстве по C++ Б.Строуструппа). Мы не будем гадать, в чём заключается смысл этого названия, а лучше заново воспроизведем несколько форм Бэкуса-Наура.
ОпределениеФункции ::= [СписокСпецификаторовОбъявления]
Описатель
[ctorИнициализатор]
ТелоФункции
ctorИнициализатор ::= : СписокИнициализаторовЧленовКласса
СписокИнициализаторовЧленовКласса ::= ИнициализаторЧленаКласса
[, СписокИнициализаторовЧленовКласса]
ИнициализаторЧленаКласса ::= ПолноеИмяКласса([СписокВыражений])
::= Идентификатор([СписокВыражений])
ПолноеИмяКласса ::= КвалифицированноеИмяКласса
::= :: КвалифицированноеИмяКласса
Для исследования свойств ctorИнициализатора, подвергнем нашу программу очередной модификации. Мы закомментируем все ранее построенные объявления и определения конструкторов и те из операторов определения объектов класса ComplexType, которые содержали значения, определяющие начальные значения данных-членов. И сразу же начинаем определение новых вариантов конструкторов.
ComplexType():x(1)
{
cout << "Здесь ComplexType():x(" << x << ")" << endl;
};
Перед нами конструктор с ctorИнициализатором. Эта конструкция позволяет решать проблемы начальной инициализации константных данных-членов. При работе с данными-членами класса транслятор рассматривает операцию присвоения как изменение начального значения члена. Инициализатор же отвечает непосредственно за установку этого САМОГО ПЕРВОГО значения.
В список инициализаторов разрешено включать все нестатические членам класса (объявленным без спецификатора static), но не более одного раза. Так что следующий вариант конструктора будет восприниматься как ошибочный:
ComplexType():x(1), x(2) // Ошибка.
{
:::::
}
Нетерминальный символ ПолноеИмяКласса определяет синтаксис инициализации нестатических объектов так называемого базового класса (об этом позже). В этом случае список выражений как раз обеспечивает инициализацию членов базового класса.
Добавим в объявление нашего класса объявление массива. Инициализация массива-члена класса при определении объекта не вызывает особых проблем (здесь следует вспомнить раздел, посвящённый массивам-параметрам). Однако в C++ отсутствует возможность инициализации нестатического константного массива-члена класса. Так что можно не стараться выписывать подобные объявления:
const int xx[2]; // Бессмысленное объявление.
всё равно массив xx[2] невозможно проинициализировать. Все варианты инициализации константного нестатического массива будут отвергнуты.
ComplexType():xx(1,2) {/*…*/};
ComplexType():xx({1,2}) {/*…*/};
ComplexType():xx[0](1), xx[1](2) {/*…*/};
Согласно БНФ, в состав инициализатора могут входить только имена или квалифицированные имена. Для обозначения элемента массива этого недостаточно. Как минимум, здесь требуется выражение индексации, которое указывало бы номер элемента массива.
И всё же выход из такой ситуации существует. Можно объявить константный указатель на константу, которому в выражении инициализации можно присвоить имя ранее определённого массива:
:::::
const int DefVal[2] = {1,2};
class ComplexType
{
:::::
const int const * px;
/* Объявили константный указатель на константу. */
:::::
ComplexType():px(DefVal) {/*…*/};
:::::
};
Окольными путями мы всё же достигаем желаемого результата. Константный указатель на константу контролирует константный массив.
Услугами инициализатора могут пользоваться не только константные члены, а инициализирующие значения можно строить на основе самых разных выражений. Главное, чтобы используемые в этих выражениях имена располагались в соответствующих областях видимости:
ComplexType():px(DefVal),
x(px[0]), // Транслятор уже знает, что такое px.
CTcharVal(32),
real(100),
imag(real/25) // И здесь тоже всё в порядке.
{
// Здесь располагается тело конструктора.
:::::
}