Similar presentations:
Объектно-ориентрованное программирование
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.IT12
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.IT24
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.IT47
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.IT58
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.IT73
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.IT95
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.com110
111.
Классы с самоадресациейКЛАССЫ С САМОАДРЕСАЦИЕЙ, СВЯЗНЫЕ
СПИСКИ, СТЕКИ, ОЧЕРЕДИ, ДЕРЕВЬЯ
111
112.
1510
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 onceclass 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.
firstPtrnewPtr
1.0
lastPtr
Выполнение функций insert при пустом списке
118
119.
firstPtrnewPtr
2.3
1.0
lastPtr
Выполнение функции insertAtFront при непустом списке 119
120.
newPtrfirstPtr
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.
firstPtrtempPtr
1.0
lastPtr
Выполнение функций remove при единичном списке
122
123.
firstPtrtempPtr
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.
firstPtrtempPtr
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.
flastPtrfirstPtr
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.
zeroPtrrightPtr
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 onceclass 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.IT142
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