Разделы презентаций


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

Содержание

Прототип перегруженного оператора задается следующей синтаксической формой:Перегрузка операторов – как и что можноВозвращаемый_тип :: оператор XX (аргументы);Вместо XX непосредственно записывается оператор, который перегружается.В С++ есть ряд операторов, которые не могут перегружаться.

Слайды и текст этой презентации

Слайд 1В качестве примеров полиморфизма в С++ мы имели дело с

перегрузкой функций. А что с перегрузкой операторов?
По сути дела каждый

оператор в С++ и представляет собой функцию. Операторы могут иметь один или два аргумента. Если аргумент один, то такой оператор называется унарным. Пример ++ или --. Если аргументов два, то мы имеем дело с бинарным оператором.
Важно! В С++ нельзя перегрузить операторы встроенных типов! Это означает, что невозможно перегрузить оператор ++ таким образом, чтобы он инкрементировал элементы одномерного массива типа int. Все операторы встроенных типов С++ и так являются перегруженными и поэтому не стоит их перегружать.
Любые операции по перегрузке операторов относятся только к пользовательским типам данных, а точнее – к типам, которые описывают объекты.
Перегрузка операторов очень похожа на перегрузку функций. Перегруженный оператор может являться членом класса или быть дружественным к нему.
В С++ нельзя перегрузить оператор отдельно от класса. Если предполагается использование перегруженного оператора за пределами класса, он должен быть объявлен как public.

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

В качестве примеров полиморфизма в С++ мы имели дело с перегрузкой функций. А что с перегрузкой операторов?По

Слайд 2Прототип перегруженного оператора задается следующей синтаксической формой:
Перегрузка операторов – как

и что можно
Возвращаемый_тип :: оператор XX (аргументы);
Вместо XX непосредственно записывается

оператор, который перегружается.

В С++ есть ряд операторов, которые не могут перегружаться. В основном это специфические операторы, используемые для работы с методами и данными классов: ., .*, ::, sizeof и ?:.

По поводу последнего оператора. Это оператор условной трансляции. Он является в С++ единственным оператором, имеющим три аргумента.
Пример

int n;

cout << (n==1 ? "One" : "Non one");

Прототип перегруженного оператора задается следующей синтаксической формой:Перегрузка операторов – как и что можноВозвращаемый_тип :: оператор XX (аргументы);Вместо

Слайд 3Нельзя перегрузить приоритет какого-либо оператора.

Это значит что вычисление (--k*2)+4

всегда будет происходить в соответствии с приоритетами в последовательности:
декрементация

k, умножение результата на 2, добавление к результату 4.
И никак нельзя перегрузить оператор +, чтобы он выполнялся первым.

Могут быть перегружены и такие операторы С++ как new и delete.

Нельзя перегрузить унарный оператор таким образом, чтобы он работал как бинарный и наоборот.

Оператор не может иметь аргументов по умолчанию

Перегрузка операторов - ограничения

Нельзя перегрузить приоритет какого-либо оператора. Это значит что вычисление (--k*2)+4 всегда будет происходить в соответствии с приоритетами

Слайд 4Для рассмотрения вариантов перегрузки различных операторов нам понадобится какой-либо пример

класса.
Будем работать, занимаясь перегрузками операторов следующего класса:
Перегрузка операторов – базовый

пример

class Nmb {
int n;
public:
Nmb(int n=0) {this->n = n; }; // конструктор
void print();
};

void Nmb::print()
{
cout << "n= " << n << endl;
}

Для рассмотрения вариантов перегрузки различных операторов нам понадобится какой-либо пример класса.Будем работать, занимаясь перегрузками операторов следующего класса:Перегрузка

Слайд 5Все унарные операторы имеют только один аргумент. При перегрузке этим

аргументом является экземпляр класса, относительно которого производится перегрузка оператора.
В случае,

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

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

class Nmb {

Nmb& operator - ();

};

Nmb& Nmb::operator - ()
{
n = -n;
return (*this);
}

Унарный оператор в качестве результирующего значения может возвращать void, однако возвращение ссылки на измененный объект позволяет использовать унарные операции в сложных записях выражений, особенно, где происходит присваивание объектов.
Перегруженный оператор может возвращать и встроенный тип. Это целесообразно, например, при перегрузке операции логического «НЕ».

Все унарные операторы имеют только один аргумент. При перегрузке этим аргументом является экземпляр класса, относительно которого производится

Слайд 6Особо следует уделить внимание перегрузке операторов инкремента и декремента, поскольку

только эти операторы имеют постфиксную и префиксную формы.
Различие этих форм

