Перегрузка унарного оператора — КиберПедия 

Типы сооружений для обработки осадков: Септиками называются сооружения, в которых одновременно происходят осветление сточной жидкости...

История создания датчика движения: Первый прибор для обнаружения движения был изобретен немецким физиком Генрихом Герцем...

Перегрузка унарного оператора

2017-11-27 375
Перегрузка унарного оператора 0.00 из 5.00 0 оценок
Заказать работу

При перегрузке унарной операции функция operator не имеет параметров. Как и в предыдущем случае, модифицируемый объект передается в функцию-operator неявным образом, используя указатель this.

Унарный оператор, как и бинарный, может быть перегружен двумя способами:

- как компонента-функция без аргументов;

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

Как известно, унарный оператор может быть префиксным и постфиксным. Для любого префиксного унарного оператора выражение #a может быть представлено при первом способе как a.operator#(), а при втором как #operator(a).

При перегрузке унарного оператора, используемого в постфиксной форме, выражение вида a# может быть представлено при первом способе как a.operator#(int) или как operator#(a,int) при втором способе. При этом аргумент типа int не существует и используется для отличия префиксной и постфиксной форм при перегрузке.

Ниже приведен пример программы перегрузки оператора ++ и реализации множественного присваивания. Для перегрузки унарного оператора ++, предшествующего оператору ++i, вызывается функция operator ++(). В случае если оператор ++ следует за операндом i++, то вызывается функция

operator++(int x), где х принимает значение 0.

#include "iostream.h"

class dek_koord

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

public:

dek_koord(){};

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

void operator++();

void operator++(int);

dek_koord operator=(dek_koord);

void see();

};

void dek_koord::operator++() // перегрузка операции ++A

{ x++;}

void dek_koord::operator++(int) // перегрузка операции A++

{ y++;}

dek_koord dek_koord::operator =(dek_koord a)

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

y=a.y;

return *this;

}

void dek_koord::see()

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

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

}

void main()

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

A.see();

A++; // увеличение значения компоненты х объекта А

A.see(); // просмотр содержимого объекта А

++A; // увеличение значения компоненты у объекта А

A.see(); // просмотр содержимого объекта А

C=B=A; // множественное присваивание

B.see();

C.see();

}

Результат работы программы:

координата х = 1

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

координата х = 1

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

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

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

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

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

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

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

 

Дружественная функция operator

Функция operator может быть не только членом класса, но и friend-функцией этого класса. Как было отмечено ранее, friend-функции, не являясь компонентами класса, не имеют неявного указателя this. Следовательно, при перегрузке унарных операторов в функцию operator передается один, а бинарных – два аргумента. Необходимо отметить, что операторы: =, (), [] и -> не могут быть перегружены с помощью friend-функции operator.

#include "iostream.h"

class dek_koord

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

public:

dek_koord(){};

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

friend dek_koord operator*(int,dek_koord);

friend dek_koord operator*(dek_koord,int);

dek_koord operator=(dek_koord);

void see();

};

dek_koord operator*(int k,dek_koord dk) // перегрузка операции int *A

{ dk.x*=k;

dk.y*=k;

return dk;

}

dek_koord operator*(dek_koord dk,int k) // перегрузка операции A*int

{ dk.x*=k;

dk.y*=k;

return dk;

}

dek_koord dek_koord::operator=(dek_koord dk)

{ x=dk.x;

y=dk.y;

return *this;

}

void dek_koord::see()

{ cout << "координата т.(х,y) = " << x<<’ ’ <<y<< endl;

}

void main()

{ dek_koord A(1,2), B;

A.see();

B=A*2; // увеличение значения объекта А в 2 раза

B.see(); // просмотр содержимого объекта B

}

Результат работы программы:

координата т.(х,у)= 1 2

координата т.(х,у)= 2 4

 

Особенности перегрузки операции =

Доопределение оператора = позволяет решить проблему присваивания, но не решает задачи инициализации, так как при инициализации должен вызываться соответствующий конструктор. В рассматриваемом случае это решается использованием конструктора копирования.

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

#include "iostream.h"

#include "string.h"

Class string

{ char *str; // символьная трока

int size; // длина символьной строки

public:

string(){}; // конструктор по умолчанию

string(int n,char *s) // конструктор с параметрами

{ str=new char[size=n>=(strlen(s)+1)? n: strlen(s)+1];

strcpy(str,s);

}

string(const string &); // конструктор копирования

~string(){}; // деструктор

friend string operator+(string, const string);

string &operator=(const string &);

void see();

};

string::string(const string &a) // описание конструктора копирования

{ str=new char[a.size+1]; // выделяем память под this->str (+1 под ’\0’)

strcpy(str,a.str); // копирование строки

size=strlen(str);

}

string operator+(string s1, const string s2) // перегрузка операции +

{ string ss;

ss.str=new char[ss.size=strlen(s1.str)+strlen(s2.str)+1];

for(int i=0; ss.str[i]=s1.str[i]; i++); // перезапись символа ’\0’

ss.str[i]=' '; // удаление ’\0’

for(int j=0; ss.str[i+1]=s2.str[j]; i++,j++); // дозапись второй строки

return ss;

}

