1.39M
Категория: ПрограммированиеПрограммирование

Объектно-ориентрованное программирование

1.

Объектноориентированное
программирование
РАССМАТРИВАЮТСЯ СОЗДАНИЕ ОБЪЕКТОВ,
КЛАССОВ И ПРОГРАММ НА ИХ ОСНОВЕ

2.

Типы данных
Скалярные типы
Векторные типы
Составные типы
Целые
Массивы
Перечислимые
Вещественные
Строки
Структуры
Символьный
Указательный тип
Классы
Логический
Типы данных в языке C++
2

3.

Скалярные типы данных
int x = 10; double y = 2.5; char z = ‘W’;
Векторные типы данных
int A[5] = {1, 2, 3, 4, 5}; double M[2][3] ={
{1, 2, 3},
{3, 2, 1}
char S1[10] = “Hello”;
};
string S2 = “Hello”;
Указательный тип данных
int A;
int *Ptr = &A;
double *Ptr = new double (2.5);
Перечеслимый тип данных
enum myType
{
e1,
e2,
e3,
e4,
e5
};
myType var = e3;
Типы данных в языке C++
3

4.

Ветвление
if (условие) операторы «Да»; else операторы «Нет»;
Множественный выбор
switch (ключ выбора)
{
case вариант 1: операторы;
case вариант 2: операторы;

case вариант N: операторы;
default: операторы;
}
С предусловием
int S = 0;
int n = 1;
while (n <= 100)
{
S+=n;
n++;
}
Циклы
С постусловием
int S = 0;
int n = 1;
do
{
S+=n;
n++;
}
while (n<=100)
С параметром
int S = 0;
for(int n =0; n<=100; n++)
{
S+=n;
}
Структурирование в императивном программировании
4

5.

Функции
Прототип функции
тип-возвращаемого-значения имя-функции (список типов параметров)
int main( )
{

имя функции // вызов функции
}
Описание функции
тип-возвращаемого-значения имя-функции (список параметров)
{
объявления и операторы
}
Структурирование в императивном программировании
5

6.

Методология объектно-ориентированного программирования – это принцип
программирования, основанный на представлении программы в виде совокупности
объектов, каждый из которых является экземпляром определённого класса, а классы
образуют иерархию наследования
Синтаксис и семантика
Классы предоставляют программисту возможность моделировать объекты, которые
имеют атрибуты (внутренние данные) и варианты поведения или методы (внутренние
функции).
Инкапсуляция – свойство ООП, позволяющее объединить данные и работающие с ними
функции внутри одного класса.
Наследование – свойство ООП, позволяющее описать новый класс на основе уже
существующего с частично или полностью заимствующейся функциональностью.
Полиморфизм
– свойство ООП, позволяющее использовать объекты с одинаковым
интерфейсом без информации о типе и внутренней структуре объекта.
Языки объектно-ориентированного программирования
Чистые языки, в наиболее классическом виде поддерживающие объектноориентированную методологию. Simula (1962), Smalltalk (1972), Self (1986), Cecil (1992).
Гибридные языки – императивные языки программирования с внедренными объектноориентированными конструкциями. C++ (1983), Object Pascal (1984).
Урезанные языки, появились в результате удаления из гибридных языков наиболее
опасных с объектно-ориентированной точки зрения конструкций. Java (1995), C#(2000)
Методология объектно-ориентированного программирования
6

7.

Fortran (1957)
Algol (1958)
PL/I (1964)
CPL (1961)
BCPL (1969)
B (1970)
C (1971)
C++ (1985)
C# (2000)
ANSI C (1989)
Java (1995)
C99 (1999)
Происхождение языка C++
7

8.

Структуры – это составные типы данных, построенные с использованием других типов.
struct Anketa
{
string name;
string surname;
int age;
char gender;
};
Определение структуры данных не резервирует никакого пространства в памяти, а
определение только создает новый тип данных. Переменные структуры объявляются так
же, как переменные других типов.
Anketa Person; Anketa StudGroup[10]; Anketa *AnkPtr;
Для доступа к элементам структуры (или класса) используются операции доступа к
элементам – операция точка (.) и операция стрелка ( >). Операция точка обращается к
элементу структуры (или класса) по имени переменной объекта или по ссылке на объект.
Person.name = “Иванов”; StudGroup[3].age = 35;
Операция стрелка, обеспечивает доступ к элементу структуры (или класса) через указатель
на объект.
AnkPtr >gender=‘M’;
Структуры
8

9.

Создать анкету, содержащую имя, фамилию и возраст человека. Вывести на экран
анкетные данные членов списка мужского пола, возраст которых больше 18, но не
превышает 27 лет.
#include<iostream>
using namespace std;
// Описание структуры Anketa, две строки, целочисленную переменную и символ
struct Anketa
{
string name;
string surname;
int age;
char gender;
};
void set(Anketa &); // прототип функции set
void search(Anketa &); // прототип функции search
int main()
{
Anketa anketa1, anketa2;
set(anketa1); set(anketa2);
search(anketa1); search(anketa2);
return 0;
}
Структуры
Next
9

10.

// Функция set() вводит требуемые значения в элементы структуры
void set(Anketa & strc)
{
cout << "Имя - ";
cin >> strc.name;
cout << "Фамилия - ";
cin >> strc.surname;
cout << "Возраст - ";
cin >> strc.age;
cout << "Пол: \nM-мужской \nF-женский \n-";
cin >> strc.gender;
}
/* Функция search() выводит на экран значения структуры в соответствии с
требованиями*/
void search(Anketa & strc)
{
if ((strc.age >= 18) && (strc.age <= 27) && (strc.gender == ‘M’))
{
cout << "Имя: " << strc.name << endl;
cout << "Фамилия: " << strc.surname << endl;
cout << "Возраст: " << strc.age << endl;
}
}
Структуры
10

11.

Структуры
11

12.

KAHOOT.IT
12

13.

Классы и объекты
13

14.

Классы предоставляют программисту возможность моделировать объекты, которые
имеют атрибуты (внутренние данные) и варианты поведения или методы (внутренние
функции).
class Anketa
{
public:
Anketa(string, string); // прототип конcтруктора
void set(); // прототип функции set
void search(); // прототип функции search
private:
string name;
string surname;
int age;
char gender;
};
Метки public: (открытая) и private: (закрытая) называются спецификаторами доступа к
элементам.
Данные-элементы и функции-элементы, объявленные после спецификатора доступа к
элементам public: доступны при любом обращении через объект класса.
Данные-элементы и функции-элементы, объявленные после спецификатора доступа к
элементам private: доступны только функциям-элементам этого класса.
Классы и объекты
14

15.

Anketa::Anketa(string nm, string snm) {
gender = 'M';
age = 0;
name = nm;
surname = snm;
}
void Anketa::set() {
cout << "Ввести имя - ";
cin >> name;
cout << "Ввести фамилию - ";
cin >> surname;
cout << "Ввести возраст - ";
cin >> age;
cout << "Указать пол: \nM-мужской \nF-женский \n-";
cin >> gender;
}
void Anketa::search() {
if ((age>=18) && (age<=27) && (gender == 'M' || gender == 'm')) {
cout << "Имя: " << name << endl;
cout << "Фамилия: " <<surname << endl;
cout << "Возраст " <<age << endl<< endl;
}
}
Классы и объекты
15

16.

Функция-элемент с тем же именем, что и класс, называется конструктором класса.
Конструктор – это специальная функция-элемент, которая инициализирует данныеэлементы объекта этого класса. Конструктор класса вызывается автоматически при
создании объекта этого класса.
Anketa::Anketa() {gender == 'M'; age = 0;} // конструктор класса
Функция с тем же именем, что и класс, но со стоящим перед ней символом тильда (~),
называется деструктором этого класса. Деструктор производит «завершающие служебные
действия» над каждым объектом класса перед тем, как память, отведенная под этот
объект, будет повторно использована системой.
~Anketa::Anketa() // деструктор класса
Начиная с версии C++13 данные-элементы класса могут получать начальные значения в
теле класса, где они объявляются.
private:
string name;
string surname;
int age = 18;
char gender;
Конструкторы и деструкторы
16

17.

int main()
{
Anketa anketa1(“Иван”, “Егоров”), anketa2(“Анна”, “Егорова”);
anketa1.set(); anketa2.set();
anketa1.search(); anketa2.search();
system("Pause");
return 0;
}
Когда класс определен, его можно использовать в качестве типа в объявлениях
Anketa man; // объекта класса Anketa;
Anketa anketaArr[5]; // массив объектов класса Anketa;
Anketa *AnkPtr; // указатель на объект класса Anketa.
Операции, использованные для доступа к элементам класса, аналогичны операциям,
используемым для доступа к элементам структуры. Операция выбора элемента точка (.)
комбинируется для доступа к элементам объекта с именем объекта или со ссылкой на
объект. Операция выбора элемента стрелка ( ) комбинируется для доступа к
элементам объекта с указателем на объект.
man.set();
AnkPtr search();
Классы и объекты
17

