Программирование на C и C++

Онлайн справочник программиста на C и C++

Дружественная функция-оператор

Функция-оператор может быть другом класса, а не только его членом. Как было показано ранее в этом разделе, поскольку функции-друзья не являются членами класса, они не могут иметь неявный аргумент this. Поэтому при использовании дружественной функции-оператора оба операнда пе­редаются функции при перегрузке бинарных операторов, а при перегрузке унарных операторов передается один операнд. Следующие операторы не могут использовать перегрузку с помощью функций-друзей: =, (), [], и ->. Остальные операторы могут быть перегружены как с помощью функций-членов, так с помощью функций-друзей. В качестве примера ниже рассматривается мо­дифицированная версия предыдущей программы, в которой оператор + перегружен с помощью дружественной функции:

#include <iostream.h>
class three_d {
int x, у, z; // трехмерные координаты
public:
friend three_d operator+ (three_d op1, three_d op2);
three_d operator=(three d op2); // op1 подразумевается
three_d operator++(); // op1 также подразумевается
void show ();
void assign (int mx, int my, int mz);
};
// дружественная функция
three_d operator+(three_d op1, three_d op2)
{
three_d temp;
temp.x = op1.x + op2.x; // целочисленное сложение
temp.у = op1.у + op2.y; // и в данном случае + сохраняет
temp.z = op1.z + op2.z; // первоначальное значение
return temp;
}
// перегрузка =
three_d three_d::operator=(three_d op2)
{
x = op2.x; // целочисленное присваивание
у = op2.y; // и в данном случае = сохраняет
z = op2.z; // первоначальное значение
return *this;
}
// перегрузка унарного оператора
three_d three_d::operator++()
{
х++;
у++;
z++;
return *this;
}
// вывод координат X, Y, Z
void three_d::show ()
{
cout << x << ", ";
cout << у << ", ";
cout << z << "\n";
}
// присвоение координат
void three_d::assign (int mx, int my, int mz)
{
x = mx;
y = my;
z = mz;
}
int main()
{
three_d a, b, c;
a.assign (1, 2, 3);
b.assign (10, 10, 10);
a.show();
b.show();
с = a+b; // сложение а и b
c.show();
с = a+b+c; // сложение a, b и с
с.show ();
с = b = a; // демонстрация множественного присваивания
с.show();
b.show();
++c; // увеличение с
c.show();
return 0;
}

Как можно видеть, в данном случае оба операнда передаются функции operator+(). Левый опе­ранд передается в переменной op1, а правый — в переменной op2.

Во многих случаях использование функций-друзей вместо функций-членов не дает выигрыша при перегрузке операторов. Однако имеется одна ситуация, в которой необходимо использо­вать дружественные функции. Как известно, указатель на объект, вызывающий функцию-оператор, передается в указателе this. В случае бинарных операторов левый объект вызывает эту фун­кцию. Такой способ работает до тех пор, пока левый объект определяет заданную операцию. Предположим, что для объекта О определены операции присваивания и сложения, так что следующий код

O = O + 10; // будет работать

является корректным. Поскольку объект О находится с левой стороны оператора+, то он вызы­вает оператор-функцию, перегружающую операцию сложения, которая по предположению спо­
собна добавить целое число к определенному элементу объекта О. Однако следующая инструкция не является корректной:

О = 10 + O; // не будет работать

Причина, по которой эта инструкция не будет выполняться, заключена в том, что слева от опера­тора + теперь стоит целое число, являющееся встроенным типом и не имеет функции-члена, кото­рая могла бы осуществить сложение с объектом О.

Можно использовать встроенные типы с левой стороны оператора +, если перегрузка осуще­ствляется с помощью двух дружественных функций. В таком случае функции-оператору явным образом передаются оба аргумента и она вызывается точно так же, как любая перегруженная функция, основываясь на типе своих аргументов. Одна из версий функции-оператора + обраба­тывает суммирование объект + целое, а вторая обрабатывает суммирование целое + объект. Пере­грузка оператора + (или любого другого бинарного оператора) с использованием дружествен­ных функций позволяет складывать переменные встроенных типов с объектами. Следующая программа показывает реализацию такой перегрузки:

#include <iostream.h>
class CL {
public:
int count;
CL operator=(int i);
friend CL operator+(CL ob, int i);
friend CL operators+(int i, CL ob);
};
CL CL::operator=(int i)
{
count = i;
return *this;
}
// обрабатывает ob + int.
CL operator+ (CL ob, int i)
{
CL temp;
temp.count = ob.count + i;
return temp;
}
// обрабатывает int + ob.
CL operator+ (int i, CL ob)
{
CL temp;
temp.count = ob.count + i;
return temp;
}
int main()
{
CL obj;
obj = 10;
cout << obj.count << " "; // выводит 10
obj = 10 + obj; // прибавление объекта к целому
cout << obj. count << " "; // выводит 20
obj = obj + 12; // прибавление целого к объекту
cout << obj.count; // выводит 32
return 0;
}

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