Переопределение оператора внешней функцией
Переопределение операторов - одна из наиболее интересных тем, поскольку в ней в полной мере проявляется краса и гибкость языка программирования С++. Идею, положенную в основу концепции переопределения операторов, проиллюстрируем на простом примере. Допустим, что для работы с комплексными числами создается специальный класс (на самом деле в С++ для работы с комплексными числами существует класс complex, однако в дан ном случае это не принципиально). Спецификация класса такова, что он содержит два поля: действительную и мнимую части комплексного числа. При сложении двух комплексных чисел, как известно, получаем комплексное число, действительная часть которого равна сумме действительных частей исходных чисел, а мнимая часть равна соответственно сумме мнимых частей. Безусловно, для сложения комплексных чисел, реализованных через объекты созданного пользователем класса, можно было бы создать специальную функцию, аргументами которой указываются комплексные числа (объекты), сумма которых вычисляется. Однако это не очень удобно в силу нескольких причин. Главная из них состоит в том, что интерфейс вызова операции сложения комплексных чисел в этом случае существенно отличается от сложения действительных чисел, которое реализуется с помощью оператора сложения +. К счастью, через механизм переопределения операторов существует возможность доопределить операцию сложения комплексных чисел (с точки зрения языка С++ это означает сложение объектов, которые принадлежат соответствующему классу) так, что можно будет для этого, как и при сложении обычных (некомнлексных) чисел, использовать оператор сложения +. Более обще, механизм переопределения операторов позволяет использовать эти операторы для выполнения операций над объектами, которые принадлежат к классам, созданным пользователям. При работе со встроенными типами данных действие переопределенных операторов изменений не претерпевает.
Технически переопределение операторов осуществляется путем создания операторных функций. Главное отличие операторных функций от обычных, которые рассматривались ранее, состоит в том, что формально на званием операторной функции является переопределяемый оператор. Эта особенность прослеживается в синтаксисе определения операторной функции. Однако важное значение имеет также то, принадлежит ли операторная функция определенному классу (тому, для работы с объектами которого переопределяется соответствующий оператор) или является внешней. Данный факт определяет в рамках процедуры переопределения оператора способ ссылки на его операнды (для бинарных операторов) или операнд (для унарных операторов). Сначала рассмотрим ситуацию, когда операторы переопределяются внешними функциями. При переопределении оператора внешней функцией имеет значение доступность тех полей (и методов) класса, к которым обращается создаваемая операторная функция. Если среди членов класса, к которым обращается операторная функция, есть закрытые, операторная функция должна быть дружественной функцией класса. Аргументами операторной функции являются операнды. Поэтому при переопределении бинарных операторов операторной функции (внешней)
передают два аргумента, а при переопределении унарных операторов операторная функция имеет один аргумент. Аргументы разделяются занятой, а перед каждым аргументом указывается тип или класс, к которому при надлежит аргумент. Программный код операторной функции указывается в фигурных скобках. Как и для обычных функций, код можно размещать сразу после прототипа функции или в ином месте программы. В качестве иллюстрации рассмотрим небольшую программу, в которой для объектов класса МСоmр, который отождествляем с комплексными числами, переопределяется операция сложения (то есть переопределяется оператор +).
#includeusing namespace std; //Класс для реализации комплексных чисел: class MComp{ public: double Re; double Im; }; //Переопределение оператора сложения: MComp operator+(MComp x, MComp y){ MComp z; z.Re=x.Re+y.Re; z.Im=x.Im+y.Im; return z;} int main(){ MComp a,b,c; a.Re=1; a.Im=2; b.Re=2; b.Im=3; //Сложение объектов: c=a+b; cout << "c.Re = " << c.Re << "\n"; cout << "c.Im = " << c.Im << "\n"; return 0; }