18.

Один из наиболее фундаментальных принципов разработки хорошего программного
обеспечения состоит в отделении интерфейса от реализации.
Объявление класса помещается в заголовочный файл, чтобы оно было доступно любому
клиенту, который захочет использовать класс.
// Заголовочный файл ANKETA.H Объявление класса Anketa.
#ifndef ANKETA_H
#define ANKETA_H
class Anketa {
public:
Anketa();
void set();
void search();
private:
string name;
string surname;
int age;
char gender;
};
#endif
Классы и объекты
18

19.

Директивы препроцессора
#include guard (защита подключений) предотвращают
включение кода между #ifndef и #endif, если определено имя ANKETA_Н. Если
заголовок еще не включался в файл, то имя ANKETA_Н определяется директивой #define
и операторы заголовочного файла включаются в результирующий файл. Если же заголовок
уже был включен ранее, ANKETA_Н уже определен и операторы заголовочного файла
повторно не включается.
// Предотвращение многократного включения заголовочного файла
#ifndef ANKETA_H
#define ANKETA_H

#endif
или
#pragma once
Применение #pragma once вместо #include guard увеличит скорость компиляции во многих
случаях благодаря высокоуровневому механизму; компилятор может самостоятельно
сравнивать имена файлов без необходимости вызова препроцессора Си для проверки
заголовка на наличие #ifndef и #endif.
Классы и объекты
19

20.

// Исходный файл Anketa.cpp содержащий описания функций.
// Определения функций-элементов для класса Anketa.
#include<iostream>
#include"anketa.h"
using namespace std;
Anketa::Anketa() {gender = 'M' age = 0; }
void Anketa::set()
{
cout << "Ввести имя - ";
cin >> name;
cout << "Ввести фамилию - ";
cin >> surname;
cout << "Ввести возраст - ";
cin >> age;
cout << "Указать пол: M-мужской F-женский "; cin >> gender;
}
void Anketa::search()
{
if ((age>=18) && (age<=27) && (gender == 'M' || gender == 'm'))
{
cout << "Имя: " << name << endl;
cout << "Фамилия: " << surname << endl;
cout << "Возраст " << age << endl << endl;
}
}
Классы и объекты
20

21.

Созданный программистом конструктор, у которого все аргументы – аргументы по
умолчанию, называется конструктором с умолчанием, т.е. конструктором, который
можно вызывать без указания каких-либо аргументов. Задание в конструкторе аргументов
по умолчанию позволяет гарантировать, что объект будет находиться в непротиворечивом
состоянии, даже если в вызове конструктора не указаны никакие значения.
#ifndef ANKETA_H
#define ANKETA_H
class Anketa {
public:
Anketa(string = “No”, string = “No”);
void set();
void search();
private:
string name;
string surname;
int age;
char gender;
};
#endif
Классы и объекты
21

22.

