Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lutsik_Yu_A_Obektno_orientir_programmir_na_yaz.pdf
Скачиваний:
63
Добавлен:
11.05.2015
Размер:
4.33 Mб
Скачать

int main() { A a;

a.fun(2,2); // ошибка, неизвестно, какая из 2 функций вызывается return 0;

}

В этом случае возникает неоднозначность вызова функции fun объекта a.

5.2. Перегрузка операторов

Имеется ограничение в языках С и С++, накладываемое на операции над

известными типами данных (классами) char, int, float и т.д.:

Р

char c,

 

int i,j;

 

double d,k;

У

В С и С++ определены множества операций над объектами c,i,j,d,k этих

 

Г

 

классов, выражаемых через операторы: i+j, j/k, d*(i+j). БольшинствоИоператоров (операций) в С++ может быть перегружено (переопределено), в результате чего

расширяется диапазон применения этих операций. Когда оператор перегружен, ни одно из его начальных значений не теряет смысла. Просто для некоторого

а

 

класса объектов определен новый оператор (опер ция). Для перегрузки (дооп-

ределения) оператора разрабатываются функции, являющиесяБ

либо компонен-

к

 

тами, либо friend-функциями того класса, для оторого они используются. Ос- тановимся на перегрузке пока только с использов нием компонент- функций

класса.

меет

Для того чтобы перегрузить оп ратор, требуется определить действие

этого оператора внутри класса. Общаяеформа записи функции-оператора, яв-

 

о

вид

ляющейся компонентой класса, и

тип_возвр_значе

имя класса :: operator #(список аргументов)

{ действия, выполняемые применительно к классу

};

 

 

б

 

 

Вместо символа ния# став тся значок перегружаемого оператора.

Следуетиотметить, что нельзя перегрузить триадный оператор «?:.», опе- ратор «sizeof» оператор разрешения контекста «::».

БФункц я operator должна быть либо компонентой класса, либо иметь хотя бы один аргумент типа «объект класса» (за исключением перегрузки операто- ров new и delete). Это позволит доопределить свойства операции, а не изменить их. При этом новое значение оператора будет иметь силу только для типов дан- ных, определенных пользователем; для других выражений, использующих опе- ранды стандартных типов, значение оператора останется прежним.

Выражение a#b, имеющее первый операнд а стандартного типа данных, не может быть переопределено функцией operator, являющейся компонентом клас- са. Например, выражениие a 4 может быть представлено как a.operator-(4), где а

объект некоторого типа. Выражение вида 4 a нельзя представить в виде

104

4.operator(a). Это может быть реализовано с использованием глобальных функ-

ций operator.

Функция operator может быть вызвана так же, как и любая другая функ- ция. Использование операции лишь сокращенная форма вызова функции. На- пример, запись вида a=в−с эквивалентна a=operator-(b,с).

В заключение отметим основные правила доопределения операторов:

- все операторы языка С++, за исключением . :: ?: sizeof и символов # ##, можно доопределять;

 

- при вызове функции operator используется механизм перегрузки функ-

ций;

Р

 

- количество операндов, которыми оперируют операторы (унарные, би-

нарные), и приоритет операций сохраняются и для доопределенных операторов.

 

 

 

 

 

 

 

 

 

У

 

может

 

 

 

 

 

 

Г

И

операторов

 

 

 

 

 

 

 

 

Б

 

 

 

 

- как компонента-функция класса с одним аргументом;

 

 

- как глобальная функция (функция, описанная вне класса) с двумя аргу-

ментами

 

 

 

 

 

ак

 

 

 

ставлено при первом способе

 

 

 

 

a.operator#(b) или как operator #(a,b) при

втором способе перегрузки.

е

ора

 

 

 

 

Рассмотрим простой прим р п реопределения операторов *, = , > и ==

 

 

 

 

 

ту

 

 

 

 

 

по отношению к объек , сод ржащ му декартовы координаты точки на плос-

кости. В примере использован п рвый способ перегрузки.

 

 

 

 

 

о

 

 

 

 

 

 

 

#include <iostream>

 

 

 

 

 

 

 

 

 

