Классы. Дружественные функции. Перегрузка операторов

Содержание

Слайд 2

Друзья классов

Дружественная функция – это функция, которая, не являясь компонентом класса, имеет

Друзья классов Дружественная функция – это функция, которая, не являясь компонентом класса,
доступ к его защищенным и собственным компонентам.
Такая функция должна быть описана в теле класса со спецификатором friend

Слайд 3

Пример использования friend - функции

Пример использования friend - функции

Слайд 4

Пример использования friend - функции

Пример использования friend - функции

Слайд 5

friend - функции

-Функция set описана в классе pair как дружественная и определена

friend - функции -Функция set описана в классе pair как дружественная и
как обычная глобальная функция (вне класса, без указания его имени, без операции ‘ : : ‘ и без спецификатора friend).
-Дружественная функция при вызове не получает указателя this.
-Объекты класса должны передаваться дружественной функции только через параметр.
По ссылке, по значению, по адресу.

Слайд 6

friend - функции

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

friend - функции Итак, дружественная функция: – не может быть компонентной функцией
класса, по отношению к которому определяется как дружественная;
– может быть глобальной функцией;
– может быть компонентной функцией другого ранее определенного класса.

Слайд 7

friend – “методы”

friend – “методы”

Слайд 8

friend – “методы”

friend – “методы”

Слайд 9

friend – “методы”

В этом примере класс А при помощи своего метода void

friend – “методы” В этом примере класс А при помощи своего метода
A::f() получает доступ к закрытым полям класса B.

Слайд 10

friend - функции

-Может быть дружественной по отношению к нескольким классам.
Например:
//

friend - функции -Может быть дружественной по отношению к нескольким классам. Например:
предварительное неполное определение класса
class CL2;
class CL1 {friend void f(CL1,CL2); . . . };
class CL2 {friend void f(CL1,CL2); . . . };

В этом примере функция f имеет доступ к компонентам классов CL1 и CL2.

Слайд 11

Friend - классы
-Класс может быть дружественным другому классу.
-Это означает, что все

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

Слайд 12

Friend - классы

Например:
class X2{friend class X1; . . .};
class X1 {. .

Friend - классы Например: class X2{friend class X1; . . .}; class
. void f1(. . .);
void f2(. . .); . . . };
- В этом примере функции f1 и f2 класса Х1 являются друзьями класса Х2, хотя они описываются без спецификатора friend.

Слайд 13

Friend - классы

Рассмотрим класс point – точка в n-мерном пространстве и дружественный

Friend - классы Рассмотрим класс point – точка в n-мерном пространстве и
ему класс vector
В классе vector определим метод для определения квадратичной длины вектора, которая вычисляется как сумма квадратов его координат.

Слайд 14

Friend - классы

Friend - классы

Слайд 15

Friend - классы

Friend - классы

Слайд 16

Friend - классы

Friend - классы

Слайд 17

Friend - классы

Friend - классы

Слайд 18

Friend - классы

Friend - классы

Слайд 19

Friend - классы

// Будет выведено – 8.

Friend - классы // Будет выведено – 8.

Слайд 20

ПЕРЕГРУЗКА ОПЕРАТОРОВ

В языке С++ определены множества операций над переменными стандартных типов, такие

ПЕРЕГРУЗКА ОПЕРАТОРОВ В языке С++ определены множества операций над переменными стандартных типов,
как +, *, / и т.д. Каждую операцию можно применить к операндам определенного типа.
Как быть, если необходимо, чтобы операторы +, *, / и т.д. могли совершать действия над объектами классов?
Есть решение – необходимо использовать перегрузку операторов.

Слайд 21

ПЕРЕГРУЗКА ОПЕРАТОРОВ

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

ПЕРЕГРУЗКА ОПЕРАТОРОВ Перегрузка операторов позволяет определить действия, которые будет выполнять оператор. Перегрузка
функции, название которой содержит слово operator и символ перегружаемого оператора.
Функция оператора может быть определена как член класса, либо вне класса, возможно, как дружественная функция.

Слайд 22

ПЕРЕГРУЗКА ОПЕРАТОРОВ