string &string::operator =(const string &st) // перегрузка операции =

{ if(this!=&st) // проверка, не копирование ли объекта в себя

{ delete str; // освобождаем память старой строки

str=new char[size=st.size];// выделяем память под новую строку

strcpy(str,st.str);

}

return *this;

}

void string::see()

{ cout << this->str << endl;

}

void main()

{ string s1(10,"язык"), s2(30,"программирования"), s3(30," ");

s1.see();

s2.see();

string s4=s1; // это только вызов конструктора копирования

s4.see();

string s5(s1); // прямой вызов конструктора копирования

s5.see();

s1+s2; // перегрузка + (вызов функции operator +)

s1.see();

s3=s2; // перегрузка = (вызов функции operator =)

s3.see();

s3=s1+s2; //перегрузка операции +, затем операции =

s3.see();

}

Результаты работы программы:

язык

программирования

язык

язык

программирования

язык программирования

Инструкция string s4=s1 только вызывает конструктор копирования для объекта s4. В инструкции string s5(s1) выполняется прямой вызов конструктора копирования для объекта s5. Выполнение инструкции s1+s2 приводит к двум вызовам конструктора копирования (вначале для копирования в стек объекта s2, затем s1). После этого выполняется вызов функции operator+ для инструкции s1+s2. При выходе из функции (инструкция return ss) вновь выполняется вызов конструктора копирования. При выполнении инструкции s3=s2 конструктор копирования для копирования объекта s2 в стек не вызывался, так как параметр в operator= передан (и возвращен) по ссылке.

Перегрузка оператора []

Как было отмечено выше, функция operator может быть с успехом использована для доопределения операторов С++ (в основном арифметические, логические и операторы отношения). В то же время в С++ существуют некоторые операторы, не входящие в число перечисленных, но которые полезно перегружать. К ним относится оператор []. Его необходимо перегружать с помощью компоненты-функции, использование friend- функции запрещено. Общая форма функции operator[]() имеет вид:

тип_возвр_значения имя_класса::operator [](int i)

{ тело функции}

Параметр функции необязательно должен иметь тип int, но он использован, так как operator[] в основном применяется для индексации. Рассмотрим пример программы с использованием перегрузки операции []:

#include "iostream.h"

Class massiv

{ float f[3];

public:

massiv(float i,float j,float k){f[0]=i; f[1]=j; f[2]=k;}

float operator[](int i)

{ return f[i];} // перегрузка оператора []

};

void main()

{ massiv ff(1,2,3);

double f;

int i;

cout << "введите номер индекса ";

cin >> i;

cout <<"f["<< i <<" ]= " << ff[i] << endl;

}

В примере перегруженная функция operator[]() возвращает величину элемента массива, индекс которого передан в функцию в качестве параметра. Данная программа при небольшой модификации может позволить использовать оператор[] как справа, так и слева от оператора присваивания. Для этого необходимо, чтобы функция operator[]() возвращала не элемент, а ссылку на него.

#include "iostream.h"

Class massiv

{ float f[3];

public:

massiv(float i,float j,float k){f[0]=i; f[1]=j; f[2]=k;}

float &operator[](int i) // перегрузка оператора []

{ if(i<0 || i>2) // проверка на выход за границы массива

{ cout << “Выход за пределы массива”<<endl;

exit(1);

}

return f[i];

}

};

void main()

{ massiv ff(1,2,3);

int i;

cout << "введите номер индекса ";

cin >> i;

cout <<"f["<< i <<" ]= " << ff[i] << endl;

ff[i]=5; // если функция operator не возвращает ссылку,то компиля-

// тор выдает ошибку =': left operand must be l-value

cout <<"f["<< i <<" ]= " << ff[i] << endl;

}

Рассмотрим случай когда функция operator возвращает не ссылку, а указатель на модифицируемое значение. Внесем некоторые изменения в рассмотренную выше программу.

Class massiv

{ float f[3];

public:

...

float *operator[](int i) // перегрузка оператора []

{ ...

return &f[i];

}

};

void main()

{ massiv ff(1,2,3);

...

*ff[i]=5; // функция operator возвращает указатель

cout <<"f["<< i <<" ]= " << *ff[i] << endl;

}

В приведенном примере создается ложное впечатление, что ff является вектором указателей. Поэтому использование ссылки представляется более предпочтительным.

Приведем еще один пример программы, использующей перегрузку operator[].

// перегрузка функции operator[] на примере вычисления n!

#include "iostream.h"

#include "values.h" // для определения константы MAXLONG

Class fact

{ long l;

public:

long operator[](int); // перегрузка оператора []

};

long fact::operator[](int n)

{ long l;

for (int i=0; i<=n; i++) //выбор буквы из уменьшаемой строки

if(l>MAXLONG/i)

cerr<<"ОШИБКА факториал числа "<<n<<" больше "<<MAXLONG;

else l*=i;

return l;

}

