Что такое findslide.org?

FindSlide.org - это сайт презентаций, докладов, шаблонов в формате PowerPoint.


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

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

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

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

Презентация на тему Перегрузка операций

Содержание

Для чего нужна перегрузка операцийДля некоторых типов данных естественными может оказаться использование операций над базовыми типами+= и + для конкатенации строк-- и ++ для итератороварифметические операции для векторов и комплексных чисел-> и * для умных указателей[]
Перегрузка операций Для чего нужна перегрузка операцийДля некоторых типов данных естественными может оказаться использование Перегрузка операцийДля пользовательских типов данных C++ позволяет задать собственные операцииНекоторые из них ОграниченияПриоритет операций над пользовательскими типами тот же, что и для базовых типовНельзя Пример 1Моделирование класса «Двухмерный вектор» Информация о предметной областиДля двухмерных векторов определены операции сложения, вычитания и умножения Каркас класса CVector2Dclass CVector2D {public:	CVector2D(double x0 = 0, double y0 = 0)		:x(x0), Перегрузка оператора сложения векторовПри перегрузке данного оператора принимаем во внимание следующие особенностиОператор Реализация оператора сложения внутри класса CVector2Dclass CVector2D {public:	…	CVector2D const operator +(CVector2D const& Реализация оператора сложения вне класса CVector2Dclass CVector2D {public:	…	…};CVector2D const operator +(CVector2D const& Реализация дружественного оператора сложенияclass CVector2D {public:	…	CVector2D const friend operator +(CVector2D const& vector1, Выбор предпочтительного способа перегрузкиВ данном случае предпочтительным способом является реализация оператора сложения Пример использования перегруженного оператора +class CVector2D{public:	…	CVector2D const operator +(CVector2D const& vector2)const;	…};…int main(int Реализация оператора вычитания векторовВ данном случае оператор сложения практически полностью идентичен оператору Перегрузка оператора умножения вектора и скаляраУмножение вектора на скаляр – более сложная Перегрузка оператора произведения вектора и скаляраПри перегрузке данного оператора принимаем во внимание Реализация оператора произведения вектора и скаляраclass CVector2D {public:	…	CVector2D const operator *(double scalar)const	{		// Пример использования#include “Vector2D.h”int main(int argc, char * argv[]){	CVector2D a(3.0, 2.1);	CVector2D b(4.0, 5.1);	CVector2D Реализация оператора деления вектора на скалярДля векторов также определена операция деления вектора Перегрузка присваивающих выраженийПомимо операций +, - и * могут понадобиться данные действия Реализация оператора +=class CVector2D {public:	…	CVector2D& operator +=(CVector2D const& vector)	{		x += vector.x;		y += Перегрузка операторов сравненияОператоры сравнения сравнивают значения операндов, не изменяя их, и возвращают Реализация операторов == и !=class CVector2D {public:	…	bool operator ==(CVector2D const& other)const	{		return (x Умные указатели Что такое умный указатель?Умный указатель (smart pointer) – класс (обычно шаблонный), имитирующий Исходный код класса CMyClassPtrclass CMyClassPtr{public:	CMyClassPtr(CMyClass * pMyClass):m_pMyClass(pMyClass){}	// деструктор автоматически удаляет управляемый объект	~CMyClassPtr(){delete Пример использования класса CMyClassPtrclass CMyClass{public:	void DoSomethingImportant()	{		…	}};class CMyClassPtr{	…};int main(int argc, char * argv[]){	CMyClassPtr Стандартные умные указателиБиблиотека STL содержит шаблонынй класс auto_ptr, обеспечивающий политику владения объектом Перегрузка унарного плюса и минуса Унарный плюс и унарный минусПомимо инфиксных операций бинарного плюса и бинарного минуса Пример перегрузки унарного минусаclass CVector2D{public:	…	CVector2D const operator –()const;	{		return CVector2D(-x, -y);	}	CVector2D const operator +()const;	{		// возвращаем копию		return *this;	}	…}; Перегрузка оператора присваивания Автоматически сгенерированный оператор присваиванияОператор присваивания, как и конструктор копирования может быть автоматически Когда нужен собственный оператор присваивания?Как правило, во всех случаях, когда классу нужен Пример некорректной реализации присваивания строкclass CMyString{public:	…	CMyString& operator =(CMyString const& other)	{		delete [] m_pChars;		m_pChars Пример корректной реализации присваивания строкclass CMyString{public:	…	CMyString& operator =(CMyString const& other)	{		if (&other != Запрет операции присваиванияВ ряде случае операция присваивания объектов может быть нежелательнойС экземпляром Перегрузка оператора индексации [] Оператор индексацииЯвляется унарным оператором, обычно использующимся для доступа к элементам контейнераВ качестве Пример: доступ к символам строкиclass CMyString{public:	…	// оператор индексированного доступа для чтения	const char Перегрузка операций инкремента и декремента Особенности перегрузки операторов инкремента и декрементаДля некоторых типов данных могут быть определены Перегрузка префиксной формы инкремента и декрементаПрефиксная операция выполняет модификацию объекта и возвращает Перегрузка постфиксной формы инкремента и декрементаПостфиксная операция выполняет модификацию объекта и возвращает Пример - счетчикclass CCounter{public:	explicit CCounter(unsigned maxValue, counter = 0)		:m_maxValue(maxValue), m_counter(counter){}	unsigned GetValue()const{return m_counter;}	unsigned Перегрузка операторов потокового ввода-вывода Потоки ввода-вывода и операторы ввода-вывода в потокВ STL операции ввода-вывода выполняются при Перегрузка операторов потокового ввода-выводаПерегрузка операторов форматированного ввода-вывода в потоки STL не может Перегрузка оператора вывода в поток для класса «Счетчик»// выводим информацию о счетчике Перегрузка оператора чтения из потока для класса «Счетчик»template std::basic_istream& operator>>(	std::basic_istream& stream, CCounter Пример использования перегруженных операций ввода-вывода#include #include “Counter.h”int main(int argc, char* argv[]){	CCounter c(10);	// Перегрузка операторов приведения типов Перегрузка оператора приведения типаИногда возникает необходимость выполнить приведение одного пользовательского типа к Пример: приведение счетчика к unsigned intclass CCounter{public:…	operator unsigned int()const	{		return m_counter;	}…};void f(unsigned value);int Пример: приведение строкового объекта к const char*class CMyString{public:…	operator const char*()const	{		return m_pChars;	}…};void f(const Не переусердствуйте!Перегружать операторы приведения типов следует осторожно, т.к. из-за неявного приведения типов Пример нежелательного приведения типов#include class CMyString{public:…	CMyString const operator+ (const char*)const;	operator const char*()const	{		return Перегрузка оператора (). Функторы ФункторыФунктор (или объект функции, function object) – объект, для которого определен оператор Пример функтора#include class CAddValue{public:	CAddValue(int value):m_value(value)	{	}	void operator()(int & arg)const	{		arg += m_value;	}private:	int m_value;};int main(int Пример: использование функтора совместно с алгоритмами STL#include #include #include Int main(int argc, char* argv[]){	std::vector arr;	arr.push_back(10);	arr.push_back(20);	arr.push_back(30);	std::cout Использование состояния функтораФунктор, в отличие от функции, обладает состояниемГлобальные и статические переменные Пример изменения состояния функтора при каждом его вызове#include class CAddValue{public:	CAddValue(int value, int Пример использования функтора с изменяющимся состоянием#include #include #include int main(int argc, char* argv[]){	std::vector arr;	arr.push_back(10);	arr.push_back(20);	arr.push_back(30);	std::cout Пример: генератор псевдослучайных чиселclass CRandomGenerator{public:	CRandomGenerator(unsigned modulus, unsigned seed = 0, 				unsigned multiplier
Слайды презентации

Слайд 2 Для чего нужна перегрузка операций
Для некоторых типов данных

Для чего нужна перегрузка операцийДля некоторых типов данных естественными может оказаться

естественными может оказаться использование операций над базовыми типами
+= и

+ для конкатенации строк
-- и ++ для итераторов
арифметические операции для векторов и комплексных чисел
-> и * для умных указателей
[] для массивов и ассоциативных контейнеров
() для функторов (объектов функций)
= для классов с собственным конструктором копирования
операции сравнения для строк и других типов

Слайд 3 Перегрузка операций
Для пользовательских типов данных C++ позволяет задать

Перегрузка операцийДля пользовательских типов данных C++ позволяет задать собственные операцииНекоторые из

собственные операции
Некоторые из них всегда определяются внутри класса
=, +=,

-=, *= и т.п.
Некоторые – снаружи
Как правило, операции, в которых применяются базовые типы
Синтаксис:
<тип> operator X(параметры)

Слайд 4 Ограничения
Приоритет операций над пользовательскими типами тот же, что

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

и для базовых типов
Нельзя переопределить операцию точка (.) и

sizeof
Бинарные операции остаются бинарными
Унарные - унарными

Слайд 5 Пример 1
Моделирование класса «Двухмерный вектор»

Пример 1Моделирование класса «Двухмерный вектор»

Слайд 6 Информация о предметной области
Для двухмерных векторов определены операции

Информация о предметной областиДля двухмерных векторов определены операции сложения, вычитания и

сложения, вычитания и умножения на скаляр, а также операции

проверки на равенство (и неравенство)
При моделировании класса векторов весьма удобно будет перегрузить соответствующие им арифметические операции
При перегрузке операций следует руководствоваться:
Особенностями данных операций в предметной области
Архитектурой класса
Требованиями и ограничениями языка C++
Здравым смыслом

Слайд 7 Каркас класса CVector2D
class CVector2D
{
public:
CVector2D(double x0 = 0,

Каркас класса CVector2Dclass CVector2D {public:	CVector2D(double x0 = 0, double y0 =

double y0 = 0)
:x(x0), y(y0)
{
}

// методы и операции над

векторами

// данные объявляем публичными, т.к. в данном конкретном случае
// нет необходимости создавать для них методы
double x, y;
};

Слайд 8 Перегрузка оператора сложения векторов
При перегрузке данного оператора принимаем

Перегрузка оператора сложения векторовПри перегрузке данного оператора принимаем во внимание следующие

во внимание следующие особенности
Оператор сложения является бинарным оператором
Оба аргумента

оператора сложения являются двухмерными векторами, значения которых не изменяются во время его выполнения
Имеет смысл передавать по константной ссылке
Оператор сложения векторов возвращает новый константный вектор, координаты которого – суммы соответствующих координат аргументов
Константность обязательна, чтобы не допустить конструкции вида: (vector1 + vector2) = vector3; (для целых и действительных чисел данная операция также запрещена)
Оператор сложения векторов можно реализовать тремя способами:
Как оператор, объявленный внутри класса
В этом случае левым аргументом оператора будет являться текущий экземпляр класса, а правый аргумент будет передаваться через единственный параметр
Как оператор, объявленный вне класса
В этом случае оператор будет принимать два аргумента
Как дружественный оператор, объявленный вне класса
Отличается от предыдущего способа возможностью доступа к приватным и защищенным методам и данным класса

Слайд 9 Реализация оператора сложения внутри класса CVector2D
class CVector2D
{
public:

CVector2D

Реализация оператора сложения внутри класса CVector2Dclass CVector2D {public:	…	CVector2D const operator +(CVector2D

const operator +(CVector2D const& vector2)const
{
// левым аргументом является текущий

экземпляр класса
// правым – единственный аргумент vector2
return CVector2D(x + vector2.x, y + vector2.y);
}

};

Слайд 10 Реализация оператора сложения вне класса CVector2D
class CVector2D
{
public:


};

CVector2D

Реализация оператора сложения вне класса CVector2Dclass CVector2D {public:	…	…};CVector2D const operator +(CVector2D

const operator +(CVector2D const& vector1, CVector2D const& vector2)
{
return CVector2D(vector1.x +

vector2.x, vector1.y + vector2.y);
}


Слайд 11 Реализация дружественного оператора сложения
class CVector2D
{
public:

CVector2D const friend

Реализация дружественного оператора сложенияclass CVector2D {public:	…	CVector2D const friend operator +(CVector2D const&

operator +(CVector2D const& vector1, CVector2D const& vector2);


};

CVector2D const operator +(CVector2D

const& vector1, CVector2D const& vector2)
{
return CVector2D(vector1.x + vector2.x, vector1.y + vector2.y);
}


Слайд 12 Выбор предпочтительного способа перегрузки
В данном случае предпочтительным способом

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

является реализация оператора сложения внутри класса
Естественность данного оператора для

класса векторов
Возможность внесения изменений в исходный код класса CVector2D
Наиболее краткая форма записи

Слайд 13 Пример использования перегруженного оператора +
class CVector2D
{
public:

CVector2D const operator

Пример использования перегруженного оператора +class CVector2D{public:	…	CVector2D const operator +(CVector2D const& vector2)const;	…};…int

+(CVector2D const& vector2)const;

};



int main(int argc, char * argv[])
{
CVector2D a(3.0,

5.8);
CVector2D b(7.3, 8.8);

CVector2D c = a + b + CVector2D(3, 9);

return 0;
}


Слайд 14 Реализация оператора вычитания векторов
В данном случае оператор сложения

Реализация оператора вычитания векторовВ данном случае оператор сложения практически полностью идентичен

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

также внутри класса CVector2D

class CVector2D
{
public:

CVector2D const operator -(CVector2D const& vector2)const
{
// левым аргументом является текущий экземпляр класса
// правым – единственный аргумент vector2
return CVector2D(x - vector2.x, y - vector2.y);
}

};


Слайд 15 Перегрузка оператора умножения вектора и скаляра
Умножение вектора на

Перегрузка оператора умножения вектора и скаляраУмножение вектора на скаляр – более

скаляр – более сложная операция, т.к. использует разные типы

аргументов и является коммутативной
Один из аргументов – вектор (CVector2D), второй – скаляр (double)
Из-за коммутативности данной операции существуют 2 версии данного оператора:
Вектор * Скаляр
Скаляр * Вектор

Слайд 16 Перегрузка оператора произведения вектора и скаляра
При перегрузке данного

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

оператора принимаем во внимание следующие особенности
Данный оператор является бинарным

оператором с параметрами различных типов
Оператор возвращает новый константный вектор, координаты которого – произведения координат исходного вектора на скаляр
Константность обязательна, чтобы не допустить конструкции вида: (vector1 * 3) = vector2; (10 * vector3) = vector4;
Существуют две версии данного оператора
Операция умножения вектора на скаляр
В нашем случае перегружается внутри класса аналогично оператору сложения и вычитания
Операция умножения скаляра на вектор
Данная операция не может быть перегружена внутри класса, т.к. левый аргумент (скаляр) имеет тип, отличный от класса CVector2D
В данном случае следует перегрузить его обычным образом вне класса
Нет необходимости в создании дружественной операции, т.к. операции не требуется доступ к приватным данным и методам класса CVector2D

Слайд 17 Реализация оператора произведения вектора и скаляра
class CVector2D
{
public:

CVector2D

Реализация оператора произведения вектора и скаляраclass CVector2D {public:	…	CVector2D const operator *(double

const operator *(double scalar)const
{
// левым аргументом является текущий экземпляр

класса
// правым – единственный аргумент vector2
return CVector2D(x * scalar, y * scalar);
}


};

CVector2D const operator *(double scalar, CVector2D const& vector)
{
return CVector2D(scalar * vector.x, scalar *vector.y);
}

Слайд 18 Пример использования
#include “Vector2D.h”

int main(int argc, char * argv[])
{
CVector2D

Пример использования#include “Vector2D.h”int main(int argc, char * argv[]){	CVector2D a(3.0, 2.1);	CVector2D b(4.0,

a(3.0, 2.1);
CVector2D b(4.0, 5.1);

CVector2D c = a * 3.4;
CVector2D

d = 8.4 * b;

CVector2D e = (a + b) * 3 + (c – d) * 4;

return 0;
}

Слайд 19 Реализация оператора деления вектора на скаляр
Для векторов также

Реализация оператора деления вектора на скалярДля векторов также определена операция деления

определена операция деления вектора на скаляр
Результатом данной операции является

вектор с координатами, равными отношению координат исходного вектора к скаляру
Данная операция перегружается внутри класса аналогично операции умножения вектора на скаляр

class CVector2D
{
public:

CVector2D const operator /(double scalar)const
{
return CVector2D(x / scalar, y / scalar);
}

};


Слайд 20 Перегрузка присваивающих выражений
Помимо операций +, - и *

Перегрузка присваивающих выраженийПомимо операций +, - и * могут понадобиться данные

могут понадобиться данные действия в составе операции присваивания:
vector1 +=

vector2;
vector3 *= 3.8;
vector4 -= vector1;
Особенностью данных операций является то, что они модифицируют операнд в левой части, но не модифицируют операнд в правой
Кроме того, важно, чтобы они возвращали ссылку на левый операнд, чтобы можно было использовать выражения, допустимые для встроенных типов данных:
(a += b) /= c;

Слайд 21 Реализация оператора +=
class CVector2D
{
public:

CVector2D& operator +=(CVector2D const&

Реализация оператора +=class CVector2D {public:	…	CVector2D& operator +=(CVector2D const& vector)	{		x += vector.x;		y

vector)
{
x += vector.x;
y += vector.y;

return *this;
}
// операторы *=, /=,

-= перегружаются аналогичным образом

};

Слайд 22 Перегрузка операторов сравнения
Операторы сравнения сравнивают значения операндов, не

Перегрузка операторов сравненияОператоры сравнения сравнивают значения операндов, не изменяя их, и

изменяя их, и возвращают результат типа bool, соответствующий результату

сравнения
Для двухмерных векторов такими операциями являются операторы:
==
!=

Слайд 23 Реализация операторов == и !=
class CVector2D
{
public:

bool operator

Реализация операторов == и !=class CVector2D {public:	…	bool operator ==(CVector2D const& other)const	{		return

==(CVector2D const& other)const
{
return (x == other.x) && (y ==

other.y);
}

bool operator !=(CVector2D const& other)const
{
return (x != other.x) || (y != other.y);
}

};

Слайд 24 Умные указатели

Умные указатели

Слайд 25 Что такое умный указатель?
Умный указатель (smart pointer) –

Что такое умный указатель?Умный указатель (smart pointer) – класс (обычно шаблонный),

класс (обычно шаблонный), имитирующий интерфейс обычного указателя и добавляющий

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

Слайд 26 Исходный код класса CMyClassPtr
class CMyClassPtr
{
public:
CMyClassPtr(CMyClass * pMyClass):m_pMyClass(pMyClass){}
// деструктор

Исходный код класса CMyClassPtrclass CMyClassPtr{public:	CMyClassPtr(CMyClass * pMyClass):m_pMyClass(pMyClass){}	// деструктор автоматически удаляет управляемый

автоматически удаляет управляемый объект
~CMyClassPtr(){delete m_pMyClass;}

CMyClass* operator->()
{
assert(m_pMyClass != NULL);
return m_pMyClass;
}

CMyClass&

operator*()
{
assert(m_pMyClass != NULL);
return * m_pMyClass;
}
private:
// запрещаем копирование и присваивание указателей CMyClassPtr
CMyClassPtr(CMyClassPtr const&);
CMyClassPtr& operator=(CMyClassPtr const&);

CMyClass *m_pMyClass;
};

Слайд 27 Пример использования класса CMyClassPtr
class CMyClass
{
public:
void DoSomethingImportant()
{

}
};

class CMyClassPtr
{

};

int main(int

Пример использования класса CMyClassPtrclass CMyClass{public:	void DoSomethingImportant()	{		…	}};class CMyClassPtr{	…};int main(int argc, char *

argc, char * argv[])
{
CMyClassPtr pMyClass(new CMyClass());
if (…)
{
pMyClass->DoSomethingImportant();
}
else
{
return 1; // нет

нужды в вызове delete (это сделает умный указатель CMyClassPtr)
}
return 0;
}

Слайд 28 Стандартные умные указатели
Библиотека STL содержит шаблонынй класс auto_ptr,

Стандартные умные указателиБиблиотека STL содержит шаблонынй класс auto_ptr, обеспечивающий политику владения

обеспечивающий политику владения объектом в динамической памяти
Недостаток: нельзя использовать

в составе контейнеров STL (а также во многих других контейнерах)
Библиотека boost предлагает шаблонные классы shared_ptr, scoped_ptr и intrusive_ptr, предоставляющие различные способы владения и управления объектом
shared_ptr, например, основывается на подсчете количества ссылок на объект и может использоваться в составе контейнеров STL

Слайд 29 Перегрузка унарного плюса и минуса

Перегрузка унарного плюса и минуса

Слайд 30 Унарный плюс и унарный минус
Помимо инфиксных операций бинарного

Унарный плюс и унарный минусПомимо инфиксных операций бинарного плюса и бинарного

плюса и бинарного минуса есть их унарные префиксные версии
Их

также можно при желании перегрузить (всеми тремя способами)
Наиболее предпочтительный – перегрузка внутри класса
В этом случае текущий экземпляр класса считается аргументом данного оператора

Слайд 31 Пример перегрузки унарного минуса
class CVector2D
{
public:

CVector2D const operator –()const;
{
return

Пример перегрузки унарного минусаclass CVector2D{public:	…	CVector2D const operator –()const;	{		return CVector2D(-x, -y);	}	CVector2D const operator +()const;	{		// возвращаем копию		return *this;	}	…};

CVector2D(-x, -y);
}

CVector2D const operator +()const;
{
// возвращаем копию
return *this;
}


};


Слайд 32 Перегрузка оператора присваивания

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

Слайд 33 Автоматически сгенерированный оператор присваивания
Оператор присваивания, как и конструктор

Автоматически сгенерированный оператор присваиванияОператор присваивания, как и конструктор копирования может быть

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

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

Слайд 34 Когда нужен собственный оператор присваивания?
Как правило, во всех

Когда нужен собственный оператор присваивания?Как правило, во всех случаях, когда классу

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

сводится к обычному копированию полей класса
Оператор присваивания должен возвращать ссылку на левый операнд, чтобы были возможны следующие выражения, допустимые для встроенных типов:
if ((a = b) == c) {…}
Оператор присваивания должен корректно обрабатывать некоторые особенные ситуации
Например, присваивание самому себе не должно приводить к порче данных
Наиболее надежный способ – использовать конструктор копирования для создания копии

Слайд 35 Пример некорректной реализации присваивания строк
class CMyString
{
public:

CMyString& operator =(CMyString

Пример некорректной реализации присваивания строкclass CMyString{public:	…	CMyString& operator =(CMyString const& other)	{		delete []

const& other)
{
delete [] m_pChars;
m_pChars = new char[other.m_length + 1];
memcpy(m_pChars,

other.m_pChars, m_length + 1);
m_length = other.m_length;
return *this;
}

private:
char * m_pChars;
size_t m_length;
};

Некорректная работа оператора в случае самоприсваивания:

CMyString s(“some string”);
s = s;


Слайд 36 Пример корректной реализации присваивания строк
class CMyString
{
public:

CMyString& operator =(CMyString

Пример корректной реализации присваивания строкclass CMyString{public:	…	CMyString& operator =(CMyString const& other)	{		if (&other

const& other)
{
if (&other != this)
{
CMyString tmpCopy(other);
std::swap(m_pChars, tmpCopy.m_pChars);
std::swap(m_length, tmpCopy.m_length);
}
return *this;
}
//

сходным образом перегружаем операторы
CMyString& operator +=(CMyString const& other);
CMyString& operator =(const char* pChars);
CMyString& operator +=(const char* pChars);

private:
char * m_pChars;
size_t m_length;
};

Слайд 37 Запрет операции присваивания
В ряде случае операция присваивания объектов

Запрет операции присваиванияВ ряде случае операция присваивания объектов может быть нежелательнойС

может быть нежелательной
С экземпляром объекта связываются какие-то внешние объекты,

например, файловый дескриптор или сетевое соединение
Операцию присваивания для объектов можно запретить, объявив оператор присваивания в приватной области класса
Реализацию можно при этом не писать

Слайд 38 Перегрузка оператора индексации []

Перегрузка оператора индексации []

Слайд 39 Оператор индексации
Является унарным оператором, обычно использующимся для доступа

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

к элементам контейнера
В качестве типа индекса может использоваться произвольный

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

Слайд 40 Пример: доступ к символам строки
class CMyString
{
public:

// оператор индексированного

Пример: доступ к символам строкиclass CMyString{public:	…	// оператор индексированного доступа для чтения	const

доступа для чтения
const char operator[](unsigned index)const
{
assert(index < m_length);
return m_pChars[index];
}

//

оператор индексированного доступа для записи
char & operator[](unsigned index)
{
assert(index < m_length);
return m_pChars[index];
}

private:
char * m_pChars;
size_t m_length;
};

Слайд 41 Перегрузка операций инкремента и декремента

Перегрузка операций инкремента и декремента

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

Особенности перегрузки операторов инкремента и декрементаДля некоторых типов данных могут быть

данных могут быть определены операции инкремента и декремента
Итераторы, счетчики
Операторы

инкремента и декремента являются унарными операциями
Префиксные и постфиксные версии данных операторов имеют различную семантику и перегружаются по-разному

Слайд 43 Перегрузка префиксной формы инкремента и декремента
Префиксная операция выполняет

Перегрузка префиксной формы инкремента и декрементаПрефиксная операция выполняет модификацию объекта и

модификацию объекта и возвращает ссылку на измененное значение объекта
Возвращается

ссылка, т.к. измененный результат может в дальнейшем быть модифицирован, как в случае с оператором ++ для встроенных типов данных:
++counter += n;
Синтаксис префиксной формы операторов:
Type& operator++()
Type& operator--()

Слайд 44 Перегрузка постфиксной формы инкремента и декремента
Постфиксная операция выполняет

Перегрузка постфиксной формы инкремента и декрементаПостфиксная операция выполняет модификацию объекта и

модификацию объекта и возвращает временную константную копию объекта до

модификации
Копия должна быть константной, чтобы не допустить операций вроде:
counter++ -= 3;
Синтаксис постфиксной формы операторов:
Type const operator++(int)
Type const operator--(int)
Целочисленный параметр фактически не используется и служит лишь для различия от префиксной формы
С точки зрения здравого смысла постфиксную форму операторов инкремента и декремента следует основывать на их префиксной форме

Слайд 45 Пример - счетчик
class CCounter
{
public:
explicit CCounter(unsigned maxValue, counter =

Пример - счетчикclass CCounter{public:	explicit CCounter(unsigned maxValue, counter = 0)		:m_maxValue(maxValue), m_counter(counter){}	unsigned GetValue()const{return

0)
:m_maxValue(maxValue), m_counter(counter){}
unsigned GetValue()const{return m_counter;}
unsigned GetMaxValue()const{return m_maxValue;}

CCounter& operator++()
{
++m_counter;
if (m_counter >=

m_maxValue)
{
m_counter = 0;
}
return *this;
}
CCounter const operator++(int) // постфиксная форма инкремента
{
// создаем копию, выполняем предынкремент и возвращаем копию
CCounter tmpCopy(*this);
++*this;
return tmpCopy;
}
private:
unsigned m_maxValue, m_counter;
};

Конструктор может быть помечен как явный при помощи ключевого слова explicit , чтобы запретить возможность его неявного вызова в ситуациях, вроде следующих:

CCounter counter(20, 5);
counter = 10;
эквивалентно:
CCounter counter(20, 5);
counter = CCounter(10, 0);


Слайд 46 Перегрузка операторов потокового ввода-вывода

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

Слайд 47 Потоки ввода-вывода и операторы ввода-вывода в поток
В STL

Потоки ввода-вывода и операторы ввода-вывода в потокВ STL операции ввода-вывода выполняются

операции ввода-вывода выполняются при помощи потоков данных
Поток – специальный

объект, свойства которого определяются классом
Вывод – запись данных в поток
Ввод – чтение данных из потока
cin и cout – глобальные объекты потоков ввода и вывода
Для потоков ввода и вывода определены операторы << и >> для каскадной форматированных операций записи данных в поток и чтения данных из потока
cout << 3;
cin >> var;

Слайд 48 Перегрузка операторов потокового ввода-вывода
Перегрузка операторов форматированного ввода-вывода в

Перегрузка операторов потокового ввода-выводаПерегрузка операторов форматированного ввода-вывода в потоки STL не

потоки STL не может быть выполнена внутри самих классов

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

Слайд 49 Перегрузка оператора вывода в поток для класса «Счетчик»

//

Перегрузка оператора вывода в поток для класса «Счетчик»// выводим информацию о

выводим информацию о счетчике в виде [counter/maxValue]
// в произвольный

поток вывода
template
std::basic_ostream& operator<<(
std::basic_ostream& stream, CCounter const& counter)
{
stream << "[" << counter.GetValue() << "/«
<< counter.GetMaxValue() << "]";
return stream;
}

Слайд 50 Перегрузка оператора чтения из потока для класса «Счетчик»
template

Перегрузка оператора чтения из потока для класса «Счетчик»template std::basic_istream& operator>>(	std::basic_istream& stream,


std::basic_istream& operator>>(
std::basic_istream& stream, CCounter & counter)
{
std::streamoff pos =

stream.tellg();

unsigned maxValue = 0;
unsigned currentValue = 0;
if (
(stream.get() == '[') && (stream >> currentValue) &&
(stream.get() == '/') && (stream >> maxValue) &&
(stream.get() == ']')
)
{
counter = CCounter(maxValue, currentValue);
return stream;
}

stream.seekg(pos);
stream.setstate(std::ios_base::failbit | stream.rdstate());

return stream;
}

Слайд 51 Пример использования перегруженных операций ввода-вывода
#include
#include “Counter.h”

int main(int

Пример использования перегруженных операций ввода-вывода#include #include “Counter.h”int main(int argc, char* argv[]){	CCounter

argc, char* argv[])
{
CCounter c(10);

// считывает данные о счетчике из

стандартного ввода в формате:
// [counter/maxValue]
cin >> c;

// выводит данные о счетчике в стандартный вывод в формате:
// [counter/maxValue]
cout << c;

return 0;
}


Слайд 52 Перегрузка операторов приведения типов

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

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

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

одного пользовательского типа к другому пользовательскому или встроенному типу

данных. Например:
Приведение CMyString к const char*
Приведение CСounter к unsigned int
Приведение CDateTime к CTime
Язык C++ позволяет в таких случаях обойтись без введения дополнительных методов, вроде GetStringData(), GetTimer(), GetTime() при помощи операторов приведения типа
Синтаксис оператора приведения к типу Type:
operator Type()[const]

Слайд 54 Пример: приведение счетчика к unsigned int
class CCounter
{
public:

operator unsigned

Пример: приведение счетчика к unsigned intclass CCounter{public:…	operator unsigned int()const	{		return m_counter;	}…};void f(unsigned

int()const
{
return m_counter;
}

};

void f(unsigned value);

int main(int argc, char* argv[])
{
CCounter c(10);

f(c); //

будет вызван оператор приведения к типу unsigned int

unsigned v = c; // аналогично

return 0;
}

Слайд 55 Пример: приведение строкового объекта к const char*
class CMyString
{
public:

operator

Пример: приведение строкового объекта к const char*class CMyString{public:…	operator const char*()const	{		return m_pChars;	}…};void

const char*()const
{
return m_pChars;
}

};

void f(const char* s);

int main(int argc, char*

argv[])
{
CMyString message(“Hello, world”);

f(message); // будет вызван оператор приведения к const char*

return 0;
}

Слайд 56 Не переусердствуйте!
Перегружать операторы приведения типов следует осторожно, т.к.

Не переусердствуйте!Перегружать операторы приведения типов следует осторожно, т.к. из-за неявного приведения

из-за неявного приведения типов иногда возможны нежелательные последствия
Не случайно

в классе std::string вместо оператора приведения к const char*, реализовали специальный метод c_str()

Слайд 57 Пример нежелательного приведения типов
#include

class CMyString
{
public:

CMyString const operator+

Пример нежелательного приведения типов#include class CMyString{public:…	CMyString const operator+ (const char*)const;	operator const

(const char*)const;
operator const char*()const
{
return m_pChars;
}

};
int main(int argc, char* argv[])
{
CMyString

msg(“5432”);

// допустим, что мы забыли заключить 1 в кавычки для склейки строк
std::cout << (msg + 1);
// фактически вызвав std::cout << (static_cast(msg) + 1);
// поэтому будет выведено «432» вместо «54321»
return 0;
}

Слайд 58 Перегрузка оператора (). Функторы

Перегрузка оператора (). Функторы

Слайд 59 Функторы
Функтор (или объект функции, function object) – объект,

ФункторыФунктор (или объект функции, function object) – объект, для которого определен

для которого определен оператор ()
Преимущества функторов перед обычными функциями
Наличие

состояния у функтора
Объект функции обладает некоторым типом и может выступать в качестве специализации шаблонов
Обычно функтор работает быстрее указателя на функцию

Слайд 60 Пример функтора
#include

class CAddValue
{
public:
CAddValue(int value):m_value(value)
{
}
void operator()(int & arg)const
{
arg

Пример функтора#include class CAddValue{public:	CAddValue(int value):m_value(value)	{	}	void operator()(int & arg)const	{		arg += m_value;	}private:	int m_value;};int

+= m_value;
}
private:
int m_value;
};

int main(int argc, char* argv[])
{
int value =

10;

CAddValue f(5);
std::cout << "Value before applying the functor: " << value << std::endl;
f(value);
std::cout << "Value after applying the functor: " << value << std::endl;

return 0;
}

Value before applying the functor: 10
Value after applying the functor: 15


Слайд 61 Пример: использование функтора совместно с алгоритмами STL
#include
#include

Пример: использование функтора совместно с алгоритмами STL#include #include #include Int main(int argc, char* argv[]){	std::vector arr;	arr.push_back(10);	arr.push_back(20);	arr.push_back(30);	std::cout


#include

Int main(int argc, char* argv[])
{
std::vector arr;
arr.push_back(10);
arr.push_back(20);
arr.push_back(30);

std::cout

array: " << std::endl;
std::copy(arr.begin(), arr.end(), std::ostream_iterator(std::cout, ","));

std::for_each(arr.begin(), arr.end(), CAddValue(5));

std::cout << std::endl << "Processed array: " << std::endl;
std::copy(arr.begin(), arr.end(), std::ostream_iterator(std::cout, ","));

std::cout << std::endl;

return 0;
}

Вывод массива в поток вывода

Применение функтора к элементам массива

Original array:
10,20,30,
Processed array:
15,25,35,


Слайд 62 Использование состояния функтора
Функтор, в отличие от функции, обладает

Использование состояния функтораФунктор, в отличие от функции, обладает состояниемГлобальные и статические

состоянием
Глобальные и статические переменные функций в расчет не берем
Состояние

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

Слайд 63 Пример изменения состояния функтора при каждом его вызове
#include

Пример изменения состояния функтора при каждом его вызове#include class CAddValue{public:	CAddValue(int value,



class CAddValue
{
public:
CAddValue(int value, int delta = 0)
:m_value(value)
,m_delta(delta)
{
}
// отметим, что

оператор объявлен как неконстантный
void operator()(int & arg)
{
arg += m_value;
m_value += m_delta;
}
private:
int m_value;
int m_delta;
};

Слайд 64 Пример использования функтора с изменяющимся состоянием
#include
#include
#include

Пример использования функтора с изменяющимся состоянием#include #include #include int main(int argc, char* argv[]){	std::vector arr;	arr.push_back(10);	arr.push_back(20);	arr.push_back(30);	std::cout



int main(int argc, char* argv[])
{
std::vector arr;
arr.push_back(10);
arr.push_back(20);
arr.push_back(30);

std::cout

" << std::endl;
std::copy(arr.begin(), arr.end(), std::ostream_iterator(std::cout, ","));

std::for_each(arr.begin(), arr.end(), CAddValue(5, 2));

std::cout << std::endl << "Processed array: " << std::endl;
std::copy(arr.begin(), arr.end(), std::ostream_iterator(std::cout, ","));

std::cout << std::endl;

return 0;
}

Original array:
10,20,30,
Processed array:
15,27,39,


  • Имя файла: peregruzka-operatsiy.pptx
  • Количество просмотров: 134
  • Количество скачиваний: 0