Страница 10 из 48
Условное выражение на основе выражения явного преобразования
В C++ можно построить условное выражение на основе выражения явного преобразования к одному из основных типов. Основные типы имеют простую структуру, а потому значение такого выражения определить очень просто: if (char(charVal)) {/*…*/} if (float(5)) {/*…*/} if ((int)3.14){/*…*/} if (double (0)){/*…*/}
Включение в условия условных операторов выражений, вычисление значений которых приводит к передаче управления конструкторам, требует дополнительных усилий со стороны прораммиста. У порождаемых конструкторами объектов сложная структура и неизвестные транслятору способы определения значений, представляемых такими объектами. Кроме того, определённый в языке набор операций приспособен исключительно для работы со значениями основных типов. Транслятор не имеет абсолютно никакого представления о том, каким образом следует, например, сравнивать значения того же самого ComplexType. Однако, C++ располагает специальными средствами, которые позволяют создавать иллюзию условных выражений с объектами-операндами производных типов. Чуть позже мы рассмотрим так называемые операторные функции (или перегруженные операции), с помощью которых можно будет всё-таки сформулировать условия, подобные тем, которые формулируются относительно значений основных типов: if (ComplexType()){/*…*/} if (ComplexType() > 10 && ComplexType() <= 25 ){/*…*/}
Правда, в данном контексте за символами операций сравнения и даже за выражением "явного вызова конструктора" скрываются так называемые сокращённые формы вызова операторных функций, а не обычные операции C++. А какое условие можно сформулировать в терминах операций, пригодных для работы исключительно со значениями основных типов по поводу значения безымянного объекта производного типа, который, к тому же и погибает сразу же после своего рождения? В C++ невозможно сформулировать условие относительно сложного объекта "в целом", используя при этом стандартный набор операций, но легко можно определить значения данных-членов этого объекта. Для этого используется операция выбора компонента: if (ComplexType().real && !ComplexType().imag){/*…*/} Вот мы и узнали кое-что о свойствах объекта. Правда, объектов в условии целых два. У первого безымянного объекта мы поинтересовались значением данного-члена real, после чего он благополучно отошёл "в мир иной", у второго объекта выяснили значение данного-члена imag. Выражения вызова функций типа void так же недопустимы в контексте условия, поскольку функции void "возвращают" пустые значения. Например, void MyProc(); ::::: void MyProc() {/*…*/} ::::: if (MyProc()) {/*…*/} /* Здесь ошибка */ for ( ; MyProc(); ) {/*…*/} /* Здесь ошибка */ if (ComplexType()){/*…*/} /* Это тоже ошибка */
Выражение явного преобразования типа можно расположить справа от символа операции присвоения в операторе присвоения. ComplexType MyVal = ComplexType (); ComplexType MyVal = ComplexType (25); ComplexType MyVal = (ComplexType) 25;
И опять перед нами так называемый явный вызов конструктора. Но, как сказано в справочном руководстве по C++, "явный вызов конструктора означает не то же самое, что использование того же синтаксиса для обычной функции-члена". Конструктор вызывается не для объекта класса, как другие функции-члены, а для области памяти. Для её преобразования ("превращения") в объект класса. На самом деле, здесь конструктор вызывается дважды. В первый раз при создании переменной MyVal, второй - в ходе выполнения операции явного преобразования значения, возможно, что пустого. При этом создаётся временный безымянный объект, значения данных-членов которого присваиваются переменной MyVal. Нам ещё предстоит выяснить, как работает операция присвоения на множестве производных типов, в частности, в сочетании с выражением явного преобразования типа, которое приводит к вызову конструктора. И если можно ещё как-то представить пустое значение, которое используется для начальной инициализации данных-членов вновь создаваемого объекта, то присвоение пустого значения леводопустимому выражению в принципе невозможно. Поэтому выражение вызова функции с void спецификатором в операторе присвоения недопустимо: int MyVal = MyProc(); /* Ошибка */ int MyVal = (void)MyProc(); /* Ошибка */
И ещё одно сравнение между конструктором и void-процедурой. Поскольку тип void - это всё же тип, мы можем объявить указатель на void-процедуру. void MyFunction (void); ::::: void (*MyFunctionPointer) (void);
Указатель на функцию можно настроить на адрес конкретной функции. Для этого существует операция взятия адреса: MyFunctionPointer = MyFunction; /* Можно так. */ MyFunctionPointer = &MyFunction; /* А можно и так. */
С конструктором всё по-другому. Мы можем определить адрес создаваемого конструктором объекта. Всё то же выражение явного преобразования типа обеспечивает обращение к конструктору, который создаёт в памяти безымянный объект, чей адрес и определяется операцией взятия адреса: if (&ComplexType()) {/*…*/} Но вот объявить указатель на конструктор и определить адрес конструктора невозможно. Объявление указателя на функцию требует стандартной спецификации типа функции. Операция взятия адреса возвращает значение определённого типа. Конструктор же не обладает стандартной спецификацией, а потому невозможно определить для него указатель и определить соответствующее значение. |