Е. К. Пугачев Объектно-ориентированное программирование Под общей редакцией Ивановой Г. С. Рекомендовано Министерством общего и профессионального образования Российской Федерации в качестве учебник

Вид материалаУчебник

Содержание


3.6.Переопределение операций
Таблица 3.3. Формы описания функции-оператора
Таблица 3.4 Формы вызова функции - оператора
Пример 3.54. Описания функции-оператора вне класса
Пример 3.55. Пример описания компонентной функции-оператора
Пример 3.56. Переопределение коммутативной операции «умножение на скаляр» и операции «+»
Подобный материал:
1   ...   17   18   19   20   21   22   23   24   ...   39
^

3.6.Переопределение операций


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

Переопределение операции реализовано как особый вид функции со специальным именем operator@ (где @ - знак переопределяемой операции). Такие функции обычно называются функциями-операторами. Функция-оператор - может быть определена как внутри описания класса, так и вне него. Поэтому различают:

а) простую (определенную вне класса) функцию-оператор, которая бывает: одноместной (с одним аргументом) и двуместной (с двумя аргументами);

б) компонентную (определенную в классе) функцию - оператор, у которой первый аргумент всегда объект класса, задаваемый неявно и которая также бывает: одноместной (не имеет явных аргументов) и двуместной (имеет один аргумент – второй операнд).

Общая форма определения функции-оператора приведена в таблице 3.2.

^ Таблица 3.3. Формы описания функции-оператора

Простая функция

Компонентная функция

одноместная

<тип результата>operator @ (аргумент)

одноместная

<тип результата>operator @ ()


двуместная

<тип результата>operator @ (арг1,арг2)


двуместная

<тип результата>operator @ (арг2.)


В приведенных выше описаниях символ @ - это любая допустимая операция, <тип результата> - тип возвращаемого значения (чаще всего того же типа, что и класс, хотя возможен и другой тип этого значения).

При переопределении операций следует помнить, что

- нельзя переопределять операции *,sizeof,?:,#,##,::,class::;

- операции =,[],() можно переопределять только в составе класса;

- переопределенная операция = не наследуется в производных классах;

- нельзя изменять приоритет и ассоциативность операции (порядок выполнения операций одного приоритета).

Функция-оператор допускает две формы вызова, которые представлены в таблице 3.3.

^ Таблица 3.4 Формы вызова функции - оператора

стандартная форма

операторная форма

для простой функции

operator @ (<аргумент>)

operator @(<аргумент1>,<аргумент2>)


для простой функции

@<аргумент>

<аргумент1>@<аргумент2>


для компонентной функции

<аргумент>.operator@()

<аргумент1>.operator@(<аргумент2>)


для компонентной функции

@<аргумент>

<аргумент1>@<аргумент2>

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

Если необходимо, чтобы первый аргумент не был объектом класса, операция может переопределяться только вне класса. При переопределении операции вне описания класса, хотя бы один аргумент должен быть объектом некоторого класса.

^ Пример 3.54. Описания функции-оператора вне класса

#include

#include

#include

class sstr// описание класса строка sstr

{ private: // скрытые поля класса

char *str;

int len;

public: // общедоступные поля класса

void print(void)

{ cout<<"Содержимое скрытых полей: "<< endl;

cout<<" строка: "<

cout<<" длина строки : "<< len<

sstr(){};

sstr(char *vs)

{ len=strlen(vs); str=new char[len+1]; strcpy(str,vs); }

~sstr(){delete str;}

friend sstr & operator +(sstr &,sstr & ); /*переопределение операции «+» - функция описывается вне класса */

};

sstr & operator +(sstr &A,sstr & B) // описание функции-оператора

{ int l =A.len +B.len;

char *s;

s=new char[l+1]; strcpy(s,A.str); strcat(s,B.str);

sstr *ps=new sstr(s); delete []s;

return *ps; }

void main()

{ clrscr();

sstr aa("пример иcпользования ");

sstr bb("переопределения операции");

sstr cc=aa+bb+" добавка"; // операторная форма вызова

cc.print();

sstr dd;

dd=operator +(aa,bb); // стандартная форма вызова

dd.print();

getch(); }

При описании функции-оператора внутри класса (как компонентной) первый аргумент всегда объект класса, что несколько ограничивает возможности ее использования.

^ Пример 3.55. Пример описания компонентной функции-оператора

#include

#include

#include

#include

// описание класса строка sstr

class sstr

{ private: // скрытые поля класса

char *str;

int len;

public:

void print(void)

{ cout<<" содержимое скрытых полей: "<< endl;

cout<<" строка: "<

cout<<" длина строки : "<< len<

sstr(){}

sstr(char *vs) { len=strlen(vs); str=new char[len+1]; strcpy(str,vs); }

~sstr(){delete str;}

sstr & operator +(sstr &); /* прототип компонентной функции, реализующей бинарную операцию «+»*/

sstr & operator -(); /* прототип компонентной функции, реализующей унарную операцию «-» */

};

sstr& sstr::operator -() /* описание переопределения унарной операции - обращение строки */

{ char c;

for (int i=0;i<=len/2-1;i++){c=str[i]; str[i]=str[len-i-1]; str[len-i-1]=c;};

return *this; }

sstr & sstr::operator +(sstr &A) /* описание бинарной компонентной операции – функции */

{ int l =A.len+len;

char *s; s=new char[l+1];

strcpy(s,str); strcat(s,A.str);

len=l; str=s;

return *this; }

void main()

{ clrscr();

sstr aa(" пример использования ");

sstr bb("переопределения операции");

sstr cc=aa+bb; // операторная форма вызова

cc.print();

sstr dd; dd=aa.operator+(bb); // стандартная форма вызова

dd.print();

-aa; -bb; // применение унарного минуса - обращение строк

aa.print(); bb.print();

getch(); }

При переопределении некоторых операций, для обеспечения их коммутативности необходимо описывать несколько вариантов функций-операторов в теле класса.

^ Пример 3.56. Переопределение коммутативной операции «умножение на скаляр» и операции «+»

#include

#include

class point

{ float x,y;

public:

void print(void)

{ cout<<" содержимое скрытых полей: "<< endl;

cout<<" x ="<

out<<" Country : "<

out<<" City : "<

out<<" Street : "<

out<<" House : "<

return out; }

istream& operator >>(istream &in,TAdress &obj) /* тело функции переопределения операции извлечения из потока */

{ cout<<" Введите адрес следующим образом:";

cout<<" страна город улица номер дома"; cout<

in>>obj.country>>obj.city>>obj.street>>obj.number_of_house;

return in; }

void main()

{ clrscr();

TAdress a,b;

cin>>a>>b;

cout<

getch();

}

Операция присваивания, по умолчанию, определена для любого класса и обеспечивает покомпонентное присвоение, аналогичное тому, которое выполняется в копирующем конструкторе. При необходимости эту операцию также можно переопределить (пример 3.37).

В некоторых случаях такое переопределение обязательно, например, при работе с объектными полями.