и

 

 

 

 

 

 

 

 

using namespace std;

 

 

 

 

 

 

 

 

л

 

 

 

 

 

 

 

 

 

class dek koord

 

 

 

 

 

 

 

 

{ int x,y; // декартовы координаты точки

 

 

 

 

public:

 

 

 

 

 

 

 

 

 

и

dek koord(){};

 

 

 

 

 

 

 

dek_koord(int X,int Y): x(X),y(Y) {}

 

 

 

Б

бdek_koord operator*(const dek_koord);

 

 

 

 

 

 

 

dek_koord operator=(const dek_koord); dek_koord operator>(const dek_koord); int operator ==(const dek_koord);

void see();

};

dek_koord dek_koord::operator*(dek_koord a) // перегрузка операции * { dek_koord tmp; // локальный объект

tmp.x=x*a.x; tmp.y= y*a.y;

105

return tmp;

}

dek_koord dek_koord::operator =(const dek_koord a)

{ x=a.x; // перегрузка операции = y=a.y;

return *this;

}

 

 

 

 

 

 

 

 

 

 

 

 

 

dek_koord dek_koord::operator >(const dek_koord a)

 

 

 

 

{ if (x<a.x) x=a.x;

 

 

 

 

 

 

// перегрузка операции >

if (y<a.y) y=a.y;

 

 

 

 

 

 

 

 

 

 

Р

return *this;

 

 

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

 

int dek_koord::operator ==(const dek_koord a)

 

 

 

 

// перегрузка операции ==

{ if (x==a.x && y==a.y) return 0;

// 0 – координаты равны

 

И

if (x>a.x

&& y>a.y)

return 1;

 

//

 

 

 

 

 

У

 

if (x<a.x

&& y<a.y) return -1;

 

//

 

 

else return 2;

 

 

 

 

 

// неопределенность

 

 

 

}

 

 

 

 

 

 

 

 

Г

 

 

 

 

 

 

 

 

 

 

содержимогоБ

 

 

 

void dek_koord::see()

 

// функция просмот

объекта

 

{ cout << "координата х = " << x << endl;

 

 

 

 

 

cout << "координата y = " << y << endl;ра

 

 

 

 

}

 

 

 

 

 

 

 

к

 

 

 

 

 

int main()

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

е

 

 

 

 

 

{ dek_koord A(1,2), B(3,4), C;

 

 

 

 

 

int i;

 

 

 

 

 

т

 

 

 

 

 

 

A.see();

 

 

 

 

 

 

 

 

 

 

B.see();

 

 

 

 

 

 

 

 

 

 

 

 

о

 

 

 

 

 

 

 

C=A*B;

 

 

 

 

 

 

 

 

 

// внача е перегрузка операции * затем =

 

 

 

 

C.see();

 

и

 

 

 

 

 

 

 

 

C=A>B;

 

 

 

 

 

 

 

 

 

// компоненты объекта С принимают значение max от А и В

C.see();

л

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

i=A==B;

// i получает значение сравнения A==B

(1,0,1,2….)

 

б

 

 

 

 

// ошибка

 

 

 

 

 

// cout << A==B << endl;

 

 

 

 

 

//иerror binary '<<' : no operator defined which takes a right-hand operand

// of type 'class dek_koord' (or there is no acceptable conversion)

 

Б

 

 

 

 

 

 

 

 

 

 

 

 

 

cout << (A==B) << endl; // верно

}

Результат работы программы: координата х = 1

координата y = 2 координата х = 3

106

координата y = 4 координата х = 3 координата y = 8 координата х = 3 координата y = 4

В приведенной выше программе функцию перегрузки оператора * можно изменить, например, следующим образом:

dek_koord &dek_koord::operator*(const dek_koord &a) { x*=a.x;

y*=a.y; Р

return *this;

} И

В этом примере функция operator* в качестве параметра получает ссылку на объект, стоящий в правой части выражения А*В,Ут.е. на В. Ссылка это вто- рое имя (псевдоним) для одного и того же объектаГ. Более подробно ссылки бу- дут рассмотрены ниже. Функция operator* при вызове получает скрытый указа-

тель на объект А и модифицирует неявные параметры (компоненты-данные

объекта А х и у). Возвращается значение по адресу this, т.е. объект А. Воз-

вращать ссылку на объект необходимо для реализации выражения вида A*B*C.

Следует отметить, что если в опи

Бнии класса dek_koord присутствуют

 

 

 

 

к

 

 

 

объявления двух функций перегруз и опер ции *:

 

 

class dek_koord

 

са

 

 

{ . . .

 

 

 

 

 

 

 

 

dek_koord operator*(const dek_koord );

 

 

 

 

dek_koord &operator*(constеdek_koord &);

 

 

};

. . .

 

 

 

 

 

 

 

и

 

ошибка будет, если одна из функций являет-

то возникает ош бка. Аналогичнот

 

 

л

 

 

 

 

 

ся компонентой классао, а другая − глобальной функцией.

 

 

Ес и возвращаемое значение функции operator является ссылкой, то в

б

 

 

 

 

 

 

этом случае возвращаемое значение не может быть автоматической или стати-

и

 

 

 

 

 

 

 

ческой лока ьной переменной.

 

 

 

 

Рассмотрим фрагмент программы, в которой функция operator* является

Б

 

 

 

 

 

 

 

 

глобальной.

 

 

 

 

 

 

class dek_koord

 

 

 

 

 

{ int x,y; // декартовы координаты точки

 

 

 

public:

 

 

 

 

 

 

 

 

. . .

 

 

 

 

 

 

 

int read_x();

// возвращает компоненту

x

 

 

 

int read_y();

// возвращает компоненту

y

 

 

 

void write_x(int);

// модифицирует компоненту

x

 

 

void write_y(int);

// модифицирует компоненту

y

 

 

. . .

 

 

 

 

 

 

 

 

 

 

 

 

 

107

};

int dek_koord::read_x(){return x;} int dek_koord::read_y(){return y;} void dek_koord::write_x(int a){x=a;} void dek_koord::write_y(int a){y=a;}

dek_koord operator*(dek_koord a,dek_koord b) // перегрузка операции *

{ dek_koord tmp;

// функция operator − глобальная

tmp.write_x(a.read_x()*b.read_x());

Р

tmp.write_y(a.read_y()*b.read_y());

 

return tmp;

 

}

И

 

В глобальной функции operator* доступ к private данным локального объ-

 

У

екта tmp возможен через public-функции этого объекта, либо данные класса

должны иметь атрибут public, что не отвечает принципу инкапсуляции. Кроме того, если функция operator является friend-функцией некоторого класса, то она имеет доступ к private-компонентам этого класса. Это будет рассмотрено не-

сколько ниже.

 

 

 

 

Г

 

 

 

 

 

 

Далее приведен пример еще одной программы перегрузки оператора «- »

для использования его при вычитании из одной строки другой.

#include <iostream>

 

 

 

Б

 

 

а

using namespace std;

 

 

#include <string.h>

 

 

 

 

к

 

class String

 

 

 

 

 

 

локальнаяекомпонента

{

char str[80];

//

 

public:

 

//

глобальные компоненты

 

void init (char *s);

// функция инициализации

 

 

 

т

 

 

 

int operator - (String s new); // прототип функции operator

}

my_string1, my string2;о// описание двух объектов класса String

void String::init (charи*s)

// функция обеспечивает копирование

 

 

л

 

// строки аргумента(s) в строку-компоненту

 

 

 

// (str)

класса String

{ strcpy(str,s);}

 

int String::operatorб - (String s_new)

// перегрузка оператора – (вычитания

{

и

 

 

 

// строк)

 

 

 

 

 

 

for (int i=0; str[i]==s_new.str[i]; i++)

 

 

if (!str[i]) return 0;

 

 

 

 

Б

 

 

 

 

 

 

return str[i] - s_new.str[i];

 

 

 

}

int main()

{char s1[51], s2[51];

cout <<"Введите первую строку (не более 80 символов) :" <<endl; cin >>s1;

108

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]