Страница 34 из 48 Объявление и определение операторных функций При объявлении и определении операторных функций (в том числе и operator=() ), используется синтаксическая конструкция, обозначаемая в терминах формальной грамматики нетерминальным символом ИмяФункцииОперации. Несколько форм Бэкуса-Наура позволяют однозначно определить это понятие: Имя ::= ИмяФункцииОперации ::= ***** ИмяФункцииОперации ::= operator СимволОперации СимволОперации ::= +|-|*|?|%|^|&|~|!|,|=|<|>|<=|>=|++|--|<<|>>|==|!=|&&| |||+=|-=|*=|<<=|>>=|[]|()|->|->*|new|delete| Как следует из приведённых БНФ, большинство символов операций языка C++ могут участвовать в создании так называемых имён функций операций или операторных функций. То есть на основе этих символов можно объявлять операторные функции, сокращённая форма вызова которых позволяет создавать видимость применения операций к объектам производных типов. C++ не накладывает никаких ограничений на семантику этих самых операторных функций. Наша операторная функция operator=() могла бы вообще не заниматься присвоением значений данных-членов. Она могла бы не возвращать никаких значений. Само собой, что тогда выражение вызова этой функции не могло бы быть коммутативным. А единственный параметр можно было бы передавать по значению. Но тогда всякий раз при вызове функции неизбежно должен был бы вызываться конструктор копирования, который бы создавал в области активации функции копию объекта, которую впоследствии должен был бы разрушать деструктор. Операторная функция operator=(), как и любая другая функция, может быть перегружена. Например, объявление параметра типа int, позволило бы присваивать комплексным числам целочисленные значения. Здесь нет пределов совершенствования. В принципе, механизм операторных функций регламентирует лишь внешний вид заголовка функции (его "операторное" имя, количество параметров, в ряде случаев - возвращаемое значение). Информация о заголовке принципиальна, поскольку от этого зависит форма сокращённого вызова операторной функции. Ещё несколько замечаний по поводу спецификации возвращаемого значения операторной функции. Операторная функция operator=() может вообще не возвращать никаких значений. Сокращённая форма вызова ctVal2 = ctVal1; с точки зрения транслятора абсолютно корректна и полностью соответствует следующим прототипам: void ComplexType::operator = (const ComplexType& ctKey); void ComplexType::operator = (ComplexType& ctKey); void ComplexType::operator = (ComplexType ctKey); Правда, в таком случае ни о какой коммутативности, "безопасности" и эффективности вновь определяемой операторной функции нет и быть не может. С другой стороны, уже существующий вариант нашей операторной функции также может быть оптимизирован. Функция может возвращать не ОБЪЕКТ (ЗНАЧЕНИЕ), а ССЫЛКУ на объект. В этом случае при возвращении значения не будет создано временного объекта. Также не будет вызываться деструктор для его уничтожения. Модификация операторной функции operator=() минимальна - всего лишь дополнительная ptrОперация & в спецификации возвращаемого значения (мы приводим здесь только прототип новой версии функции): ComplexType& operator = (const ComplexType &); Всё остальное транслятор исправит самостоятельно, так что никаких дополнительных модификаций в тексте программы производить не придётся. Эта функция будет эффективней, правда, семантика выражения её вызова будет отличаться от семантики соответствующего выражения присвоения с базовыми типами. В первом случае результатом выполнения выражения оказывается присваиваемое значение, во втором - ссылка на объект. Следующий пример является подтверждением того факта, что при объявлении операторных функций полностью отсутствуют чёткие правила. Это подтверждает следующий пример, посвящённый объявлению и вызову различных вариантов операторных функций operator(): ::::: class ComplexType { ::::: }; ::::: class rrr // Объявляется новый класс. { public: ComplexType* pComplexVal; // Собственные версии конструкторов и деструкторов. rrr () { pComplexVal = new ComplexType; // Порождение собственного экземпляра объекта ComplexType. } ~rrr () { if (pComplexVal) = delete pComplexVal; } // Наконец, встроенная операторная функция. ComplexType* operator -> () { cout << "This is operator -> ()..." << endl; return pComplexVal; } }; ::::: // А это уже собственно фрагмент программы… rrr rrrVal; // Определяем объект - представитель класса rrr. cout << rrrVal ->real << " real." << endl; ::::: Сокращённая форма вызова операторной функции operator->() имеет вид rrrVal->real и интерпретируется транслятором как (rrrVal.operator->())->real, о чём и свидетельствует оператор, содержащий полную форму вызова этой операторной функции. ::::: cout << (rrrVal.operator->())->imag << " imag." << endl; ::::: |