Функции Операции

<!--StartFragment -->
  • Бинарные и Унарные Операции

  • Предопределенные Значения Операций

  • Операции и Определяемые Пользователем Типы

Можно описывать функции, определяющие значения следующих операций:

+ - * / % ^ & | ~ !
= < > += -= *= /= %= ^= &=
|= << >> >>= <<= == != <= >= &&
|| ++ -- [] () new delete


Последние четыре - это индексирование , вызов функции , выделение свободной памяти и освобождение свободной памяти. Изменить приоритеты перечисленных операций невозможно, как невозможно изменить и синтаксис выражений. Нельзя, например, определить унарную операцию % или бинарную !. Невозможно определить новые лексические символы операций, но в тех случаях, когда множество операций недостаточно, вы можете использовать запись вызова функции. Используйте например, не **, а pow(). Эти ограничения могут показаться драконовскими, но более гибкие правила могут очень легко привести к неоднозначностям. Например, на первый взгляд определение операции **, означающей возведение в степень, может показаться очевидной и простой задачей, но подумайте еще раз. Должна ли ** связываться влево (как в Фортране) или вправо (как в Алголе)? Выражение a**p должно интерпретироваться как a*(*p) или как (a)**(p)?

Имя функции операции есть ключевое слово operator (то есть, операция), за которым следует сама операция, например, operator<<. Функция операция описывается и может вызываться так же, как любая другая функция. Использование операции - это лишь сокращенная запись явного вызова функции операции.

Например:


void f(complex a, complex b)
{
complex c = a + b; // сокращенная запись
complex d = operator+(a,b); // явный вызов
}


При наличии предыдущего описания complex оба инициализатора являются синонимами.

Бинарные и Унарные Операции

Бинарная операция может быть определена или как функция член, получающая один параметр, или как функция друг, получающая два параметра. Таким образом, для любой бинарной операции @ aa@bb может интерпретироваться или как aa.operator@(bb), или как operator@(aa,bb). Если определены обе, то aa@bb является ошибкой. Унарная операция, префиксная или постфиксная, может быть определена или как функция член, не получающая параметров, или как функция друг, получающая один параметр. Таким образом, для любой унарной операции @ aa@ или @aa может интерпретироваться или как aa.operator@(), или как operator@(aa). Если определена и то, и другое, то и aa@ и @aa являются ошибками.

Рассмотрим следующие примеры:


class X {
// друзья

friend X operator-(X); // унарный минус
friend X operator-(X,X); // бинарный минус
friend X operator-(); // ошибка: нет операндов
friend X operator-(X,X,X); // ошибка: тернарная


// члены (с неявным первым параметром: this)

X* operator&(); // унарное & (взятие адреса)
X operator&(X); // бинарное & (операция И)
X operator&(X,X); // ошибка: тернарное
};


Когда операции ++ и -- перегружены, префиксное использование и постфиксное различить невозможно.

Предопределенные Значения Операций

Относительно смысла операций, определяемых пользователем, не делается никаких предположений. В частности, поскольку не предполагается, что перегруженное = реализует присваивание ее первому операнду, не делается никакой проверки, чтобы удостовериться, является ли этот операнд lvalue.

Значения некоторых встроенный операций определены как равносильные определенным комбинациям других операций над теми же аргументами. Например, если a является int, то ++a означает a+=1, что в свою очередь означает a=a+1. Такие соотношения для определенных пользователем операций не выполняются, если только не случилось так, что пользователь сам определил их таким образом. Например, определение operator+=() для типа complex не может быть выведено из определений complex::operator+() и complex::operator=().

По историческому совпадению операции = и & имеют предопределенный смысл для объектов классов. Никакого элегантного способа \"не определить\" эти две операции не существует. Их можно, однако, сделать недееспособными для класса X. Можно, например, описать X::operator&(), не задав ее определения. Если где-либо будет браться адрес объекта класса X, то компоновщик обнаружит отсутствие определения. Или, другой способ, можно определить X::operator&() так, чтобы вызывала ошибку во время выполнения.

Операции и Определяемые Пользователем Типы

Функция операция должна или быть членом, или получать в качестве параметра по меньшей мере один объект класса (функциям, которые переопределяют операции new и delete, это делать необязательно). Это правило гарантирует, что пользователь не может изменить смысл никакого выражения, не включающего в себя определенного пользователем типа. В частности, невозможно определить функцию, которая действует исключительно на указатели.

Функция операция, первым параметром которой предполагается основной тип, не может быть функцией членом. Рассмотрим, например, сложение комплексной переменной aa с целым 2: aa+2, при подходящим образом описанной функции члене, может быть проинтерпретировано как aa.operator+(2), но с 2+aa это не может быть сделано, потому что нет такого класса int, для которого можно было бы определить + так, чтобы это означало 2.operator+(aa). Даже если бы такой тип был, то для того, чтобы обработать и 2+aa и aa+2, понадобилось бы две различных функции члена. Так как компилятор не знает смысла +, определенного пользователем, то не может предполагать, что он коммутативен, и интерпретировать 2+aa как aa+2. С этим примером могут легко справиться функции друзья.

Все функции операции по определению перегружены. Функция операция задает новый смысл операции в дополнение к встроенному определению, и может существовать несколько функций операций с одним и тем же именем, если в типах их параметров имеются отличия, различимые для компилятора, чтобы он мог различать их при обращении .



Опубликовал admin
23 Мар, Вторник 2004г.



Программирование для чайников.