Кроме обычных методов в языке C++ могут предусмотрены специальные методы доступа,
которые называют свойства. Они обеспечивают управляемый доступ к полям классов.
#ifndef LIST_H
#define LIST_H
class List
{
public:
List((string = “No”, string = “No”); // прототип конструктора
void set (); // прототип функции set
void search (); // прототип функции search
void setAge(int);
void setGender(char);
string getName() { return name; }
string getSurname() { return surname; }
private:
string name;
string surname;
int age;
char gender;
};
#endif
Классы и объекты
22

23.

void Anketa::setAge(int value)
{
age = (value > 0) ? value : 0;
}
void Anketa::setGender(char value)
{
if (value == 'M' || value == 'm' || value == 'F' || value == 'm')
gender = value;
else gender = 'M';
}
int main()
{
setlocale(LC_ALL,"Rus");
List anketa;
anketa.set();
anketa.setAge(30);
anketa.setGender('F');
cout << "Фамилия: " << anketa.getSurname() << endl;
cout << "Имя: " << anketa.getName() << endl;
return 0;
}
Функции утилиты
23

24.

KAHOOT.IT
24

25.

Дополнительные
возможности классов
25

26.

Некоторые объекты должны допускать изменения, другие – нет. Программист может
использовать ключевое слово const для указания на то, что объект неизменяем – является
константным и что любая попытка изменить объект является ошибкой.
const List anketa;
Компиляторы C++ не допускают вызовов стандартных функций-элементов константных
объектов. Константные объекты могут вызывать только функции объявленные как
константные.
Константная функция указывается как const и в объявлении, и в описании с помощью
ключевого слова const после списка параметров функции, но перед левой фигурной
скобкой, которая начинает тело функции.
int А::getValue ( ) const {return privateDateMember};
Константная функция-элемент может быть перегружена неконстантным вариантом. Выбор
того, какая из перегруженных функций-элементов будет использоваться, осуществляется
компилятором автоматически в зависимости от того, был объявлен объект как const или
нет.
Константные объекты
26

27.

Класс может включать в себя другие классов в качестве элементов. Такая возможность
называется композицией.
class Human {
public:
void Think()
{
brain.Think();
}
private:
class Brain {
public:
void Think()
{
cout << "Я думаю!" << endl;
}
};
Brain brain;
};
int main() {
Human human;
human.Think();
}
Композиция классов
27

28.

Класс может включать в себя объекты других классов в качестве элементов. Такая
возможность называется агрегацией.
// Файл date.h. Объявление класса // Файл anketa.h. Объявление класса
anketa
date.
#pragma once
#pragma once
#include "date.h"
class date
class anketa
{
{
public:
date(int=1, int=1, int=1900); public:
anketa(string, string,
void print();
int,int,int);
private:
void print();
int day;
private:
int month;
string LastName;
int year;
string FirstName;
int checkDay(int);
date BirthDate; /* Объект класса
};
date как объект класса Anketa */
};
Next
Агрегация классов
28

29.

// Файл date.cpp. Определения функций-элементов класса date.
#include<iostream>
#include "date.h"
date::date(int d, int m, int y)
{
if (m > 0 && m <= 12)
month = m;
else
{
month = 1;
cout << "Номер месяца" << m <<" неверен. Month=1" << endl;
}
year = y;
day = checkDay(d);
}
Next
Композиция классов
29

30.

// Файл date.cpp. Определения функций-элементов класса date.
int date::checkDay(int chekD)
{
int DayPerMonth[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
if ((chekD>0) && (chekD<= DayPerMonth[month]))
return chekD;
if ((chekD == 29) && (month == 2) && (year % 4 == 0))
return chekD;
cout << "Day " << chekD << " is wrong.
Day=1";
return 1;
}
void date::print()
{
cout << day << "." << month << "." << year
}
Next
Композиция классов
30

31.

// Файл anketa.cpp. Определения функций-элементов класса anketa.
#include<iostream>
#include "anketa.h"
#include "date.h"
using namespace std;
anketa::anketa(string lname, string fname, int bd, int bm, int by) :
birthDate(bd, bm, by)
{
LastName = lname;
FastName = fname;
}
Next
Композиция классов
31

32.

// Файл anketa.cpp. Определения функций-элементов класса anketa.
void anketa::print()
{
cout << "Имя: " << lastName << " " << firstName;
cout << "Дата рождения: "; birthDate.print();
cout << endl;
}
// Файл main.cpp. Демонстрация объекта с объектом-элементом
#include<iostream.h>
#include "anketa.h"
int main()
{
anketa ank("Иванов", "Иван", 1, 10, 2010);
ank.print();
system("Pause");
return 0;
}
Композиция классов
32

33.

Дружественные функции класса определяются вне области действия этого класса, но
имеют право доступа к закрытым элементам private данного класса. Функции или класс
в целом могут быть объявлены другом (friend) другого класса.
#include<iostream>
class Count
{
friend void setX(Count&, int); // прототип дружественной функции
public:
Count() { x = 0; }
inline void print() const { cout << "x=" << x << endl; }
private:
int x;
};
void setX(Count& c, int val) //
{
c.x = val;
}
описание дружественной функции
Чтобы объявить функцию как дружественную (friend) класса, перед ее прототипом в
описании класса ставится ключевое слово friend. Определение friend исключает
функцию из области спецификаторов доступа
Дружественные функции
33

34.

int main()
{
Count obj;
cout << "Obj.x до запуска setX:";
obj.print();
cout << "Obj.x после запуска setX:";
setX(obj, 10); // Вызов дружественной функции
obj.print();
system("Pause");
return 0;
}
Дружественные функции
34

35.

Функции-члены класса могут быть объявлены в других классах как дружественные.
class B;
class A
{
public:
int Func1(B& val) { return val.x; }; // OK
private:
int Func2(B& val); { return val.x; } // Error
};
class B
{
private:
int x;
friend int A::Func1(B&);
};
int main() {
B B_obj;
A A_obj;
cout << A_obj.Func1(B_obj) <<
return 0;
}
endl;
Функции классов как дружественные
35

36.

Дружественный класс – это класс, все функций-члены которого являются дружественными
функциями класса, то есть функции-члены которого имеют доступ к закрытым и
защищенным членам другого класса.
#include <iostream>
using namespace std;
class A
{
friend class B; // Объявление дружественного класса
public:
A() { var_x = 0; }
void print() { cout << var_x << endl; }
private:
int var_x;
};
class B
{
public:
void change(A& val, int t) { val.var_x = t; }
};
Дружественные классы
36

37.

Дружественный класс – это класс, все функций-члены которого являются дружественными
функциями класса, то есть функции-члены которого имеют доступ к закрытым и
защищенным членам другого класса.
int main()
{
A Obj_A;
B Obj_B;
Obj_A.print();
Obj_B.change(Obj_A, 5);
Obj_A.print();
system("Pause");
return 0;
}
Дружественные классы
37

38.

Каждый объект сопровождается указателем на самого себя – называемым указателем
this – это неявный аргумент во всех ссылках на элементы внутри этого объекта. Основным
применением указателя this является возможность сцепленных вызовов функций
элементов.
// Файл time.h. Объявление класса Time.
#pragma once
class Time {
public:
Time(int = 0, int = 0, int = 0);
Time& setTime(int, int, int);
Time& setHour(int);
Time& setMinute(int);
Time& setSecond(int);
void printEuropean() const;
void printAmerican() const;
private:
int hour;
int minute;
int second;
};
Next
Использование указателя *this
38

39.

// Файл time.срр. Определения функций-элементов класса Time.
#include <iostream>
#include "time.h"
Time::Time(int hr, int min, int sec) {
setTime(hr, min, sec);
}
Time& Time::setTime(int h, int m, int s) {
hour = (h >= 0 && h < 24) ? h : 0;
minute = (m >= 0 && h < 60) ? m : 0;
second = (s >= 0 && s < 60) ? s : 0;
return *this;
}
Time& Time::setHour(int h) {
hour = (h >= 0 && h < 24) ? h : 0;
return *this;
}
Time& Time::setMinute(int m) {
minute = (m >= 0 && m < 60) ? m : 0;
return *this;
}
Использование указателя *this
Next
39

40.

// Файл time.срр. Определения функций-элементов класса Time.
Time& Time::setSecond(int s)
{
second = (s >= 0 && s < 60) ? s : 0;
return *this;
}
void Time::printEuropean() const
{
cout << (hour < 10 ? "0" : "") << hour << ":";
cout << (minute < 10 ? "0" : "") << minute << ":";
cout << (second < 10 ? "0" : "") << second << endl;
}
void Time::printAmerican() const
{
cout << ((hour == 10 || hour == 12) ? 12 : hour % 12) << ":";
cout << (minute < 10 ? "0" : "") << minute << ":";
cout << (second < 10 ? "0" : "") << second;
cout << (hour < 12 ? "AM" : "PM");
}
Next
Использование указателя *this
40

41.

// Файл main.cpp. Сцепление вызовов функций-элементов указателем this.
#include <iostream>
#include "time.h"
int main()
{
Time t(1, 1, 1);
cout << "Европейский формат времени: "; t.printEuropean();
cout << "Американский формат времени: "; t.printAmerican();
cout << " Европейский формат времени: ";
t.setTime(22, 33, 45).printEuropean(); // сцепленный вызов функций
cout << " Американский формат времени: "; t.printAmerican();
t.setHour(15).setMinute(30).setSecond(45); //сцепленный вызов функций
cout << " Европейский формат времени: "; t.printEuropean();
cout << " Американский формат времени: "; t.printAmerican();
return 0;
}
Использование указателя *this
41

42.

Операции new и delete обеспечивают удобные средства для реализации динамического
распределения памяти для любых встроенных или определенных пользователем типов.
TypeName* typeNamePtr;
typeNamePtr = new TypeName;
Операция new автоматически создает объект соответствующего размера, вызывает
конструктор объекта и возвращает указатель правильного типа. Если new не в состоянии
найти необходимое пространство в памяти, она возвращает указатель 0
float* thingPtr = new float(3.14);
int* array1D = new int[8];
float** array2D
array2D = new float* [2]; // две строки в матрице
for (int i = 0; i < 2; i++)
array2D[i] = new float[5]; // и пять столбцов
Чтобы освободить пространство,
использовать операцию delete
delete typeNamePtr;
delete thingPtr;
delete[] array1D;
выделенное
ранее
для
объекта,
необходимо
for (int i = 0; i < 2; i++)
delete[] array2D[i];
Динамическое распределение памяти
42

43.

Обычно каждый объект класса имеет свою собственную копию всех данных-элементов
класса. Но в определенных случаях во всех объектах класса должна фигурировать только
одна копия некоторых данных-элементов для всех объектов класса. Для этих и других
целей используются статические данные-элементы, которые содержат информацию «для
всего класса».
Для обеспечения доступа в
указанном случае к закрытому
или защищенному элементу
// Файл TestStatic.h. Описание класса TestStatic
класса
должна
быть
#pragma once
предусмотрена
открытая
class TestStatic
статическая
функция-элемент,
{
которая должна вызываться с
public:
добавлением перед ее именем
TestStatic(){};
имени класса и бинарной
void setStaticVar(int);
операции разрешения области
void setNonStaticVar(int);
действия.
int getStaticVar();
int getNonStaticVar();
static void changeStaticVar(int); // Статическая функция
private:
static int StaticVar = 1; // Статическая переменная
int NonStaticVar = 1; // Нестатическая переменная
Next
};
Статические элементы класса
43

44.

// Файл TestStatic.cpp. Реализация функций класса TestStatic.
#include "TestStatic.h"
int TestStatic::StaticVar = 1;
void TestStatic::setStaticVar(int v) {
StaticVar = v;
}
void TestStatic::setNonStaticVar(int v) {
NonStaticVar = v;
}
int TestStatic::getStaticVar() {
return StaticVar;
}
int TestStatic::getNonStaticVar() {
return NonStaticVar;
}
void TestStatic::changeStaticVar(int v) {
Next
StaticVar = v;
NonStaticVar = v; // Ошибка! Нет доступа к нестатическим данным
}
Статические элементы класса
44

45.

Функция-элемент тоже может быть объявлена как static, если она не должна иметь
доступ к нестатическим элементам класса. В отличие от нестатических функций-элементов
статическая функция-элемент не имеет указателя this, потому что статические данныеэлементы и статические функции-элементы существуют независимо от каких-либо
объектов класса.
int student::getCount()
{
return count;
}
const char* student::getLastName() const
{
return lastName;
}
const char* student::getFirstName() const
{
return firstName;
}
Next
Статические элементы класса
45

46.

// Файл main.cpp. Использование функций класса TestStatic
#include "TestStatic.h"
#include <iostream>
using namespace std;
int main()
{
setlocale(LC_ALL, "Russian");
TestStatic TS1, TS2;
TS1.setNonStaticVar(3);
cout <<"Не статическая переменная TS1 = " <<TS1.getNonStaticVar() <<endl;
cout <<"Не статическая переменная TS2 = " <<TS2.getNonStaticVar() <<endl;
TS1.setStaticVar(10);
cout << "Cтатическая переменная TS1 = " << TS1.getStaticVar() <<
cout << "Cтатическая переменная TS2 = " << TS2.getStaticVar() <<
system("Pause");
return 0;
endl;
endl;
}
Статические элементы класса
46

47.

KAHOOT.IT
47

48.

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

49.

Перегрузка – это механизм описание нескольких арифметических или логических
операторов для различных пользовательских типов данных. Операции перегружаются
путем составления описания функции, за исключением того, что имя функции состоит из
ключевого слова operator, после которого записывается перегружаемая операция.
Перегружаемые операции
+
~
/=
<<=
--
!
%=
==
->*
*
=
^=
!=
,->
/
<
&=
<=
[]
%
>
|=
>=
()
^
+=
<<
&&
new
&
-=
>>
||
delete
|
*=
>>=
++
Не перегружаемые операции
.
.*
::
?:
sizeof
Ограничения на перегрузку операций
Приоритет операций не может быть изменено перегрузкой.
Ассоциативность операций не может быть изменена перегрузкой.
Изменить количество операндов, которое берет операция, невозможно
Создавать новые операции невозможно; перегружать можно только уже
существующие операции
Перегрузка операций
49

50.

При перегрузке операций ( ), [ ], -> или = функция перегрузки операции должна быть
объявлена как элемент класса. Для других операций функции перегрузки операций могут
быть дружественными функциями.
Перегрузка унарных операций
Унарную операцию можно перегружать как не статическую функцию-элемент без
аргументов, либо как дружественную функцию, с одним аргументом; этот аргумент
должен быть либо объектом класса, либо ссылкой на объект класса.
/* Перегрузка префиксной и постфиксной формы операции инкремента
(++). Операция перегружается как дружественная функция. */
#pragma once
#include <iostream>
class Value1 {
friend void operator++(Value1&); // префиксная форма
public:
Value1(int, int);
void print();
void operator++(int);
private:
int x1;
int x2;
Next
};
Перегрузка унарных операций
50

51.

Value1::Value1(int a, int b) { x1 = a; x2 = b; }
void Value1::print() {
cout << "x1=" << x1 << endl;
cout << "x2=" << x2 << endl;
}
void operator++(Value1& B) {
B.x1 = B.x1 + 1;
B.x2 = B.x2 + 1;
}
void Value1::operator++(int) {
x1 = x1 + 1;
x2 = x2 + 1;
}
int main() {
Value1 A(10, 20);
A.print();
++A; // запуск перегруженной операции
A.print();
A++; // запуск перегруженной операции
A.print();
return 0;
}
Перегрузка операций
51

52.

Перегрузка бинарных операций
Бинарную операцию можно перегружать как не статическую функцию-элемент с одним
аргументом, либо как функцию, не являющуюся элементом, с двумя аргументами (левый
аргумент должен быть либо объектом класса, либо ссылкой на объект класса).
/* Файл value2.h. Описание класса Value2. Перегрузка бинарных
операций +и =. Операции перегружаются как члены класса*/
#pragma once
class Value2 {
public:
friend Value2& operator+(Value2&, Value2&);
void operator=(Value2&);
Value2(int = 0, int = 0);
void print();
private:
int x1;
int x2;
int c1;
int c2;
};
Next
Перегрузка бинарных операций
52

53.

// Файл value2.cpp. Реализация класса Value2.
#include <iostream>
#include "value2.h"
Value2::Value2(int a, int b) {
x1 = a;
x2 = b;
}
void Value2::print() {
cout << "x1=" << x1 << endl;
cout << "x2=" << x2 << endl;
}
Value2& operator+(Value2& v1, Value2& v2)
{
v1.c1 = v1.x1 + v2.x1;
v1.c2 = v1.x2 + v2.x2;
return v1;
}
void Value2::operator=(Value2& v)
{
x1 = v.c1;
x2 = v.c2;
}
Перегрузка бинарных операций
Next
53

54.

// Файл main.cpp. Драйвер класса Value2.
#include <iostream>
#include "value2.h"
int main()
{
Value2 Obj1(1, 1), Obj2(2, 2), Obj3;
Obj3 = Obj2 + Obj1;
cout << "Obj1:" << endl; Obj1.print();
cout << "Obj2:" << endl; Obj2.print();
cout << "Obj3:" << endl; Obj3.print();
system("Pause");
return 0;
}
Перегрузка бинарных операций
54

55.

Перегрузка операций поместить в поток и взять из потока
Перегруженная операция << должна иметь левый операнд типа ostream& (такой, как
cout). Аналогично, перегруженная операция >> должна иметь левый операнд типа
istream& (такой, как cin), так что эти функции не могут быть функциями-элементами.
/* Файл IPaddress.h. Описание класса IPaddress. Операции << и >>
перегружаются как дружественные функции */
#pragma once
class IPaddress {
friend ostream& operator <<(ostream&, IPaddress&);
friend istream& operator >>(istream&, IPaddress&);
public:
MyStream();
private:
char ip1[4];
char ip2[4];
char ip3[4];
char ip4[4];
};
Перегрузка операций << и >>
55

56.

// Файл stream.cpp. Реализация класса IPaddress.
#include <iostream>
#include "IPaddress.h"
ostream& operator <<(ostream& output, IPaddress& IP)
{
output<<IP.ip1<<"."<<IP.ip2<<"."<<IP.ip3<< ."<<IP.ip1 <<".";
return output;
}
istream& operator >>(istream& input, IPaddress& IP)
{
input.get(IP.ip1, 3);
input.ignore();
input.get(IP.ip2, 3);
input.ignore();
input.get(IP.ip3, 3);
input.ignore();
input.get(IP.ip4, 3);
return input;
}
Перегрузка операций << и >>
56

57.

// Файл main.cpp. Драйвер класса stream.
#include <iostream>
#include "IPaddress.h"
int main()
{
IPaddress ip_addr;
cout << "Введите IP адрес: ";
cin >> ip_addr;
cout << "IP адрес: ";
cout << ip_addr << endl;
system("Pause");
return 0;
}
Функция-операция взять из потока (operator>>) получает как аргументы ссылку input
типа istream, и ссылку, названную num, на определенный пользователем тип
IPaddress; функция возвращает ссылку типа istream.
Перегрузка операций
57

58.

KAHOOT.IT
58

59.

Наследование
59

60.

Наследование – это способ повторного использования программного обеспечения, при
котором новые классы создаются из уже существующих классов путем заимствования их
атрибутов и функций и обогащения этими возможностями новых классов.
Наследование формирует древовидные иерархические структуры. Базовый класс
находится в иерархических отношениях со своими производными классами.
Производный класс не может иметь доступ к закрытым элементам своего базового класса;
разрешение такого доступа явилось бы нарушением инкапсуляции базового класса.
Производный класс может, однако, иметь доступ к открытым и защищенным элементам
своего базового класса. Защищенный уровень доступа (protected) служит
промежуточным уровнем защиты между открытым доступом и закрытым доступом.
Защищенные элементы базового класса могут быть доступны только элементам и друзьям
базового класса и элементам и друзьям производного класса.
Наследование
60

61.

Чтобы указать, что например класс 2D_Shape порожден классом Shape, класс 2D_Shape
должен быть определен следующим образом:
class 2D_Shape: public Shape {…};
Существует открытое наследование (public inheritance), закрытое наследование
(private inheritance) и защищенное наследование (protected inheritance)
Спецификатор доступа
к элементам в базовом
классе
public – открытое
наследование
Тип наследования
protected –
защищенное
наследование
private – закрытое
наследование
public
public в
protected в
private в
производном классе производном классе производном классе
protected
protected в
protected в
private в
производном классе производном классе производном классе
private
невидим в
невидим в
невидим в
производном классе производном классе производном классе
Типы наследования
61

62.

Производный класс может переопределить функцию-элемент базового класса. При
описании в производном классе функции с тем же именем, версия функции производного
класса переопределяет версию базового класса.
// Файл anketa.h. Описание базового класса Anketa.
#ifndef ANKETA_H
#define ANKETA_H
class Anketa
{
public:
Anketa(const char*, const char*);
~Anketa();
void print();
protected: /* Защищенный раздел. Аналогичен private, но наследуется
в производный класс*/
char* lastName;
char* firstName;
};
#endif
Наследование
62

63.

// Файл anketa.cpp. Реализация базового класса anketa.
#include<iostream>
#include<Сstring>
#include<assert.h>
#include"Anketa.h"
Anketa::Anketa(const char* lname, const char* fname)
{
lastName = new char[strlen(lname) + 1];
assert(lastName != 0);
strcpy(lastName, lname);
firstName = new char[strlen(fname) + 1];
assert(firstName != 0);
strcpy(firstName, fname);
}
Anketa::~Anketa()
{
delete[] lastName;
delete[] firstName;
}
void Anketa::print() { cout << lastName << " " << firstName << endl; }
Наследование
63

64.

//Файл student.h. Описание производного класса student.
#ifndef STUDENT_H
#define STUDENT_H
#include "anketa.h"
//Класс student открыто наследуется от класса anketa
class Student : public Anketa
{
public:
Student(const char*, const char*, float, float, float);
float averagePoint();
void print();
private:
float Point1;
float Point2;
float Point3;
};
#endif
Наследование
64

65.

//Файл student.cpp. Реализация класса student.
#include<iostream>
#include"student.h"
Student::Student(const char* lname, const char* fname, float p1, float
p2, float p3) : Anketa(lname, fname)
{
Point1 = p1;
Point2 = p2;
Point3 = p3;
}
float Student::averagePoint()
{
return (Point1 + Point2 + Point3) / 3;
}
void Student::print()
{
cout << "Студент: ";
Anketa::print();
cout << "Средний балл: " << averagePoint() << endl;
}
Наследование
65

66.

// Файл main.cpp. реализация наследования класса Anketa классом Student.
#include<iostream>
#include"student.h"
int main()
{
Student s("Иванов", "Иван", 5.0, 4.0, 3.0);
s.print();
system("Pause");
return 0;
}
Наследование
66

67.

Объекты открыто порожденного класса могут также рассматриваться как объекты
соответствующего ему базового класса. Это ведет к некоторым интересным следствиям.
Но обратное неверно: объекты базового класса не являются автоматически объектами
производного класса.
#include<iostream>
#include"student.h"
int main()
{
Student S("Кузнецов", "Роман", 5.0, 4.0, 3.0), * sPtr;
Anketa A("Иванов", "Сергей"), * aPtr;
aPtr = &A; // корректно
aPtr->print();
sPtr = &S; // корректно
sPtr->print();
aPtr = &S; // допустимо
aPtr->print();
sPtr = &A; // ошибка
sPtr = (Student*)aPtr;
system("Pause");
return 0;
}
Приведение типов указателей базовых классов к указателям производных классов
67

68.

Конструкторы, деструкторы и операции присваивания не наследуются производными
классами. Однако конструкторы и операции присваивания производного класса могут
вызывать конструкторы и операции присваивания базового класса.
При наследовании конструкторы базовых классов вызываются в той последовательности, в
которой указано наследование в определении производного класса. На это не влияет
последовательность, в которой указаны конструкторы базовых классов в описании
конструктора производного класса.
// Файл anketa.h. Описание класса anketa.
#ifndef ANKETA_H
#define ANKETA_H
class Anketa
{
public:
Anketa(const char*, const char*);
~Anketa();
void print();
protected:
char* lastName;
char* firstName;
};
#endif
Наследование
68

69.

// Файл anketa.cpp. Реализация класса anketa.
#include<iostream>
#include<Сstring>
#include<Сassert>
#include"Anketa.h"
Anketa::Anketa(const char* lname, const char* fname)
{
lastName = new char[strlen(lname) + 1];
assert(lastName != 0);
strcpy(lastName, lname);
firstName = new char[strlen(fname) + 1];
assert(firstName != 0);
strcpy(firstName, fname);
cout << "Конструктор для класса Anketa" << lastName << endl;
}
Anketa::~Anketa()
{
cout << " Деструктор для класса Anketa " << lastName << endl;
delete[] lastName;
delete[] firstName;
}
void Anketa::print() { cout << lastName << " " << firstName << endl; }
Наследование
69

70.

// Файл student.h. Описание класса student.
#ifndef STUDENT_H
#define STUDENT_H
#include "anketa.h"
class Student : public Anketa
{
public:
Student(const char*, const char*, float, float, float);
~Student();
float averagePoint();
void print();
private:
float Point1;
float Point2;
float Point3;
};
#endif
Наследование
70

71.

// Файл student.cpp. Реализация класса student.
#include<iostream>
#include"student.h"
Student::Student(const char* lname, const char* fname, float p1, float
p2, float p3) : Anketa(lname, fname)
{
Point1 = p1; Point2 = p2; Point3 = p3;
cout << " Конструктор для класса Student " << lastName << endl;
}
Student::~Student()
{
cout << " Деструктор для класса Student " << lastName << endl;
}
float Student::averagePoint() { return (Point1 + Point2 + Point3) / 3;
}
void Student::print()
{
cout << "Студент: ";
Anketa::print();
cout << "Средний балл: " << averagePoint() << endl;
}
Наследование
71

72.

// Файл main.cpp. реализация наследования класса Anketa классом
Student.
#include<iostream>
#include"student.h"
#include"anketa.h"
int main()
{
{
Anketa A("Кузнецов", "Роман");
}
{
Student S("Иванов", "Сергей", 5.0, 4.0, 3.0);
cout << "Средний балл:" << S.averagePoint() << endl;
}
system("Pause");
return 0;
}
Наследование
72

73.

KAHOOT.IT
73

74.

Полиморфизм
74

75.

Полиморфизм – возможность для объектов разных классов, связанных с помощью
наследования, реагировать различным образом при обращении к одной и той же
функции-элементу.
Полиморфизм реализуется посредством виртуальных функций. Функция объявляется
виртуальной с помощью ключевого слова virtual, предшествующего прототипу функции
в базовом классе.
#include<iostream>
class Base
{
public:
void print() { cout << "Base::print( )" << endl; }
virtual void print_v() { cout << "Base::virtual print( )"; }
};
class Derived : public Base
{
public:
void print() { cout << "Derived::print( )" << endl; }
virtual void print_v() { cout << "Derived::virtual print( )" }
};
Полиморфизм
75

76.

int main()
{
Derived* DPtr = new Derived;
Base* BPtr = DPtr;
DPtr->print();
BPtr->print();
DPtr->print_v();
BPtr->print_v();
system("PAUSE");
return 0;
}
Полиморфизм
76

77.

Абстрактный класс – базовый класс, который не предполагает создания объектов.
Единственным назначением абстрактного класса является создание соответствующего
базового класса, от которого другие классы могут унаследовать интерфейс и реализацию.
Классы, объекты которых могут быть реализованы, называются конкретными классами.
Класс делается абстрактным путем объявления одной или более его виртуальных функций
чисто виртуальными. Чистой виртуальной функцией является такая функция, у которой в
ее прототипе тело определено как 0.
Динамическое связывание
Новые классы встраиваются при помощи динамического связывания. Если функция в
базовом классе объявлена как virtual и затем вызывается через указатель базового
класса, указывающий на объект производного класса, то программа будет динамически
(т.е. во время выполнения программы) выбирать соответствующую функцию
производного класса. Это называется динамическим связыванием.
Виртуальные деструкторы
Если объект уничтожается явным использованием операции delete над указателем
базового класса на объект, то вызывается деструктор базового класса данного объекта.
Если базового класса деструктор объявлен виртуальным, то при использовании операции
delete будет вызван деструктор соответствующего класса.
Полиморфизм
77

78.

/* Файл Shape.h. Абстрактный класс Shape. В нем имеются чистые
виртуальные функции printShapeName и print */
#ifndef SHAPE_H
#define SHAPE_H
class Shape
{
public:
virtual float area() { return 0.0; }
virtual float volume() { return 0.0; }
virtual void printShapeName() const = 0; // чистая виртуальная функция
virtual void print() = 0; // чистая виртуальная функция
};
#endif
Полиморфизм
78

79.

// Файл point.h. Конкретный класс Point
#ifndef POINT_H
#define POINT_H
#include <iostream>
#include "shape.h"
class Point : public Shape
{
friend ostream& operator<<(ostream&, const Point&);
public:
Point(float = 0, float = 0);
void setPoint(float, float);
float getX() const { return x; }
float getY() const { return y; }
virtual void printShapeName() const override { cout << "Точка:"; }
virtual void print() override;
private:
float x, y;
};
#endif
Ключевое слово override можно использовать для обозначения функций-членов, которые
переопределяют виртуальную функцию в базовом классе.
Полиморфизм
79

80.

// Файл point.cpp. Конкретный класс Point
#include <iostream>
#include "point.h"
Point::Point(float a, float b) { setPoint(a, b); }
void Point::setPoint(float a, float b)
{
x = a; y = b;
}
void Point::print() { cout<<"координаты: ["<<x<<", "<<y<< "]"; }
ostream& operator<<(ostream& output, const Point& p)
{
p.print();
return output;
}
Полиморфизм
80

81.

// Файл circle.h. Конкретный класс Circle
#ifndef CIRCLE_H
#define CIRCLE_H
#include "point.h"
#include <iostream>
class Circle : public Point
{
friend ostream& operator<<(ostream&, const Circle&);
public:
Circle(float x = 0, float y = 0, float r = 0);
void setRadius(float);
float getRadius() const;
virtual float area() override;
virtual void printShapeName() const override
{
cout << "Круг:" << endl;
}
virtual void print() override;
private:
float radius;
};
#endif
Полиморфизм
81

82.

// Файл circle.cpp. Конкретный класс Circle
#include <iostream>
#include "circle.h"
Circle::Circle(float a, float b, float r) :Point(a, b)
{
setRadius(r);
}
void Circle::setRadius(float r)
{
radius = r > 0 ? r : 0;
}
float Circle::getRadius() const { return radius; }
float Circle::area() { return 3.14 * radius * radius; }
void Circle::print()
{
cout << "site: [" << getX() << ", " << getY() <<"] Радиус:"<< radius << " ";
}
ostream& operator<<(ostream& output, const Circle& c)
{
c.print();
return output;
}
Полиморфизм
82

83.

// Файл Cylinder.h. Конкретный класс Cylinder
#ifndef CYLINDER_H
#define CYLINDER_H
#include <iostream>
#include "circle.h"
class Cylinder :public Circle
{
friend ostream& operator<<(ostream&, const Cylinder&);
public:
Cylinder(float x = 0, float y = 0, float r = 0, float h = 0);
void setHeight(float);
virtual float area() override;
virtual float volume() override;
virtual void printShapeName() const override { cout << "Цилиндр:"; }
virtual void print() override;
private:
float height;
};
#endif
Полиморфизм
83

84.

// Файл Cylinder.cpp. Конкретный класс Cylinder
#include <iostream>
#include "cylinder.h"
Cylinder::Cylinder(float x, float y, float r, float h) : Circle(x, y, r)
{
setHeight(h);
}
void Cylinder::setHeight(float h) { height = h > 0 ? h : 0; }
float Cylinder::area()
{
return 2 * Circle::area() + 2 * 3.14 * Circle::getRadius() * height;
}
float Cylinder::volume() { return Circle::area() * height; }
void Cylinder::print()
{
Circle::print();
cout << "Высота: " << height;
}
ostream& operator<<(ostream& output, const Cylinder& c) {
c.print();
return output;
}
Полиморфизм
84

85.

// Файл main.cpp. Реализация полиморфизма
#include <iostream>
#include "shape.h"
#include "point.h"
#include "circle.h"
#include "cylinder.h"
int main() {
Point pnt(2.3, 4.5); // объект класса Point
pnt.printShapeName();
cout << pnt << endl << endl;
Circle crl(1.2, 3.4, 2.0); //объект класса Cirle
crl.printShapeName();
cout << crl << endl << endl;
Cylinder cnd(2.5, 2.5, 3.0, 5.0); // объект класса Cylinder
cnd.printShapeName();
cout << cnd << endl << endl;
Shape* sPtr[n];sPtr[0] = &pnt; sPtr[1] = &crl; sPtr[2] = &cnd;
for (int i = 0; i < 3; i++) {
sPtr[i]->printShapeName();
sPtr[i]->print();
cout << "Площадь: " << sPtr[i]->area() << endl;
cout << "Объем: " << sPtr[i]->volume() << endl;
}
return 0;
}
Полиморфизм
85

86.

Полиморфизм
86

87.

Класс может порождаться более чем от одного базового класса, и такое порождение
называется множественным наследованием. Множественное наследование означает,
что производный класс наследует элементы нескольких базовых классов.
/* Файл base1.h. Описание первого
/* Файл base2.h. Описание первого
базового класса Base1.*/
базового класса Base2.*/
#ifndef BASE1_H
#ifndef BASE2_H
#define BASE1_H
#define BASE2_H
class Base1
class Base2
{
{
public:
public:
Base1(int x) { value = x; }
Base2(char y) { letter = y; }
int getValue() { return value; }
char getValue() {return letter;}
protected:
protected:
int value;
char letter;
};
};
#endif
#endif
Множественное наследование
87

88.

/* Файл derived.h. Описание производного класса Derived. Класс
наследуется от классов Base1 и Base2 */
#ifndef DERIVED_H
#define DERIVED_H
#include"base1.h"
#include"base2.h"
class Derived : public Base1, public Base2
{
friend ostream& operator<<(ostream&, const Derived&);
public:
Derived(int, char, float);
float getValue();
private:
float real;
};
#endif
Множественное наследование
88

89.

// Файл derived.cpp. Реализация производного класса Derived.
#include<iostream>
#include"derived.h"
Derived::Derived(int a, char b, float c) : Base1(a), Base2(b)
{
real = c;
}
float Derived::getValue() { return real; }
ostream& operator<<(ostream& output, const Derived& d)
{
output << "Integer: " << d.value << endl << "Char: " << d.letter <<
endl << "Real:" << d.real;
return output;
}
Множественное наследование
89

90.

// Файл main.cpp. Реализация множественного наследования.
#include<iostream>
#include"derived.h"
#include"base1.h"
#include"base2.h"
int main()
{
Base1* b1Ptr;
Base2* b2Ptr;
Derived Obj(3, 'P', 3.1415);
cout << "Obj содержит:" << endl << Obj << endl << endl;
cout << "Отдельный доступ:" << endl;
cout << "Целое значение: " << Obj.Base1::getValue() << endl;
cout << "Символ: " << Obj.Base2::getValue() << endl;
cout << "Вещественное значение: " << Obj.getValue() << endl;
b1Ptr = &Obj;
cout << "b1Ptr->getValue: " << b1Ptr->getValue() << endl;
b2Ptr = &Obj;
cout << "b2Ptr->getValue: " << b2Ptr->getValue() << endl;
system("Pause");
return 0;
}
Множественное наследование
90

91.

Множественное наследование
91

92.

/* Файл base.h. Описание начального базового класса Base.*/
class Base {
public:
Base() {}
protected:
};
/* Файл base1.h. Описание первого базового класса Base1.*/
class Base1 : public Base {
public:
Base1(int x) { value = x; }
int getValue() { return value; }
protected:
int value;
};
/* Файл base2.h. Описание второго базового класса Base2.*/
class Base2 : public Base {
public:
Base2(char y) { letter = y; }
char getValue() {return letter;}
protected:
char letter;
};
Ромбовидное наследование
92

93.

/* Файл derived.h. Описание производного класса Derived. Класс
наследуется от классов Base1 и Base2 */
class Derived : public Base1, public Base2 {
public:
Derived(int, char, float);
float getValue();
private:
Base
float real;
};
Base1
Base2
Derived
При ромбовидном наследовании конструкторы запускаются в следующем порядке:
Base(), Base1(), Base(), Base2(), Derived()
Ромбовидное наследование
93

94.

/* Виртуальное наследование от класса Base.*/
class Base1 : public virtual Base {
public:
Base1(int x) { value = x; }
int getValue() { return value; }
protected:
int value;
};
/* Виртуальное наследование от класса Base.*/
class Base2 : public virtual Base {
public:
Base2(char y) { letter = y; }
char getValue() {return letter;}
protected:
char letter;
};
При виртуальном наследовании конструкторы запускаются в следующем порядке:
Base(), Base1(), Base2(), Derived()
Ромбовидное наследование
94

95.

KAHOOT.IT
95

96.

ШАБЛОНЫ
96

97.

Шаблоны дают возможность определять при помощи одного фрагмента кода целый набор
перегруженных функций, называемых шаблонными функциями, или набор связанных
классов, называемых шаблонными классами.
Шаблоны функций
Если ряда типа данных должны выполняться идентичные операции, то более компактным
решением является использование шаблонов функций.
#include<iostream>
template < typename T>
void printArray(T* array, const int count) {
for (int i = 0; i < count; i++)
cout << array[i] << " ";
}
int main() {
const int aCount = 5, bCount = 5, cCount = 6;
int A[aCount] = { 1, 2, 3, 4, 5 };
float B[bCount] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
char C[cCount] = "Hello";
cout << "Массив A:" << endl;
printArray(A, aCount);
cout << "Массив B:" << endl;
printArray(B, bCount);
cout << "Массив C:" << endl; printArray(C, cCount);
return 0;
}
Шаблоны функций
97

98.

Шаблон функции может также быть перегружен, введена другая, не шаблонную функция с
тем же самым именем, но другим набором параметров функции.
#include<iostream>
template < typename T>
void printArray(T* array, const int count)
{
for (int i = 0; i < count; i++)
cout << array[i] << " ";
}
template < typename T>
void printArray(T* array, const int count, const char S)
{
for (int i = count - 1; i >= 0; i--)
cout << array[i] << " ";
}
void printArray(const int count, char* array)
{
for (int i = 0; i < count; i++)
cout << array[i] << " " << endl;
}
Шаблоны функций
98

99.

int main()
{
int A[5] = { 1, 2, 3, 4, 5 };
float B[5] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
char C[6] = "Hello";
cout << "Прямой массив A:" << endl;
printArray(A, 5);
cout << " Прямой массив B:" << endl; printArray(B, 5);
cout << " Прямой массив C:" << endl; printArray(C, 6);
cout << " Обратный массив A:" << endl; printArray(A, 5, 'R');
cout << " Обратный массив B:" << endl; printArray(B, 5, 'R’);
cout << " Обратный массив C:" << endl; printArray(C, 6, 'R’);
cout << "Столбец из массива C:" << endl; printArray(6, C);
return 0;
}
Шаблоны функций
99

100.

Если тип формального параметра нельзя определить из вызова функции, то его
необходимо указывать явно, используя треугольные скобки после имени функции.
template <typename T>
T get()
{
T a = 2.5;
return a;
}
int main()
{
cout << get<double>() << endl;
cout << get<int>() << endl;
return 0;
}
Шаблоны функций
100

101.

Шаблоны функций
101

102.

Количество формальных параметров в шаблоне может быть любым
template<typename T1, typename T2>
auto sum(T1 v1, T2 v2)->decltype(v1 + v2)
{
return v1 + v2;
}
int main()
{
cout << sum(3, 2) << endl;
cout << sum(3.2, 2.1) << endl;
cout << sum(3.2, 2) << endl;
cout << sum<int, int>(3.2, 2.1) << endl;
cout << sum(3, "abcdef") << endl;
}
Шаблоны функций
102

103.

Шаблоны классов часто называют параметризованными типами, так как они имеют один
или большее количество параметров типа, определяющих настройку шаблона класса на
специфический тип данных при создании объекта класса.
template<typename T = int>
class point
{
public:
point(T = 0, T = 0);
void set(T, T);
void get();
T length();
private:
T x;
T y;
};
Шаблоны классов
103

104.

template<typename T>
inline point<T>::point(T vX, T vY) {
x = vX; y = vY;
}
template<typename T>
inline void point<T>::set(T vX, T vY) {
x = vX; y = vY;
}
template<typename T>
void point<T>::get() {
cout << "Координаты точки:\n x=" << x << "
}
y=" << y << endl;
template<typename T>
T point<T>::length() {
T L = sqrt(x * x + y * y);
return L;
}
Шаблоны классов
104

105.

#include "point.h"
#include<iostream>
using namespace std;
int main()
{
setlocale(LC_ALL, "Rus");
point<int> P1(10, 20);
P1.get();
cout << "L1 = " << P1.length() << endl;
point<double> P2(10, 20);
P2.get();
cout << "L2 = " << P2.length() << endl;
point<> P3(2.5, 5.1);
P3.get();
cout << "L3 = " << P3.length();
return 0;
}
Шаблоны классов
105

106.

При создании шаблонов имеется возможность перегрузить класс для работы с
конкретным типом данных
template <typename T>
class Spec
{
public:
void inc() { val++; }
T get() { return val; }
void set(T v) { val = v; }
private:
T val;
};
template <>
class Spec<string>
{
public:
void inc() { val = val + "1"; }
string get() { return val; }
void set(string v) { val = v; }
private:
string val;
};
#include<iostream>
#include"Spec.h"
using namespace std;
int main()
{
Spec<double> S1;
S1.set(20);
S1.inc();
cout << "S1="<<S1.get() << endl;
Spec<string> S2;
S2.set("20");
S2.inc();
cout << “S1=" << S2.get() << endl;
system("Pause");
return 0;
}
Шаблоны классов
106

107.

При создании шаблонов имеется возможность перегрузить класс с так называемой
«частичной специализацией»
template <typename T>
class Spec {
public:
void inc() { val++; }
T get() { return val; }
void set(T v) { val = v; }
private:
T val;
};
#include<iostream>
#include"Spec.h"
using namespace std;
int main()
{
Spec<int*> Test;
Test.set();
Test.get();
return 0;
}
template <typename T>
class Spec<T*> {
public:
Spec() { Arr = new T[5]; }
void get() { for (int i = 0; i < 5; i++) cout << Arr[i]; }
void set() { for (int i = 0; i < 5; i++) Arr[i] = i; }
private:
T* Arr;
};
Шаблоны классов
107

108.

В шаблонах имеется возможность использования и так называемых нетиповых
параметров.
template<typename Т, int elements>
// нетиповой параметр
Шаблоны и наследование
Шаблон класса может быть производным от шаблонного класса.
Шаблон класса может являться производным от не шаблонного класса.
Шаблонный класс может быть производным от шаблона класса.
Не шаблонный класс может быть производным от шаблона класса.
Шаблоны и статические элементы
Каждый шаблонный класс, полученный из шаблона класса, имеет собственную копию
каждого статического элемента данных шаблона; все экземпляры этого шаблонного
класса используют свой статический элемент данных. Как и статические элементы данных
не шаблонного класса, статические элементы данных шаблонных классов должны быть
инициализированы в области действия файл. Каждый шаблонный класс получает
собственную копию статической функции-элемента шаблона класса.
Шаблоны классов
108

109.

Шаблоны и дружественность.
Если внутри шаблона класса X, объявленного как template<typename Т> class X
находится объявление дружественной функции f1(); то функция f1 является
дружественной для каждого шаблонного класса, полученного из данного шаблона.
Если внутри шаблона класса X, объявленного как template<typename Т> class X
находится объявление дружественной функции в форме friend void f2(Х<Т> &); то
для конкретного типа T, например, float, дружественной для класса X<float> будет
только функция f2(X<float> &).
Если внутри шаблона класса X, объявленного как template<typename Т> class X
объявляется дружественная функция в форме friend void А::f3(); то функцияэлемент f3 класса A будет дружественной для каждого шаблонного класса, полученного из
данного шаблона.
Внутри шаблона класса X, template<typename Т> class X объявление
дружественной функции в виде friend void C<T>::f4 (Х<Т> &); для конкретного
типа Т, например, float, сделает функцию-элемент C<float>::f4(X<float> &) другом
только шаблонного класса X<float>.
Внутри шаблона класса X, объявленного как template<typename T> class X
можно объявить другой, дружественный класс Y friend class Y; в результате чего,
каждая из функций-элементов класса Y будет дружественной для каждого шаблонного
класса, произведенного из шаблона класса X.
Шаблоны классов
109

110.

joinmyquiz.com
110

111.

Классы с самоадресацией
КЛАССЫ С САМОАДРЕСАЦИЕЙ, СВЯЗНЫЕ
СПИСКИ, СТЕКИ, ОЧЕРЕДИ, ДЕРЕВЬЯ
111

112.

15
10
class Node
{
public:
Node(int) ;
void set(int);
int get();
private:
int data; // данные узла
Node *nextPtr; // указатель на следующий узел
};
Классы самоадресацией
112

113.

Односвязные списки
firstPtr
10
lastPtr

-3
27
Двухсвязные списки
firstPtr

10

-3
27

lastPtr
Связные списки
113

114.

#pragma once
class ListNode
{
public:
ListNode(double);
double getData() const;
double data; // данные
ListNode *nextPtr; // указатель на следующий узел
};
ListNode::ListNode(const double &info)
{
data = info;
nextPtr = 0;
}
double ListNode::getData() const
{
return data;
}
Класс ListNode – узел списка
114

115.

#pragma once
#include "listnd.h"
class List
{
public:
List();
~List();
void insertAtFront(const double &); // вставка узла в начало списка
void insertAtBack(const double &); // вставка узла в конец списка
int removeFromFront(double &); // удаление узла из начала списка
int removeFromBack(double &); // удаление узла из конца списка
bool isEmpty() const {return firstPtr == 0;} // проверка на наличие
узлов в списке
void print() const; // печать всех узлов в списке
private:
ListNode *firstPtr;
ListNode *lastPtr;
ListNode *getNewNode(const double &); // создание нового узла
};
Класс List – односвязный список
115

116.

List::List( )
{
firstPtr = lastPtr = 0;
}
List::~List( )
{
if (! isEmpty( ))
{
cout<<"Удаление узлов..."<<endl;
ListNode *currentPtr = firstPtr, *tempPtr;
while (currentPtr!=0)
{
tempPtr=currentPtr;
cout<<tempPtr->data<<endl;
currentPtr=currentPtr->nextPtr;
delete tempPtr;
}
}
cout<<"Все узлы удалены"<<endl<<endl;
}
Конструктор и деструктор класса List
116

117.

ListNode * List::getNewNode(const double &value)
{
ListNode *ptr = new ListNode (value);
assert(ptr != 0);
return ptr;
}
void List::insertAtFront(const double &value)
{
ListNode *newPtr = getNewNode(value);
if (isEmpty())
firstPtr = lastPtr = newPtr;
else
{
newPtr -> nextPtr = firstPtr;
firstPtr = newPtr;
}
}
Функции класса List
117

118.

firstPtr
newPtr
1.0
lastPtr
Выполнение функций insert при пустом списке
118

119.

firstPtr
newPtr
2.3
1.0
lastPtr
Выполнение функции insertAtFront при непустом списке 119

120.

newPtr
firstPtr
2.3
1.0
7.6
lastPtr
Выполнение функции insertAtBack при непустом списке 120

121.

void List::insertAtBack(const double &value)
{
ListNode *newPtr = getNewNode(value);
if (isEmpty( ))
firstPtr = lastPtr = newPtr;
else
{
lastPtr -> nextPtr = newPtr;
lastPtr = newPtr;
}
}
Выполнение функции insertAtBack
121

122.

firstPtr
tempPtr
1.0
lastPtr
Выполнение функций remove при единичном списке
122

123.

firstPtr
tempPtr
2.3
1.0
7.6
lastPtr
Выполнение функции removeFromFront при неединичном списке 123

124.

int List::removeFromFront(double &value)
{
if (isEmpty())
return 0;
else
{
ListNode *tempPtr=firstPtr;
if (firstPtr = = lastPtr)
firstPtr=lastPtr=0;
else
firstPtr = firstPtr -> nextPtr;
value = tempPtr -> data;
delete tempPtr;
return 1;
}
}
Выполнение функции removeFromFront
124

125.

firstPtr
tempPtr
2.3
1.0
7.6
currentPtr
currentPtr
lastPtr
Выполнение функции removeFromBack при неединичном списке
125

126.

int List::removeFromBack(double &value)
{
if (isEmpty())
return 0;
else
{
ListNode *tempPtr = lastPtr;
if (firstPtr = = lastPtr)
firstPtr = lastPtr = 0;
else
{
ListNode *currentPtr = firstPtr;
while(currentPtr -> nextPtr != lastPtr)
currentPtr = currentPtr -> nextPtr;
lastPtr = currentPtr;
currentPtr -> nextPtr = 0;
}
value = tempPtr -> data;
delete tempPtr;
return 1;
}
}
Выполнение функции removeFromBack
126

127.

flastPtr
firstPtr
2.3
1.0
7.6
currentPtr
currentPtr
currentPtr
currentPtr
2.3 1.0 7.6
Выполнение функции print()
127

128.

void List::print() const
{
if (isEmpty())
{
cout<<"Список пуст"<<endl<<endl;
return;
}
ListNode *currentPtr=firstPtr;
cout<<"Список состоит из..."<<endl;
while(currentPtr!=0)
{
cout<<currentPtr->data<<" ";
currentPtr=currentPtr->nextPtr;
}
}
Функция print()
128

129.

#include <iostream>
#include "list.h"
using namespace std;
int main( )
{
List i_List;
cout<<"Выберите:"<<endl;
cout<<"1 - Вставить в начало списка"<<endl;
cout<<"2 - Вставить в конец списка"<<endl;
cout<<"3 - Удалить из начала списка"<<endl;
cout<<"4 - Удалить из конца списка"<<endl;
cout<<"5 - Завершить обработку списка"<<endl;
int choice;
double value;
Функция main()
129

130.

do
{
cout<<"? ";
cin>>choice;
switch (choice)
{
case 1:
cout<<"Введите любое значение: ";
cin>>value;
i_List.insertAtFront(value);
i_List.print( );
break;
case 2:
cout<<"Введите любое значение: ";
cin>>value;
i_List.insertAtBack(value);
i_List.print( );
break;
Функция main()
130

131.

case 3:
if (i_List.removeFromFront(value))
cout<<value<<" удаляется из списка"<<endl;
i_List.print( );
break;
case 4:
if (i_List.removeFromBack(value))
cout<<value<<" удаляется из списка"<<endl;
i_List.print( );
break;
}
}
while(choice!=5);
cout<<"Конец проверки списка"<<endl<<endl;
system("Pause");
return 0;
}
Функция main()
131

132.

zeroPtr
rightPtr
leftPtr
A
rightPtr
leftPtr
rightPtr
leftPtr
B
C
rightPtr
leftPtr
D
Графическое представление дерева
132

133.

У всех узлов левого поддерева произвольного узла X значения ключей данных меньше,
нежели значение ключа данных самого узла X.
У всех узлов правого поддерева произвольного узла X значения ключей данных больше
либо равны, нежели значение ключа данных самого узла X.
47
77
25
11
7
43
17 31
65
44
93
68
Дерево двоичного поиска
133

134.

1.
2.
3.
4.
5.
Узел либо красный, либо чёрный.
Корень – чёрный.
Все листья (NIL) – чёрные.
Оба потомка каждого красного узла – чёрные.
Всякий простой путь от данного узла до любого листового узла, являющегося его
потомком, содержит одинаковое число чёрных узлов.
47
77
25
11
7
43
NIL
NIL
65
44 NIL
93
68
Красно-черные деревья
NIL
NIL
134

135.

#pragma once
class TreeNode
{
public:
TreeNode(const int &);
int getData() const;
TreeNode *leftPtr;
TreeNode *rightPtr;
int data;
};
TreeNode::TreeNode(const int &d)
{
data=d; leftPtr=rightPtr=0;
}
int TreeNode::getData() const
{
return data; }
Класс узла дерева TreeNode
135

136.

#pragma once
#include <iostream>
#include "treenode.h"
class Tree
{
public:
Tree( ) { rootPtr=0; };
void insertNode(const int &);
void preOrderTraversal( ) const;
void inOrderTraversal( ) const;
void postOrderTraversal( ) const;
private:
TreeNode int *rootPtr;
void insertNodeHelper(TreeNode int **, const int &);
void preOrderHelper(TreeNode int *) const;
void inOrderHelper(TreeNode int *) const;
void postOrderHelper(TreeNode int *) const;
};
Описание класса дерева – Tree
136

137.

void Tree::insertNode(const int &value)
{
insertNodeHelper(&rootPtr, value);
}
void Tree::insertNodeHelper(TreeNode int **ptr, const int &value)
{
if (*ptr == 0)
{
*ptr = new TreeNode int(value);
}
else
if (value < (*ptr) -> data)
insertNodeHelper(&((*ptr) -> leftPtr ),value);
else
if (value>(*ptr)->data)
insertNodeHelper(&((*ptr)->rightPtr),value);
else
cout <<value<<" дубль";
}
Вставка элемента в дерево
137

138.

void Tree::inOrderTraversal() const
{
inOrderHelper(rootPtr);
}
void Tree::inOrderHelper(TreeNode int *ptr) const
{
if (ptr != 0)
{
inOrderHelper(ptr -> leftPtr);
cout<<ptr->data<<" ";
inOrderHelper(ptr -> rightPtr);
}
}
Симметричный обход дерева
138

139.

void Tree::preOrderTraversal() const
{
preOrderHelper(rootPtr);
}
void Tree::preOrderHelper(TreeNode int *ptr) const
{
if (ptr!=0)
{
cout<<ptr->data<<" ";
preOrderHelper(ptr->leftPtr);
preOrderHelper(ptr->rightPtr);
}
}
Прямой обход дерева
139

140.

void Tree::postOrderTraversal() const
{
postOrderHelper(rootPtr);
}
void Tree::postOrderHelper(TreeNode int *ptr) const
{
if (ptr!=0)
{
postOrderHelper(ptr->leftPtr);
postOrderHelper(ptr->rightPtr);
cout<<ptr->data<<" ";
}
}
Обратный обход дерева
140

141.

#include <iostream>
#include <Сstdlib>
#include "tree.h"
int main()
{
srand(25);
Tree binTree; int Val;
cout<<"Ввод 10 случайных значений"<< endl;
for(int i=0; i<10; i++)
{
Val=rand()%21;
cout<<Val<<" ";
binTree.insertNode(Val);
}
cout<<endl<<" Симметричный обход"<<endl;
binTree.inOrderTraversal();
cout<<endl<<"Прямой обход"<<endl; binTree.preOrderTraversal();
cout<<endl<<"Обратный обход"<<endl;
binTree.postOrderTraversal();
return 0;
}
Реализация дерева
141

142.

KAHOOT.IT
142

143.

Умные указатели
КЛАССЫ С САМОАДРЕСАЦИЕЙ, СВЯЗНЫЕ
СПИСКИ, СТЕКИ, ОЧЕРЕДИ, ДЕРЕВЬЯ
143

144.

template <typename T>
class ScopedPtr
{
public:
explicit ScopedPtr(T* ptr);
~ScopedPtr();
T& operator*();
T* operator->();
private:
T* ptr;
};
Next
Реализация умного указателя
144

145.

template< typename T >
ScopedPtr< T >::ScopedPtr(T* ptr) : ptr(ptr)
{
}
template< typename T >
ScopedPtr< T >::~ScopedPtr()
{
delete ptr;
}
template< typename T >
T& ScopedPtr< T >::operator*()
{
return *ptr;
}
template< typename T >
T* ScopedPtr< T >::operator->()
{
return ptr;
}
Реализация умного указателя
Next
145

146.

int main()
{
int* b = new int(10);
ScopedPtr<int> b(new int(10));
cout << "b = " << *b << endl;
*b = 5;
cout << "b = " << *b << endl;
return 0;
}
Next
Реализация умного указателя
146
English     Русский Правила