Типы сооружений для обработки осадков: Септиками называются сооружения, в которых одновременно происходят осветление сточной жидкости...
История создания датчика движения: Первый прибор для обнаружения движения был изобретен немецким физиком Генрихом Герцем...
Топ:
Характеристика АТП и сварочно-жестяницкого участка: Транспорт в настоящее время является одной из важнейших отраслей народного хозяйства...
Эволюция кровеносной системы позвоночных животных: Биологическая эволюция – необратимый процесс исторического развития живой природы...
Выпускная квалификационная работа: Основная часть ВКР, как правило, состоит из двух-трех глав, каждая из которых, в свою очередь...
Интересное:
Инженерная защита территорий, зданий и сооружений от опасных геологических процессов: Изучение оползневых явлений, оценка устойчивости склонов и проектирование противооползневых сооружений — актуальнейшие задачи, стоящие перед отечественными...
Наиболее распространенные виды рака: Раковая опухоль — это самостоятельное новообразование, которое может возникнуть и от повышенного давления...
Мероприятия для защиты от морозного пучения грунтов: Инженерная защита от морозного (криогенного) пучения грунтов необходима для легких малоэтажных зданий и других сооружений...
Дисциплины:
2017-11-27 | 375 |
5.00
из
|
Заказать работу |
|
|
При перегрузке унарной операции функция 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 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!