осуществляется путем использования второго фиктивного аргумента типа int. Если необходимо подчеркнуть , что оператор будет использоваться в постфиксной форме, то необходимо наличие этого аргумента, который по умолчанию устанавливается в 0. Для класса Nmb:

Перегрузка унарных операторов «++» и «--»

Nmb& Nmb::operator ++ ()
{
++n;
return (*this);
}

Nmb& Nmb::operator ++ (int)
{
n++;
return (*this);
}

Особо следует уделить внимание перегрузке операторов инкремента и декремента, поскольку только эти операторы имеют постфиксную и префиксную

Слайд 7В случае перегрузки указанных операторов как дружественных по отношению к

классу, их описание может быть выполнено как:
Перегрузка дружественных унарных операторов

«++» и «--»

class Nmb {

friend Nmb& operator ++ (Nmb&);
friend Nmb& operator ++ (Nmb&, int);

};

Nmb& operator ++ (Nmb& obj)
{
++(obj.n);
return obj;
}
Nmb& operator ++ (Nmb& obj, int)
{
(obj.n)++;
return obj;
}

В случае перегрузки указанных операторов как дружественных по отношению к классу, их описание может быть выполнено как:Перегрузка

Слайд 8Начнем со случая перегрузки операторов как членов класса. Функция реализации

бинарного оператора будет иметь только один аргумент. Объясняется это тем,

что второй аргумент по умолчанию является экземпляром этого класса.
Для начала перегрузим бинарные операторы «+» и «-» таким образом, чтобы их можно было использовать так:
единственный аргумент при перегрузке
будет иметь тип int. В обоих случаях эти
операторы должны возвращать объекты,
поскольку результатом операции
является объект, который
присваивается другому.

Перегрузка бинарных операторов

class Nmb {

Nmb operator + (const int);
Nmb operator - (const int);

};

Nmb n1, n2(5);
n2 = n1 + 5;
n1 = n2 – 3;

Nmb Nmb::operator + (const int m)
{
Nmb tmp;
tmp.n = this->n + m;
return tmp;
}
Nmb Nmb::operator - (const int m)
{
Nmb tmp;
tmp.n = this->n - m;
return tmp;
}

Начнем со случая перегрузки операторов как членов класса. Функция реализации бинарного оператора будет иметь только один аргумент.

Слайд 9Для того, чтобы не возникло никаких побочных эффектов при возврате

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

Данные операторы работают только при записях, приведенных в примерах их использования.
Если бы перегруженные операторы работали без временных объектов и возвращали бы ссылки на текущие объекты, то использование таких операторов было бы некорректным. Почему?
В случае операции n2 = n1 + 5 сначала бы сработал оператор «+», который бы изменил объект n1, а затем измененный объект присвоил бы объекту n2, т.е. произошло бы изменение сразу двух объектов.
Важно! Использовать данный оператор можно только в данной записи, когда первым операндом выступает объект-переменная, а вторым – константа. Это значит, что компилятор не пропустит записи вида n2 = 5+n1. Дело в том, что при такой записи для компилятора появляются два явных аргумента – это целое число и экземпляр класса. В этом случае при описании оператора внутри класса компилятор не пропустит эту запись и выдаст сообщение о том, что у бинарного оператора +(-) должен быть один аргумент, а не 2.

Перегрузка бинарных операторов - как

Для того, чтобы не возникло никаких побочных эффектов при возврате объекта из перегруженного оператора, необходимо наличие конструктора

Слайд 10Для решения этой проблемы можно определить перегружаемый оператор как дружественный

к данному классу. В этом случае передача ссылки на объект

в качестве второго аргумента кажется весьма оправданной!

Перегрузка бинарных операторов как дружественных

class Nmb {

friend Nmb operator + ( const int, const Nmb&);
friend Nmb operator - ( const int, const Nmb&);

};

Nmb operator + ( const int m, const Nmb &obj)
{
Nmb tmp;
tmp.n = m + obj.n
return tmp;
}

Перегрузка оператора «-» будет идентичной, за исключением одной операции вычитания внутри тела оператора.

Для решения этой проблемы можно определить перегружаемый оператор как дружественный к данному классу. В этом случае передача

Слайд 11Рассмотрим теперь перегрузку оператора расширенного присвоения, используемую в записи вида

n2 += 6 (упрощенная форма для записи n2 = n2+6)

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

Перегрузка операторов расширенного присвоения

class Nmb {

Nmb& operator += ( const int);

};

Nmb& Nmb::operator += ( const int m)
{
this->n +=m;
return (*this);
}