void main()

{ fact f;

int i,k;

cout << "введите число k для нахождения факториала"

cin >> k;

for (i=1; i<=k; i++)

cout << i <<"! = " << f[i] << endl;

}

Перегрузка оператора ()

Оператор вызова функции можно доопределить, как и любой другой оператор. Как и в предыдущем случае, оператор () необходимо перегружать только с помощью компоненты-функции, использование friend- функции запрещено. Общая форма функции operator()() имеет вид:

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

{ тело функции}

#include "iostream.h"

Class matr

{ int **m,a,b;

public:

matr(int,int);

~matr();

int operator()(int,int); // перегрузка оператора ()

int operator()(int); // перегрузка оператора ()

};

matr::matr(int i,int j): a(i),b(j) // конструктор

{ i=0;

m=new int *[a];

for(int k=0; k<a; k++)

{ *(m+k)=new int[b];

for(int n=0; n<b; n++)

*(*(m+k)+n)=i++; // заполнение m числами 0,1,2,3, …a*b

}

}

matr::~matr() // деструктор

{ for(int k=0; k<a; k++)

delete [] m[k]; // освобождение памяти для k-й строки

delete [] m; // освобождение памяти для всего массива

} // указателей m

int matr::operator()(int i,int j)

{ if (i<0 || i>=a || j<0 || j>=b)

{ cerr<<"выход за пределы матрицы ";

return m[0][0]; // например, при этом возврат m[0][0]

}

return m[i][j]; // возврат требуемого элемента

}

int matr::operator()(int i)

{ if (i<0 || i>=a*b)

{ cerr<<"выход за пределы массива ";

return **m; // как и выше возврат m[0][0]

}

return m[i/b][i%b]; // возврат требуемого элемента

}

void main()

{ matr mt(3,5);

cout << mt(2,3) << endl;

cout << mt(3,2) << endl; // попытка получить элемент из 3-й строки

cout << mt(3) << endl;

}

Результаты работы программы:

выход за пределы массива 0

Конструктор класса matr динамически выделяет и инициализирует память двухмерного массива. Деструктор разрушает массив автоматически при завершении программы. В классе matr реализованы две функции operator(): первая получает два аргумента (индексы в матрице), вторая получает один аргумент (порядковый номер элемента в матрице). Обе функции возвращают либо требуемый элемент, либо элемент m[0][0] при попытке выхода за пределы матрицы.

 

Перегрузка оператора ->

Оператор -> доступа к компонентам объекта через указатель на него определяется как унарный постфиксный.

Ниже приведен простой пример программы перегрузки оператора ->:

#include "iostream.h"

#include "iomanip.h"

#include "string.h"

class cls_A

{ char a[40];

public:

int b;

cls_A(char *aa,int bb): b(bb) // конструктор

{strcpy(a,aa);}

char *put_A() {return a;}

};

class cls_B

{ cls_A *p; // указатель на класс cls_A

public:

cls_B(char *aa,int bb) {p=new cls_A(aa,bb);} // конструктор

~cls_B() {delete p;} // деструктор

cls_A *operator->(){return p;} // функция перегрузки ->

};

void main()

{ cls_B ptr("перегрузка оператора -> ",2); // объект класса cls_B

cout << ptr->put_A() << setw(6) << ptr->b <<endl; // перегрузка ->

cout << (ptr.operator->())->put_A() << setw(6)

<< (ptr.operator->())->b <<endl;

cout << (*ptr.operator->()).put_A() << setw(6)

<< (*ptr.operator->()).b <<endl;

}

Результат работы программы:

перегрузка оператора -> 2

перегрузка оператора -> 2

перегрузка оператора -> 2

В приведенной программе инструкции ptr->put_A() и ptr->b приводят к перегрузке операции ->, то есть позволяют получить адрес указателя на компоненты класса cls_A для (из) объекта ptr. Таким образом, инструкция ptr->b соответствует инструкции (ptr.p)->b. Следующие далее две группы инструкций также верны, но не являются примером перегрузки оператора ->, а только приводят к явному вызову функции operator-> - компоненты класса cls_B.

В целом доопределение оператора -> позволяет использовать ptr с одной стороны как специальный указатель (в примере для класса cls_A), а с другой стороны, как объект (для класса cls_B).

Специальные указатели могут быть доопредены следующим образом:

cls_A &operator*(){return *p;}

cls_A &operator[](int index){return p[index];}

а также доопределение может быть выполнено по отношению к большинству рассмотренных ранее операций (+, ++ и др.).

 


Поделиться с друзьями:

Таксономические единицы (категории) растений: Каждая система классификации состоит из определённых соподчиненных друг другу...

Индивидуальные очистные сооружения: К классу индивидуальных очистных сооружений относят сооружения, пропускная способность которых...

Организация стока поверхностных вод: Наибольшее количество влаги на земном шаре испаряется с поверхности морей и океанов (88‰)...

Двойное оплодотворение у цветковых растений: Оплодотворение - это процесс слияния мужской и женской половых клеток с образованием зиготы...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.109 с.