Краткие теоретические сведения. Понятие переопределения операций
Понятие переопределения операций. Переопределение операций (операторов) – один из видов переопределения функций. Цель перегрузки функций состоит в том, чтобы функция с одним именем по-разному выполнялась и возвращала разные значения при обращении к ней с разными по типам и количеству фактическими параметрами.
Для переопределения оператора создается операция-функция, являющаяся членом класса или дружественной классу, для которого она определена.
Синтаксис операции-функции:
Возвращаемый_тип имя_класса::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 оба инициализатора являются синонимами.