
- •Объектно-ориентированное программирование
- •Глава 1. Объектно-ориентированный подход.
- •Глава 4. Статические компоненты классов
- •Глава 5. Друзья класса
- •Глава 7. Перегрузка стандартных операторов
- •Глава 8. Классы ресурсоемких объектов.
- •Глава 11. Динамическая идентификация типа (rtti). Операторы приведения типа.
- •Глава 12. Обработка исключительных ситуаций
- •Глава 16. Библиотека ввода-вывода.
- •Глава 17. Контейнеры, итераторы, алгоритмы
Глава 7. Перегрузка стандартных операторов
В С++ имеется специальный механизм, позволяющий расширить действие стандартных операторов на объекты пользовательских классов, который называется механизмом перегрузки.
п.7.1. Суть перегрузки операторов
П
y y
v=(;)
(x,y)
а
s s2
x
x
class Square
{
double x, y;
double a;
...
};
class Vector
{
double alpha;
double beta;
...
};
Хотим сделать так: Square
s(0, 0, 1); Vector
v(1, 6); Square
s2 = s + v;
Написали так: class
Square {
... public: ... Square
move(const Vector &v); };
Square
s(0, 0, 1); Vector
v(1, 6); Square
s2 = s.move(v);
По своей сути два предложенных варианта являются равносильными, но вариант с использованием знака «+» более компактный и наглядный
Механизм перегрузки операторов представляет собой всего лишь альтернативный, более удобный, способ вызова функции. Технически любой оператор в С++ представляет собой обыкновенную функцию.
Конструкцию s + v можно представить в префиксной форме +(s, v), что и делается программистом при перегрузке операторов.
Перегрузка операторов лишь механизм, чтобы сделать программу более читаемой, но не нужно злоупотреблять.
п.7.2. Синтаксис перегрузки операторов
Чтобы перегрузить какой-нибудь оператор на объекты конкретного класса, достаточно определить компонентную или внешнюю глобальную функцию, имя которой начинается с ключевого слова operator, а за ним следует обозначение перегружаемого оператора.
п.7.2.1. Перегрузка унарного оператора.
Для перегрузки унарного оператора необходимо создать либо компонентную функцию (метод) без параметров (в этом случае операнд, к которому применяется оператор, является объектом класса, для которого перегружается операторная функция), либо внешнюю глобальную функцию с одним параметром (операнд передается как единственный параметр операторной функции).
Предположим, есть класс Point, и мы хотим перегрузить оператор «-», который строит симметричную точку.
Point p, p2;
p2 = -p;
Технически это реализуется так:
p2 = p.operator –(); либо:
p2 = operator –(p);
Создание:
class Point
{
public:
Point operator –();
};
Point
operator –(Point &p) { ... }
Пример:
class Point
{
double x, y;
public:
Point(double = 0.0, double = 0.0); // Комбинированный конструктор
// общего вида и умолчаний
void print() const;
Point operator –() const;
friend point operator +(const Point &); // Глобальная функция
};
Point::Point(double x, double y): x(x), y(y)
{
}
void Point::print()
{
cout << “(“ << x << “, “ << y << “)” << endl;
}
Point Point::operator –() const
{
return Point(-x, -y);
}
Point operator +(const Point &p)
{
return Point(fabs(p.x), fabs(p.y));
}
int main()
{
Point p1(-1, 1), p2, p3;
p2 = -p1; //~~ p2 = p1.operator –();
p3 = +p1; //~~ p3 = operator +(p1);
p2.print();
p3.print();
return 0;
}
п.7.2.2. Перегрузка бинарных операторов
Для перегрузки бинарного оператора необходимо создать либо метод класса с одним параметром (в этом случае левый операнд является объектом класса, для которого вызывается операторная функция, а правый операнд – ее параметром), либо внешнюю глобальную функцию с двумя параметрами (в этом случае левый операнд передается как первый параметр, а правый – как второй). То есть, предположим, у нас есть точка, и мы хотим наделить бинарный минус, применимый к двум точкам, следующим смыслом: вычисление расстояния между точками.
Point p1, p2;
double distance;
distance = p1 – p2;
С
class
Point { ... double
operator –(const Point & r); };
...
distance
= p1.operator –(p2);
double
operator –(const Point & l, const Point & r);
...
distance
= operator –(p1, p2);
Пример:
class Point
{
double x, y;
public:
Point(double = 0.0, double = 0.0);
void print() const;
double operator –(const Point & r) const;
friend Point operator +(const Point & l, const Point & r);
};
double Point::operator –(const Point & r) const
{
return sqrt((x – r.x)*(x – r.x) + (y – r.y)*(y – r.y));
}
Point operator +(const Point & l, const Point & r)
{
return Point(l.x + r.x, l.y + r.y);
}
int main()
{
Point p1(-1, 1), p2(2, 2), p3;
double distance;
distance = p1 – p2;
p3 = p1 + p2;
cout << “Distance: “ << distance << endl;
p3.print();
return 0;
}
п.7.2.3. Перегрузка операторов инкремента и декремента
Для операторов инкремента и декремента существует префиксный и постфиксный варианты. Перегрузка обоих операторов в префиксном варианте производится по правилу унарных операторов. Перегрузка в постфиксном варианте производится особым образом: добавляется фиктивный параметр типа int.
class Point
{
double x, y;
public:
...
const Point & operator ++();
const Point operator ++(int);
friend const Point & operator -–(Point &);
friend const Point operator -–(Point &, int);
};
const Point & Point::operator ++()
{
++x;
++y;
return *this;
}
const Point Point::operator ++(int)
{
Point old = *this;
++x;
++y;
return old;
}
const Point operator –(Point & p)
{
--p.x;
--p.y;
return p;
}
const Point operator –-(Point & p, int)
{
Point old = p;
--p.x;
--p.y;
return old;
}
int main()
{
...
Point A(1, 1), B(2, 2);
cout << (A – B) << endl;
cout << (A – (++B)) << endl;
cout << (A – (B++)) << endl; // A.operator – (B.operator ++(0));
cout << (A – (--B)) << endl;
cout << (A – (B--)) << endl;
return 0;
}
п.7.3. Особенности перегрузки операторов
Операторы присваивания (=), индексирования ([]) и косвенного обращения (->) к компоненту можно перегружать только в виде компонентных функций.
Если в классе перегружен оператор ‘>’ и оператор ‘=’, то это не значит, что будет перегружен составной оператор ‘>=’. При необходимости составной оператор нужно перегружать отдельно.
Общая схема перегрузки операторов
Выражение
Метод
Глобальная функция
@x
x.operator@()
operator@(x)
x@y
x.operator@(y)
operator@(x, y)
x@
x.operator@(0)
operator@(x, 0)
x = y
x.operator=(y)
x[y]
x.operator[](y)
x->y
x.operator->(y)
x(y, z)
x.operator()(y, z)
operator()(x, y, z)
При перегрузке операторов НЕЛЬЗЯ:
создавать новые операторы;
изменять приоритеты операторов;
изменять синтаксис оператора
перегружать операторы для стандартных типо
перегружать операторы:
разрешения области видимости «::»
прямого доступа «.»
прямого доступа с разыменованием «. *»
тернарный оператор «? :»
п.7.4. Оператор приведения
Оператор приведения перегружается только через компонентную функцию, имя которой начинается с ключевого слова operator, а затем через пробел указывается имя типа, к которому осуществляется приведение. Оператор приведения типа не имеет возвращаемого значения и не имеет параметров.
Пример:
class Point
{
...
public:
Point(double = 0.0, double = 0.0);
operator double() const; // преобразование «точки» в действит. число
};
Point::operator double() const
{
return sqrt(x*x + y*y);
}
int main()
{
Point A(1, 1);
doubre rv = A; // rv = (double) A;
cout << “Длина радиус-вектора: “ << rv << endl;
return 0;
}
Производится автоматическое преобразование типа «Point» в тип «double».
Замечание: Вместо «double» может быть указан любой тип, даже сложный, например:operator const double *().