Определение оператора-функции имеет следующий синтаксис:
тип operator “знак оператора”(параметры)
{
Действия…
return тип();
}

ПЕРЕГРУЗКА ОПЕРАТОРОВ Определение оператора-функции имеет следующий синтаксис: тип operator “знак оператора”(параметры) { Действия… return тип(); }

Слайд 23

ПЕРЕГРУЗКА УНАРНЫХ ОПЕРАТОРОВ

Любой унарный оператор⊕ может быть определен двумя способами:
как компонентная функция

ПЕРЕГРУЗКА УНАРНЫХ ОПЕРАТОРОВ Любой унарный оператор⊕ может быть определен двумя способами: как
без параметров
как глобальная (возможно, дружественная) функция с одним параметром.
В первом случае выражение ⊕ Z означает вызов Z.operator⊕(), во втором – вызов operator⊕(Z)
где ⊕ – знак операции

Слайд 24

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

А) как компонентная функция
тип operator “знак оператора”(void)

Два случая перегрузки унарных операторов А) как компонентная функция тип operator “знак
{
Действия…
}

Б) как глобальная функция
тип operator “знак оператора”(class&)
{
Действия…
return тип();
}

Где class& - передача значения по ссылке

Слайд 25

ПЕРЕГРУЗКА УНАРНЫХ ОПЕРАТОРОВ

Унарные операции инкремента ++ и декремента – существуют в двух формах:

ПЕРЕГРУЗКА УНАРНЫХ ОПЕРАТОРОВ Унарные операции инкремента ++ и декремента – существуют в
префиксной и постфиксной
Они определены следующим образом:
– префиксная форма:
operator++();
operator—();
– постфиксная форма:
operator++(int);
operator—(int);

Слайд 26

ПЕРЕГРУЗКА УНАРНЫХ ОПЕРАТОРОВ

ПЕРЕГРУЗКА УНАРНЫХ ОПЕРАТОРОВ

Слайд 27

ПЕРЕГРУЗКА УНАРНЫХ ОПЕРАТОРОВ

ПЕРЕГРУЗКА УНАРНЫХ ОПЕРАТОРОВ

Слайд 28

ПЕРЕГРУЗКА УНАРНЫХ ОПЕРАТОРОВ

ПЕРЕГРУЗКА УНАРНЫХ ОПЕРАТОРОВ

Слайд 30

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ

Любая бинарная операция ⊕ может быть определена двумя способами:
-как компонентная функция

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ Любая бинарная операция ⊕ может быть определена двумя способами:
с одним параметром
-как глобальная функция с двумя параметрами.
В первом случае x⊕y означает вызов x.operator⊕(y), во втором – вызов operator⊕(x,y).

Слайд 31

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ

При перегрузке операторов стоит учитывать приоритет и ассоциативность.
Приоритет операторов задает порядок операций

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ При перегрузке операторов стоит учитывать приоритет и ассоциативность. Приоритет
в выражениях, содержащих более одного оператора.
Ассоциативность операторов указывает, будет ли операнд в выражении, содержащем несколько операторов с одинаковым приоритетом, группироваться по левому краю или по правому.
(порядок выполнения)

Слайд 32

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ

К примеру оператор << имеет высокий приоритет и ассоциативен слева направо.
Поэтому

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ К примеру оператор Поэтому при перегрузке оператора
при перегрузке оператора << для работы с потоковыми объектами возвращаем ссылку на поток.

Слайд 33

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ

Допустим, у нас есть класс point – точка в двумерном пространстве.

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ Допустим, у нас есть класс point – точка в
Хотим обращаться к координатам точки через operator[].
хотим сделать так, чтобы была возможность выводить в консоль координаты точки привычным образом.( cout << point)
Для этого перегрузим operator[] как нестатическую компонентную функцию класса
И operator<< как дружественную глобальную функцию

Слайд 35

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ

Слайд 36

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ

Слайд 37

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ

Перегрузка operator= - оператора присваивания.
Операция отличается тремя особенностями:
Оператор не наследуется;
Оператор определен

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ Перегрузка operator= - оператора присваивания. Операция отличается тремя особенностями:
по умолчанию для каждого класса в качестве операции побайтного копирования объекта, стоящего справа от знака операции, в объект, стоящий слева;
Операция может перегружаться только как функция компонент класса.

