Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ООП (С++) Лекции Бобин.doc
Скачиваний:
61
Добавлен:
08.02.2015
Размер:
625.66 Кб
Скачать

Глава 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);

Создание:

  1. 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. Особенности перегрузки операторов

  1. Операторы присваивания (=), индексирования ([]) и косвенного обращения (->) к компоненту можно перегружать только в виде компонентных функций.

  2. Если в классе перегружен оператор ‘>’ и оператор ‘=’, то это не значит, что будет перегружен составной оператор ‘>=’. При необходимости составной оператор нужно перегружать отдельно.

  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)

  1. При перегрузке операторов НЕЛЬЗЯ:

  1. создавать новые операторы;

  2. изменять приоритеты операторов;

  3. изменять синтаксис оператора

  4. перегружать операторы для стандартных типо

  5. перегружать операторы:

  • разрешения области видимости «::»

  • прямого доступа «.»

  • прямого доступа с разыменованием «. *»

  • тернарный оператор «? :»

п.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 *().