Например, в выражении вида c+z, где с — число типа double, а z — объект класса комплексных чисел, операция + может быть переопределена только friend-функцией с двумя параметрами. В этом можно убедиться, анализируя способ интерпретации компилятором выражения: z+48. Если операция + реализована как метод класса, то он будет z.operator+(48); что вполне осмысленно, но при этом выражение: 48+z; должно интерпретироваться как: 48.operator+(z);. Однако это не имеет смысла, так как 48 не является и не может быть (в С++) объектом класса, определенного пользователем. Необходимо также иметь в виду и следующие ограничения:
¨ операции =,(),[],-> могут быть переопределены только с помощью метода класса;
¨ описатель static для метода класса, переопределяющего операцию, недопустим.
Рассмотрим, как используется возможность переопределения операции на примере реализации абстрактного типа данных: Вектор на плоскости. Каждый вектор (объект класса Vector) имеет две координаты, задающие положение вектора на плоскости. Будем хранить эти координаты в двух private-компонентах класса x1, x2. Совместим с этим типом данных следующие операции: присвоения, сложения, вычитания, скалярного произведения, проверки равенства, отношения «больше», а также вычисления нормы. С этой целью осуществим переопределение операций (соответственно: =, +, -, *, ==, >, !) в классе Vector. Ясно, что только одна операция взятия нормы является унарной. Реализуем ее в виде метода класса без параметров. Операции =, ==, >, +, - тоже можно реализовать в виде методов класса с одним параметром. С целью иллюстрации технологии выполним переопределение операции умножения на скаляр (слева) c помощью внешней friend-функции.
class Vector
{
double x1, x2; // Координаты вектора
public: // Три конструктора
Vector () { x1 = x2 = 0.; } // Default
Vector (double c1, double c2) { x1=c1; x2=c2; } // С параметрами
Vector (const Vector& v){ x1 = v.x1; x2 = v.x2; }// Copy
//====== Переопределение операций =====//
Vector operator=(Vector& v) // Присвоение
{
if (this==&v)
return *this;
x1=v.x1; x2=v.x2;
return *this;
}
bool operator>(Vector& v) // Операция >
{
return !*this > !v;
}
bool operator==(Vector& g) // Проверка равенства
{
return x1==g.x1 && x2==g.x2;
}
Vector operator+(Vector& g) // Сложение
{
return Vector (x1+g.x1, x2+g.x2);
}
Vector operator-(Vector& g) // Вычитание
{
return Vector(x1-g.x1, x2-g.x2);
}
Vector operator*(double f) // Умножение на скаляр
{
return Vector(f*x1, f*x2);
}
double operator*(Vector& g) // Скалярное произведение
{
return (x1*g.x1 + x2*g.x2);
}
double operator!() // Вычисление нормы
{
return sqrt (x1*x1+x2*x2);
}
friend Vector operator*(double, Vector&);
};
//====== Внешняя friend-функция ======//
Vector operator*(double f, Vector& g) // Умножение. на скаляр
{
return Vector(f*g.x1, f*g.x2);
}
void main()
{
Vector a(3,4), b(0,4), c=a, vv[5];
puts ("\n Class Vector Demonstration");
c=vv[0]=b; // Цепочка присвоений
printf ("\n Norm of c=vv[0]=b:\t %6.2f",!c);
c=5.*a;
printf ("\n Norm of 5.*a:\t\t %6.2f",!c);
c=b*!a*5.;
printf ("\n Norm of c=b*!a*5.:\t %6.2f",!c);
c=a+b;
printf ("\n Norm of a+b:\t\t %6.2f",!c);
printf ("\n Norm of a-b:\t\t %6.2f",!(a-b));
printf ("\n\n Scalar product (a,b) = %3.0f",a*b);
if (b>a)
puts("\n\n Norm of b is greater than that of a");
if (a==b) ;
else
puts("\n\n Vector a is not equal to vector b");
a=b;
if (a==b)
puts("\n After assignment a=b; we have a==b");
vv[3]=a;
printf ("\n Norm of vv[3]=a =%6.2f",!vv[3]);
}
При анализе этой программы следует обратить внимание на следующие моменты. Класс Vector имеет три конструктора. Конструктор с двумя параметрами позволяет создать вектор с желаемыми компонентами, конструктор без параметров позволяет определить массив объектов класса, конструктор копирования позволяет корректно производить инициализацию объектов класса Vector другими объектами своего же класса. Реализация public-метода, переопределяющего операцию бинарную операцию присвоения, использует указатель this. Так как this содержит адрес объекта, пославшего сообщение (operator=), то значением, возвращаемым оператором return *this; будет объект, стоящий слева от знака присвоения. Поэтому становится возможной цепочка присвоений c=vv[0]=a;. Метод класса, переопределяющий операцию !, возвращает вещественное число, равное сумме квадратов компонент вектора, то есть выбранную нами норму вектора. Здесь унарная операция переопределяется с помощью public-метода без аргументов. Результат проверки равенства двух векторов (операция ==) имеет тип bool. Параметры всех friend-функций — есть ссылки на объекты класса Vector. Это позволяет сэкономить память стека, так как при передаче ссылкой копии параметров в системном стеке не делаются. Методы, переопределяющие операции + и - возвращают объекты класса Vector. Операция * переопределена трижды:
¨ методом, возвращающим double, (скалярное произведение двух векторов);
¨ методом, возвращающим Vector, (произведение вектора на скаляр);
¨ внешней функцией, возвращающей Vector, (произведение скаляра на вектор).
Чтобы переопределять операции ++ и -- с учетом префиксной и постфиксной модификаций, допустим, что эти операции определены для вектора на плоскости и означают увеличение или уменьшение модуля вектора на единицу при неизменной его ориентации. Очевидно, формулы пересчета координат вектора должны быть:
x1=(|x|+1)*x1/|x|, x2=(|x|+1)*x2/|x|,
где |x| — модуль вектора. Любую унарную операцию можно переопределить либо с помощью метода класса без параметров, либо с помощью friend-функции с одним параметром. В Visual C++ принято отличать постфиксную модификацию от префиксной путем описания лишнего фиктивного, неиспользуемого формального параметра типа int в заголовке метода или функции. Например, объявления:
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.