Слайд 38

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ

Когда следует перегружать оператор присваивания?
В большинстве случаев перегрузка не требуется.
НО

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ Когда следует перегружать оператор присваивания? В большинстве случаев перегрузка
бывают случаи, когда побайтное копирование нежелательно. (использование оператора копирования без перегрузки – побайтное копирование).
Например, если некоторый класс содержит указатели на динамическую область памяти, то после поразрядного копирования выйдет так, что два экземпляра будут владеть одним указателем.
Что вызывает некоторые проблемы.

Слайд 40

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ
-После побайтного копирования b.mas и a.mas
указывают на одну область памяти.
-Память, которая

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ -После побайтного копирования b.mas и a.mas указывают на одну
выделилась под массив b
не будет очищенна.
-По завершении программы у объектов вызовутся деструкторы, что означает очищение одной и той же области памяти два раза.
-может вызвать крах программы.

Слайд 41

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ

Выход – перегрузить оператор присваивания.

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ Выход – перегрузить оператор присваивания.

Слайд 42

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ

Слайд 43

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ

Оператор присваивания (operator=) ассоциативен справа налево.(порядок чтения). И имеет низкий приоритет.

Тут

ПЕРЕГРУЗКА БИНАРНЫХ ОПЕРАТОРОВ Оператор присваивания (operator=) ассоциативен справа налево.(порядок чтения). И имеет
– первым действием выполняется B = A – копируется информация, возвращается ссылка на объект B класса Array, далее выполняется C = B.

Слайд 44

Лабораторная работа №3.
Дружественные функции и классы. Перегрузка операторов.

Создать класс Pair(пара чисел). Пара

