Краткие теоретические сведения. Понятие переопределения операций

Понятие переопределения операций. Переопределение операций (операторов) – один из видов переопределения функций. Цель перегрузки функций состоит в том, чтобы функция с одним именем по-разному выполнялась и возвращала разные значения при обращении к ней с разными по типам и количеству фактическими параметрами.

Для переопределения оператора создается операция-функция, являющаяся членом класса или дружественной классу, для которого она определена.

Синтаксис операции-функции:

Возвращаемый_тип имя_класса::operator*(список аргументов)

{

тело операции-функции;

}

 

Типом возвращаемого значения операции-функции часто является класс, для которого она определена. Часть описания operator* составляет имя операции-функции, а список аргументов зависит от переопределяемого оператора и реализации операции-функции.

Имеется два способа описания функции, соответствующей переопределяемой операции:

если функция задается как обычная функция-элемент класса, то первым операндом операции является объект класса, указатель на который передается неявным параметром this;

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

Объявление дружественной функции представляет собой прототип функции, объявление переопределяемой операции или имя класса, которым разрешается доступ к личной части данного класса, с ключевым словом friend впереди.

class A{ int x; // Личная часть класса

...

friend class B; // Функции класса B дружественны A

friend void C::fun(A&);// функция fun класса C имеет доступ к A

friend void xxx(A&,int);// Функция xxx дружественна классу A

friend void C::operator+(А&);// операция <объект C>+<объект A>

...}

 

Существуют некоторые ограничения на состав переопределяемых операций и способов переопределения:

нельзя переопределять операции «.», «::», «.*», «?:» ,sizeof, а также директивы препроцессора;

нельзя изменить приоритет операторов;

нельзя изменить число операндов оператора (например, переопределить операцию + так, чтобы в ней использовался только один операнд).

Часто программы работают с объектами, которые являются конкретными представлениями абстрактных понятий. Например, тип данных int в C++ вместе с операциями +, -, *, / и т.д. предоставляет реализацию (ограниченную) математического понятия целых чисел. Такие понятия обычно включают в себя множество операций, которые кратко, удобно и привычно представляют основные действия над объектами. К сожалению, язык программирования может непосредственно поддерживать лишь очень малое число таких понятий. Например, такие понятия, как комплексная арифметика, матричная алгебра, логические сигналы и строки не получили прямой поддержки в C++. Классы дают средство спецификации в C++ представления неэлементарных объектов вместе с множеством действий, которые могут над этими объектами выполняться. Иногда определение того, как действуют операции на объекты классов, позволяет программисту обеспечить более общепринятую и удобную запись для манипуляции объектами классов, чем та, которую можно достичь используя лишь основную функциональную запись. Например:

class complex {

double re, im;

public:

complex(double r, double i) { re=r; im=i; }

friend complex operator+(complex, complex);

friend complex operator*(complex, complex);

};


определяет простую реализацию понятия комплексного числа, в которой число представляется парой чисел с плавающей точкой двойной точности, работа с которыми осуществляется посредством операций + и * (и только). Программист задает смысл операций + и * с помощью определения функций с именами operator+ и operator*. Если, например, даны b и c типа complex, то b+c означает (по определению) operator+(b,c). Теперь есть возможность приблизить общепринятую интерпретацию комплексных выражений. Например:

void f()

{

complex a = complex(1, 3.1);

complex b = complex(1.2, 2);

complex c = b;

 

a = b+c;

b = b+c*a;

c = a*b+complex(1,2);

}

 

Выполняются обычные правила приоритетов, поэтому второй оператор означает b=b+(c*a), а не b=(b+c)*a.

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

+ - * / % ^ & | ~ !

= < > += -= *= /= %= ^= &=

|= << >> >>= <<= == != <= >= &&

|| ++ -- [] () new delete

Последние четыре - это индексирование , вызов функции, выделение свободной памяти и освобождение свободной памяти . Изменить приоритеты перечисленных операций невозможно, как невозможно изменить и синтаксис выражений. Нельзя, например, определить унарную операцию % или бинарную !. Невозможно определить новые лексические символы операций, но в тех случаях, когда множество операций недостаточно, вы можете использовать запись вызова функции. Используйте например, не оператор ** для возведения в степень, а функцию step().

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

void f(complex a, complex b)

{

complex c = a + b; // сокращенная запись

complex d = operator+(a,b); // явный вызов

}


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