Рассмотрим теперь перегрузку оператора расширенного присвоения, используемую в записи вида n2 += 6 (упрощенная форма для записи

Слайд 12Мы рассматривали случаи, когда операция выполнялась над разными типами данных:

один объект Nmb, а второй – встроенный тип int.
Но можно

перегрузить бинарные операторы так, чтобы оба операнда были объектами данного класса. Для оператора сложения определение метода будет выглядеть таким образом, что:

Перегрузка бинарных операторов над объектами

class Nmb {

Nmb operator + ( const Nmb &);

};

Nmb Nmb::operator + ( const Nmb & obj)
{
Nmb tmp;
tmp.n = this->n + obj.n
return tmp;
}

Теперь становится возможной такая запись:

Nmb n1(3), n2(4), n3;
n3 =n1 + n2;

Мы рассматривали случаи, когда операция выполнялась над разными типами данных: один объект Nmb, а второй – встроенный

Слайд 13Рассмотрим теперь перегрузку оператора присваивания «=», который тоже является бинарным.
Важно!

Перегрузка не может быть выполнена в форме дружественного к классу

оператора – обязательно должен быть членом класса.
Применяется этот оператор в качестве механизма, предотвращающего создание копий и побитного копирования экземпляров класса.
Для класса Nmb оператор будет иметь один явный аргумент – константную ссылку на объект и возвращать ссылку на модифицированный объект:

Перегрузка операторов присвоения

class Nmb {

Nmb& operator = ( const Nmb &);

};

Nmb& Nmb::operator + ( const Nmb & obj)
{
this->n = obj.n
return (*this);
}

Все рассмотренные нами примеры перегрузки бинарных операторов возвращают либо объект, либо ссылку на него. В С++ нет ограничений на возвращаемый тип при перегрузке операторов.
Но так делать удобно – можно использовать операторы при описании сложных выражений
Nmb n1(3), n2, n3;
n3 =n1 = n1 + n2 +=5+6;

Рассмотрим теперь перегрузку оператора присваивания «=», который тоже является бинарным.Важно! Перегрузка не может быть выполнена в форме

Слайд 14Все операторы данных типов являются бинарными и если они перегружаются

как члены класса, то должны иметь один аргумент – объект

или ссылку на него. В качестве возвращаемого типа должен указываться один из встроенных типов данных, который говорил нам верно ли предложенное выражение. Это позволит при проверке использовать сложные выражения.
Для класса Nmb оператор «==» может быть перегружен следующим образом:

Перегрузка логических операторов и операторов отношения

class Nmb {

int operator == ( const Nmb &);

};

int Nmb::operator == ( const Nmb & obj1)
{
if (this->n == obj.n) return 1;
return 0;
}

Подобным образом перегружаются и все другие логические операторы и операторы отношений.

Все операторы данных типов являются бинарными и если они перегружаются как члены класса, то должны иметь один

Слайд 15Операторы потокового ввода/вывода «» уже являются перегруженными в

стандарте языка С++.
Для реализации ввода/вывода в С++ используются стандартные

классы. Одними из таких классов являются ostream и istream. Данные классы созданы для поддержки стандартного вывода и ввода данных соответственно. Экземпляры данных классов - cout и cin. Их можно перегружать. Но! Данные операторы не могут быть членами какого-либо класса, но могут быть дружественными классу.

Перегрузка операторов ввода/вывода

friend ostream& operator << (ostream&, Nmb& );


ostream& operator << (ostream &os, Nmb &obj)
{
os<<“n=“ << obj.n << endl;
return os;
}

Общий синтаксис перегрузки оператора вставки в поток для классов:
ostream& operator << (ostream &ссылка_на_поток, имя_класса объект)

Операторы потокового ввода/вывода «» уже являются перегруженными в стандарте языка С++. Для реализации ввода/вывода в С++ используются

Слайд 16 Форма перегрузки оператора извлечения из потока похожа на перегрузку оператора

« (istream&, Nmb& );


istream& operator

>> (istream &is, Nmb &obj)
{
cout << “Input n: “;
is >> obj.n;
return is;
}
Форма перегрузки оператора извлечения из потока похожа на перегрузку оператора « (istream&, Nmb& );…istream& operator >> (istream

Слайд 17 Для стандартных потоков данных в С++ могут быть применены специальные

операторы, называемые манипуляторами.
Манипуляторы предоставляют средства форматированного ввода/вывода. Часть стандартных марипуляторов

определа в заголовочном файле iostream, а часть – в файле iomanip.
Один из манипуляторов – endl - переход к новой строке и очистка буфера. Еще: oct, dec, hex – манипуляторы, которые отвечают за установку системы счисления для выводимых данных. Все вышеперечисленные манипуляторы работают с внутренними компонентами, в данном случае, класса ostream, и действуют до их явной отмены.
Средства работы с манипуляторами в С++ являются расширяемыми.

Перегрузка манипуляторов

Для стандартных потоков данных в С++ могут быть применены специальные операторы, называемые манипуляторами.	Манипуляторы предоставляют средства форматированного ввода/вывода.

Слайд 18Пользователь может определить свой собственный манипулятор.
Рассмотрим построение манипулятора, который записывает

в выходной поток десять символов «*».
Определение собственных манипуляторов
ostream& operator stars

(ostream& os)
{
for (int i=0; i< 10; os<<’*’, i++);
os << endl;
return os;
}

cout << stars;

Из примера видно, что данный манипулятор является простейшим – без параметров. Определение манипуляторов такого вида весьма простое – один единственный аргумент – ссылка на поток (в данном случае – выводной) и возвращаемое значение – видоизмененный поток. Данное описание является описанием простой функции.

Компилятор воспринимает запись вида cout << stars следующим образом:
Происходит разыменование перегруженного оператора «<<», результат есть адрес функции stars. Затем выполняется эта функция и после этого происходит возврат ссылки на поток и джальнейшие связанные с ним действия.

Пользователь может определить свой собственный манипулятор.Рассмотрим построение манипулятора, который записывает в выходной поток десять символов «*».Определение собственных

Слайд 19Пользователь может определить свой собственный манипулятор, у которого есть один

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

предназначенных для создания манипуляторов с аргументами. Простейшие манипуляторы могут принимать только один аргумент типа int или long. В этом специальном случае используется макрос _Smanip:

Определение манипуляторов с параметром int

#include "iomanip«

void stars(ios_base& os, int n)
{
ostream *pos = dynamic_cast(&os);
for (int i=0; i< n; (*pos)<<'*', i++); (*pos) << endl;
}
_Smanip Stars(int how_many)
{
return _Smanip (&stars, how_many);
}

cout << Stars(12);

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

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

Слайд 20При использовании параметра другого типа используется техника, которая продемонстрирована на

следующем примере:
Определение манипуляторов с параметрами
#include "iostream"
#include "iomanip"
using namespace std;
typedef char*

charp;
class money
{
private:
long value;
static char *CurM;
public:
money( long val ) { value = val; }
friend ostream& operator << ( ostream& os, money m ) {
os << m.value << money::CurM; return os;
}
static void setpic( char* szPic ) {
money::CurM = new char[strlen( szPic ) + 1];
strcpy_s( money::CurM, strlen( szPic ) + 1, szPic );
}
};
При использовании параметра другого типа используется техника, которая продемонстрирована на следующем примере:Определение манипуляторов с параметрами#include

Слайд 21Продолжение примера:
Определение манипуляторов с параметрами 2
char *money::CurM; // Static pointer

void

func( ios_base& os, char * somename )
{
money::setpic(somename);
}

_Smanip

setpic(char * somename)
{
return (_Smanip(&func, somename));
}

void main ()
{
money amt = (long)54321;
cout << setpic( "$") << "amount = " << amt << endl; // Альтернатива Br или €
}
Продолжение примера:Определение манипуляторов с параметрами 2char *money::CurM; // Static pointervoid func( ios_base& os, char * somename ){

Слайд 22В С++  имеются две возможности перегрузки операторов new() и delete()

-  локально (в пределах класса) и глобально (в пределах программы).

Эти операторы имеют правила переопределения, отличные от рассмотренных правил переопределения других операторов.
Одна из причин перегрузки этих операторов состоит в том, чтобы придать им допол-нительную семантику, например, для обеспечения выдачи диагностической информа-ции, или увеличения защищенности от ошибок. Кроме того, класс может иметь более эффективную схему распределения памяти, чем та, которая обеспечивается системой.
Оператор new можно задать в следующих формах:
<::> new <аргументы> имя_типа  <инициализирующее_выражение>;
<::> new <аргументы> имя_типа  [ ];
Параметр ”аргументы” можно использовать либо для того, чтобы различить разные версии глобальных операторов new, либо для использования их в теле функции operator. Доопределенную функцию operator  new можно объявить:
void *operator new(size_t t, <список _аргументов>);
void *operator new[](size_t t, <список _аргументов>);
Вторая форма используется для выделения памяти для массивов. Возвращаемое значение всегда должно иметь тип void *. Единственный обязательный аргумент функции operator всегда должен иметь тип size_t. При этом в функцию operator автоматически подставляется аргумент sizeof(t).


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

В С++  имеются две возможности перегрузки операторов new() и delete() -  локально (в пределах класса) и глобально

Слайд 23
Перегрузка оператора new - пример
#include "iostream“
using namespace std;

void *operator new(size_t

tip, int kol) //глобальная ф-ция operator new
{ cout

"GF 1" < return new char[tip*kol];
}
void *operator new(size_t tip,int n1,int n2) //глобальная ф-ция operator new
{ cout << "GF 2" < void *p=new char[tip*n1*n2];
return p;
}
class cls
{ char a[40];
public:
cls(char *aa){ strcpy(a,aa); }
~cls(){}
void *operator new(size_t,int);
};
Перегрузка оператора new - пример#include

Слайд 24В
Перегрузка оператора new - пример
void *cls::operator new(size_t tp,int n)

// локальная функция operator new
{ cout

return new char[tp*n];
}

void main()
{ cls obj("Overload of the operator new");
float *ptr1;
ptr1=new (5) float; // вызов 1 глобальной функции operator new
ptr1=new (2,3) float; // вызов 2 глобальной функции operator new
ptr1=new float; // вызов сиcтемной глобальной функции
cls *ptr2=new (3) cls("aa"); // вызов локальной функции operator new
// используя cls::cls("aa")
}

warning C4291: 'void *cls::operator new(size_t,int)' : no matching operator delete found; memory will not be freed if initialization throws an exception

ВПерегрузка оператора new - примерvoid *cls::operator new(size_t tp,int n)  // локальная функция operator new{ cout

Слайд 25Оператор delete разрешается доопределять только по отношению к классу. В

то же время можно заменить системную версию реализации оператора delete

на  свою.
Доопределенную функцию operator  delete  можно объявить:
void operator delete(void  *p<,size_t  t>);
void operator delete[](void *p<,size_t  t>);
Функция operator должна возвращать значение void и имеет один обязательный аргумент типа void * - указатель на область памяти, которая должна быть освобождена.

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

Оператор delete разрешается доопределять только по отношению к классу. В то же время можно заменить системную версию

Слайд 26
Перегрузка оператора delete - пример

class cls {

     void operator

delete(void *);

}

void cls::operator delete(void *p)  // локальная функция operator
{ cout

<< “LF DELETE" <   delete p;               // вызов системной функции  delete
}

void operator delete[](void *p)       // глобальная функция operator
{ cout << “GF DELETE" <   delete p;               // вызов системной функции  delete
}

Перегрузка оператора delete - пример…class cls {…      void operator delete(void *);…}void cls::operator delete(void *p)  // локальная

Слайд 27Необходимо отметить тот факт, что при реализации переопределения глобальной функции

delete в ней не должен использоваться оператор delete[], так как

это приведет к бесконечной рекурсии. При выполнении инструкции системный оператор delete ptr2 сначала вызывается локальная функция доопределения оператора delete для класса cls, а затем из нее глобальная функция переопределения delete.

Перегрузка оператора delete - использование

void main()
{ cls obj("перегрузка операторов NEW и  DELETE");
  float *ptr1;
  ptr1=new (5) float;        // вызов глобальной ф-ции  доопр. оператора new
  delete [] ptr1;                // вызов глобальной ф-ции  доопр.оператора delete
  cls *ptr2=new (10) cls("aa");  // вызов локальной функции доопределения
                                             // оператора  new  (из класса cls)
  delete ptr2;                          // вызов локальной функции   доопределения
                                             // оператора delete (из класса cls)
}

Необходимо отметить тот факт, что при реализации переопределения глобальной функции delete в ней не должен использоваться оператор

Слайд 28warning C4291: 'void *cls::operator new(size_t,int)' : no matching operator delete

found; memory will not be freed if initialization throws an

exception
Нужны добавки!

Перегрузка операторов new – delete

class cls
{ …
void operator delete(void *,int);

};

void cls::operator delete(void *p, int n) // локальная функция operator
{
cout << "LFD" < delete p; // вызов системной функции delete
}


warning C4291: 'void *cls::operator new(size_t,int)' : no matching operator delete found; memory will not be freed if

Обратная связь

Если не удалось найти и скачать доклад-презентацию, Вы можете заказать его на нашем сайте. Мы постараемся найти нужный Вам материал и отправим по электронной почте. Не стесняйтесь обращаться к нам, если у вас возникли вопросы или пожелания:

Email: Нажмите что бы посмотреть 

Что такое TheSlide.ru?

Это сайт презентации, докладов, проектов в PowerPoint. Здесь удобно  хранить и делиться своими презентациями с другими пользователями.


Для правообладателей

Яндекс.Метрика