Лабораторная работа №3. Дружественные функции и классы. Перегрузка операторов. Создать класс Pair(пара
должна быть представлена двумя полями: типа int для первого числа и типа double для второго. Первое число при выводе на экран должно быть отделено от второго числа двоеточием. Реализовать:
-Вычитание пар чисел
-Добавление константы к паре (увеличивается первое число, если константа целая, второе, если константа вещественная).

Слайд 48

Ответы на вопросы

1. Для чего используются дружественные функции и классы?
Чтобы предоставить доступ

Ответы на вопросы 1. Для чего используются дружественные функции и классы? Чтобы
к private – полям класса, методам другого класса, который является дружественным.
Чтобы предоставить доступ к private-полям глобальным не компонентным функциям.

Слайд 49

Ответы на вопросы

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

Ответы на вопросы 2. Сформулируйте правила описания и особенности дружественных функций. -
объявляется внутри класса с ключевым словом friend.
- Поскольку дружественная функция не является компонентной (ей не передается указатель this), то необходимо, чтобы она принимала в качестве параметра объект класса по ссылке, по значению или по адресу.
- Дружественная функция может быть дружественной сразу к нескольким классам.
- На неё не распространяются спецификаторы доступа, поэтому то место, где она описана в классе, неважно.

Слайд 50

Ответы на вопросы

3. Каким образом можно перегрузить унарные операции.
-Как компонентные нестатические функции

Ответы на вопросы 3. Каким образом можно перегрузить унарные операции. -Как компонентные
класса.
тип operator “знак оператора”(void);
-Как обычная глобальная не компонентная функция, которая также может быть дружественная классу.
тип operator “знак оператора”(class A), где class A – передача объекта класса.
- Унарные операторы перегружаются чаще всего как методы класса.

Слайд 51

Ответы на вопросы

4. Сколько операндов должна иметь унарная функция-операция, определяемая внутри класса.
-

Ответы на вопросы 4. Сколько операндов должна иметь унарная функция-операция, определяемая внутри
Унарная операция по определению работает с одним операндом. Раз она перегружается как компонентная нестатическая функция, то она не должна принимать параметров. (неявно принимает указатель this).
5. Сколько операндов должна иметь унарная функция-операция, определяемая вне класса.
- Т.к. оператор перегружается как глобальная функция, то параметр this ему не передается, следовательно необходимо явно передавать объект класса.

Слайд 52

Ответы на вопросы

 

Ответы на вопросы

Слайд 53

Ответы на вопросы

7. Сколько операндов должная иметь бинарная функция-операция, определяемая вне класса?
-

Ответы на вопросы 7. Сколько операндов должная иметь бинарная функция-операция, определяемая вне
Если оператор перегружается как не компонентная функция, чаще всего дружественная классу. То указатель this не передается, поэтому, необходимо явно передавать объект класса в качестве параметра. Также необходимо передавать объект другого класса, с которым должен взаимодействовать исходный класс посредством оператора.
Например:
Из лабораторной работы №3: -
friend Pair operator+(const double& y, const Pair& p).
Существуют бинарные операторы, перегрузка которых вне класса невозможна: это “->”, “[]”, “()”, “=”
Ответ: два

Слайд 54

Ответы на вопросы

8. Чем отличается перегрузка префиксных и постфиксных унарных операций.
-Префиксные и

Ответы на вопросы 8. Чем отличается перегрузка префиксных и постфиксных унарных операций.
постфиксные операции по сути являются версией одного оператора в разных формах. Если при перегрузке префиксного оператора не нужно передавать никаких параметров, то при перегрузке постфиксного оператора необходимо передать незначащий параметр int. – Чтобы объяснить компилятору разницу.
Также эти операторы могут отличатся по типу возвращаемого значения. Допустим, если префиксный оператор (инкремента или декремента) модифицирует какое-либо информационное поле, а затем возвращает ссылку на объект этого класса, то постфиксный оператор должен сохранить состояние объекта класса во временную переменную, затем модифицировать поле класса, затем вернуть копию предыдущего состояния. – Это накладывает некоторые ограничения на использование постфиксных операторов, т.к. они не позволяют взаимодействовать напрямую с объектом класса.

Слайд 55

Ответы на вопросы

9. Каким образом можно перегрузить операцию присваивания.
- Оператор присваивания можно

Ответы на вопросы 9. Каким образом можно перегрузить операцию присваивания. - Оператор
перегрузить только как нестатическую компонентную функцию класса.
10. Что должна возвращать операция присваивания?
- Ссылку на объект класса, в который происходит копирование (левый операнд). Это нужно для реализации многочисленного присваивания.
Например: a = b = c; Где a, b и c – Объекты одного класса.

Слайд 56

Ответы на вопросы

11. Каким образом можно перегрузить операции ввода-вывода?
- Для того, чтобы

Ответы на вопросы 11. Каким образом можно перегрузить операции ввода-вывода? - Для
обеспечить взаимодействие пользовательского класса и потокового класса (левым операндом является объект потокового класса, правым операндом является объект пользовательского класса), необходимо перегрузить оператор<< или оператор>> как дружественную функцию. С двумя параметрами – первый: объект класса std::ostream или std::istream, второй: объект пользовательского класса.
Например: friend std::ostream& operator<<(std::ostream& stream, const Pair& p)
Тут operator << объявлен в классе как дружественная функция.

Слайд 57

Ответы на вопросы

12. В программе описан класс
class Student
{
… Student&

Ответы на вопросы 12. В программе описан класс class Student { …
operator++();
….
}; и определен объект этого класса Student s; Выполняется операция ++s; Каким образом, компилятор будет воспринимать вызов функции-операции?
-Как вызов метода класса: s.operator++();

Слайд 58

Ответы на вопросы

13. В программе описан класс
class Student {
… friend

Ответы на вопросы 13. В программе описан класс class Student { …
Student& operator ++( Student&);
…. };
и определен объект этого класса Student s; Выполняется операция ++s; Каким образом, компилятор будет воспринимать вызов функции-операции?
-Как вызов глобальной функции: operator++(s);

Слайд 59

Ответы на вопросы

14. В программе описан класс class Student
{

bool operator<(Student &P);
….
};
и определены

Ответы на вопросы 14. В программе описан класс class Student { …
объекты этого класса Student a,b;
Выполняется операция cout<Каким образом, компилятор будет воспринимать вызов функции-операции?
Приоритет оператора << выше, чем у оператора<. Программа просто не скомпилируется.
Если выполнялась бы операция cout<<(a