Similar presentations:
Объектно - ориентированное программирование
1.
Объектно-ориентированноепрограммирование
Объектно-ориентированное программирование (ООП) — это парадигма
программирования, основанная на концепции «объектов», которые содержат
данные и код: данные в форме полей (часто называемые атрибутами) и код в
форме функций (называемых методами).
ООП реализуется практически на всех современных языках программирования
Лабораторные Контрольная
Экзамен
Бонусы
9+9+9+9+9+5=
50
2 *20 = 40
<=10
10
Содержание курса:
ООП на С++, С#, Java
1
2.
C++• Возник в начале 1980-х годов, когда сотрудник фирмы Bell Labs Бьёрн Страуструп придумал
ряд усовершенствований к языку C под собственные нужды
• Официальная стандартизация языка началась в 1998 году, когда был опубликован стандарт
языка ISO/IEC 14882:1998 (известный как C++98)
• В 2003 г. опубликован стандарт C++ ISO/IEC 14882:2003, где исправлены выявленные ошибки
и недочёты предыдущей версии стандарта
• C++11. В него включены дополнения в ядре языка и расширение стандартной библиотеки
• Следующая версия стандарта, C++14, вышла в августе 2014 года.
• Стандарт C++17, опубликованный в декабре 2017 года, включил в стандартную библиотеку
параллельные версии стандартных алгоритмов и удалил некоторые устаревшие и крайне
редко используемые элементы.
• Последняя стабильная на текущий момент действующая версия стандарта — C++20.
• Никто не обладает правами на язык C++, он является свободным.
Популярные авторы: Шилдт, Майерс, Мартин, Липпман
–
https://msdn.microsoft.com/en-us/default.aspx
–
http://cppreference.en/
2
3.
Некоторые замечания по С++• Варианты инициализации переменных
int x=1;
int x(1);
int x{1};
Фигурные скобки предпочтительней
Варианты инициализации по умолчанию:
int х{}; double y{}; string str{}; char alph{}; bool simb{}; int *ptr{};
0
0.0
пустая строка
‘\0’
false
nullptr
соответственно:
• auto – неявная типизация
auto x=1;
Возможна только для инициализации литералом или выражением в return
auto foo(){
double alpha=0;
alpha+=…
return alpha;
}
• цикл по диапазону
int arr[]={1,2,3,4}
for(auto elem:arr) cout<<elem;
• size_t – unsigned long long int
• using - используется вместо typedef
3
4.
Ссылки (reference)Type& - ссылочный тип
Type x=expression;
Type& rc=x; // rc – ссылка на x
Ссылку называют другим именем переменной, изменение ссылки приводит к изменению
соответствующей переменной.
Инициализация ссылки обязательна, нулевые ссылки (ссылки на «ничто») не
поддерживаются.
void foo(int& arg){/*…*/}
foo(x); //При вызове ссылка arg инициализируется значением x
Можно определить ссылку на константу
const Type d =expression;
const T& rcd = d;
Rvalue-ссылки
Type&& rv = expression;
Rvalue-ссылки представляют отдельный ссылочный тип.
4
5.
array#include <array>
using array <int,3> arr=massiv;
//Использование как параметра функции
int foo(massiv ar){ //
/* ar[0]=…. */
}
//Вызов
massiv arr{0,1,6};
foo(arr);
//Использование как типа функции
massiv foo(…){
{ massiv ar{0,0,0};
ar[0]=2;
return ar;
}
array является классом – контейнером библиотеки STL с соответствующими возможностями (см. раздел STL),
но без автоматического увеличения емкости.
STL
5
6.
ПараБиблиотека utility или любой класс-контейнер из STL
Пара (pair) – cтруктура, два объекта которой могут иметь разные типы
pair<T1 , T2 >
pair<int, double> p = make_pair<int, double>(1, 2.6);
pair<int, string> arr[] = { {1,”Понедельник”}, {2,”Вторник”}, {3,”Среда”} };
p.first=5; arr[0].first //Имя первого элемента пары
p.second=9.8; arr[0].second//Имя второго элемента пары
Две пары, в которых T1 и T2 одинаковы, могут сравниваться между
собой, если в типах определена операция «меньше».
6
7.
Кортеж• Кортеж (tuple) - коллекция элементов с фиксированным размером.
Любая связанная пара, тройка, четверка и т.д. элементов является
кортежем. В качестве элементов кортежа могут выступать объекты
произвольного типа.
• Для кортежей определены функции сравнения и хеширования.
#include <tuple>
typedef std::tuple<int, double, int, double> myTuple;
myTuple c0(0, 1, 2, 3);
cout << std::get<0>(c0) << " "; //Чтение 1-го элемента
cout << std::get<1>(c0) << " "; //Чтение 2-го элемента
get<2>(c0) =8; // Изменение 3-го элемента
c0 = make_tuple(4, 5, 6, 7);//Создание нового значения кортежа
7
8.
Явное преобразования типов с использованием castЯвные static_cast, dynamic_cast, const_cast, reinterpret_cast.
cast-name< type >( expression );
double d = 97.0;
сhar ch =static_cast< char >( d );
reinterpret_cast работает с внутренними представлениями объектов (re-interpret – другая интерпретация того
же внутреннего представления), причем правильность этой операции целиком зависит от программиста.
Например:
int i;
char p[] = "Это строка";
// приведение типа указателя к типу целого
i = reinterpret_cast<int> (p); //Предупреждение компилятора и ошибка
cout << i;
dynamic_cast применяется при идентификации типа во время выполнения (run-time type identification).
Оператор dynamic_cast можно применять для преобразования указателя, ссылающегося на объект типа класса
в указатель на тип класса из той же иерархии
const_cast служит для трансформации константного типа в неконстантный и подвижного (volatile) – в
неподвижный.
extern char *string_copy( char* );
const char *pc_str;
char *pc = string_copy( const_cast< char* >( pc_str ));
8
9.
Перегруженные функцииC++ позволяет указать несколько функций одного и того же имени в
одной области. Эти функции называются перегруженными
функциями. Перегруженные функции должны отличаться друг от
друга списком параметров: их количеством или/и типами.
int print(string s);
int print(double dvalue);
int print(double dvalue, int prec);
Существование перегруженных функций является одной из
разновидностей полиморфизма.
9
10.
ООПСоздание типа данных
Пользовательский тип данных организуется посредством конструкции class /struct
сlass имя_класса {
Члены класса:
-конструкторы;
-поля;
-методы;
-деструктор
};
- Конструктор создает объект класса.
- Значения полей определяют состояние объекта в каждой точке программы.
- Методы (функции) сообщают о состоянии объекта или изменяют его состояние. Набор
методов определяет поведение объекта.
- Деструктор разрушает объект.
Конструкторы и деструктор называются специальными методами
10
11.
class Alpha { // Начало определения// Поля класса
private:
int _x=0;
double _y=0;
public:
//Конструкторы
Alpha (){} // или Alpha()=default
Alpha(int x, double y){ _x=x; _y=y;}
//Alpha(int x, double y): _x(x), _y(y){}
//Методы
void setAlpha( int a, double b){_x=a; _y=b;}
int getX() const { return _x;}
double getY() const {return _y;}
//Деструктор
~Alpha(){} //или ~Alpha()=default;
}; //Конец определения класса
int main() {
double f;
Alpha q; // или Alpha q=Alpha();
Alpha alp1(4,6.0); //Alpha alp1=Alpha(4, 6.0);
Alpha alp2[3]; //массив объектов
alp2[0].setAlpha(7,-5.3);
cout<<alp2[0].getY()<<alp2[0].getX();
Alpha* alp3=nullptr;
alp3= new Alpha;
alp3->setAlpha(1,1);
delete alp3;
alp1:Alpha
}
Объект al1
public и private определяют доступ к членам класса
из точек программы вне определения класса 11
12.
class Beta {public: int x{};
};
При отсутствии в классе явных конструкторов
и деструкторов автоматически создаются
открытые:
• Конструктор
Beta(){}
• Деструктор
~Beta(){}
• Конструктор копирования
Beta (const Beta& other){
this->x=other.x }
int main (){
Beta b1, b2;
b1.x=5;
b2=b1;//Присваивание = почленное копирование
//Конструктор копирования создает новый объект и
//переносит в него состояние объекта-аргумента
Beta b3(b2);
//Другая форма конструктора копирования
Beta b4=b3;
}
Кроме этого пользователю предоставляется
операция присваивания для объектов класса
Если в классе явно определен один из
специальных методов, то аналогичный ему
автоматически не создается
12
13.
//Спецификация класса Complex#include <iostream>
#include <vector>
using namespace std;
//Объявление класса
class Complex
{ //Поля
double real_ = 0;
double image_ = 0;
public:
//Конструкторы
Complex() {}; //Стандартный
//или Complex()=default;
Complex(double, double);//С
параметрами
Complex(const Complex&); //Копирования
// Методы
// Ввод числа
void setComplex(const double&, const double&);
string showComplex() const; //Вывод на консоль
//Перегруженные операции
Complex operator + (const Complex&);// сложения
Complex operator * (const Complex&); //
умножения
Complex operator / (const Complex& );//деления
Complex operator = (const Complex& );
//Копирования
Complex operator -(); //Инверсии знака
//Дружественная функция вывода на консоль
friend void getComplex(Complex);
//Деструктор
~Complex();
//или ~Complex() = default;
};
Дружественными называются функции, которые не
являются членами класса, но имеют доступ к закрытым
его членам. Дружественная функция вызывается без
объекта класса. Хотя бы один из параметров функции
13
должен иметь тип этого класса.
14.
//Реализация класса ComplexComplex::Complex(const Complex& c1) {
this->real_ = c1.real_; this->image_ = c1.image_;
}
Complex::Complex(double r, double im) {
this->real_ = r; this->image_ = im;
}
void Complex::setComplex(const double& r, const double&
im) {
real_ = r; image_ = im;
}
string Complex::showComplex()const {
string str = std::to_string(real_) + " +i" +
std::to_string(image_);
return str;
}
Complex Complex:: operator + (const Complex& t)
{
Complex tmp;
tmp.real_ = this->real_ + t.real_;
tmp.image_ = this->image_ + t.image_;
return tmp;
}
Complex Complex::operator *(const Complex& t) {
Complex tmp;
tmp.real_ = this->real_ * t.real_ - this->image_ * t.image_;
tmp.image_ = this->image_ * t.real_ + this->real_ *
Complex Complex::operator / (const Complex& t) {
Complex tmp;
double buf = t.real_ * t.real_ + t.image_ * t.image_;
//выброс исключения
if (buf == 0) throw 1;
tmp.real_ = (this->real_ * t.real_ + this->image_ *
t.image_) / buf;
tmp.image_ = (this->image_ * t.real_ - this->real_ *
t.image_) / buf;
return tmp;
}
Complex Complex:: operator = (const Complex& c1) {
this->real_ = c1.real_; this->image_ = c1.image_;
return *this;
}
Complex Complex::operator -() {
real_ = -real_; image_ = -image_;
return *this; }
string getComplex(const Complex& c1) {
string str = std::to_string(c1.real_) + " +i" +
std::to_string(c1.image_);
return str;
}
14
15.
Complex cm1, cm2, cm3, cm4;try {
double temp1 = 0, temp2 = 0;
cout << "1-е число " << endl;
cin >> temp1 >> temp2;
cm1.setComplex(temp1, temp2);
cout << "2-е число " << endl;
cin >> temp1 >> temp2;
cm2.setComplex(temp1, temp2);
cout << "3-е число " << endl;
cin >> temp1 >> temp2;
cm3.setComplex(temp1, temp2);
cout << "Сумма \n";
cm4 = cm1 + (cm2 + cm3);
cout << cm4.showComplex() << endl;
cout << "Произведение \n";
cm3 = cm1 * cm2 * cm4;
cout << cm3.showComplex();
cout << "Частное\n";
cm3 = cm1 / cm2;
cout << cm3.showComplex() << endl;
cout << "Массив из 2-х чисел \n";
Complex cm5[] = { {2.1, 3.0},{-4.8, 2.7}
};
cout << "Объект-указатель \n";
Complex* cm6 = new Complex(3, 8);
cout << cm6->showComplex() << endl;
//Копирование объектов
cout << "Объект-копия cm3 \n ";
Complex cm8 = cm3;
cout << getComplex(cm8) << endl;
cout << "Инверсия объекта \n ";
cout << getComplex(-cm8) << endl;
delete cm6;
}//try
catch (int) {
cout << "Деление на 0!\n";
return 1;
}
return 0;
}
15
16.
UML диаграмма классаДиаграмма создана uml-редактором (слева). Справа схема класса, созданная в VS
16
17.
Конструкторы• Конструктор – специальный метод, имеющий имя класса и не возвращающий значения. Конструктор
создает объект класса или во время компиляции или во время выполнения программы функцией new.
• Конструкторов в классе может быть несколько. Отличаться друг от друга они должны списком
параметров – количеством или типами.
• Кроме конструкторов без параметров и с параметрами для создания объектов применяется
конструктор копирования. Конструктор копирования создает новый объект класса и копирует
значения полей уже имеющегося объекта в поля нового.
• В классе конструкторы и деструктор можно не определять явно. В этом случае компилятор
автоматически создает конструктор с пустым списком параметров и пустым телом, конструктор
копирования и деструктор с пустым телом – они называются конструкторами и деструктором по
умолчанию или неявными.
• При наличии в классе поля-указателя применение стандартного конструктора копирования приводит к
созданию двух объектов, у которых указатели имеют одно и то же значение, так что при изменении
одного объекта изменяется и другой. Для предотвращения этого надо создавать конструктор
копирования такой, который делал бы создавал в новом объекте новый указатель и только тогда
выполнял копирование
18.
Доступ к полям объектаСозданный объект имеет доступ к полям и методам класса согласно
расстановке спецификаторов доступа в определении класса. Те члены класса,
которые перечислены после двоеточия, называются в случае public
открытыми, а в случае private закрытыми. К закрытым членам из точки
программы вне класса обращаться нельзя.
int main()
{
Complex cm1;
cm1._real = 1; //Ошибка: поле _real
cm1._image = 4; //Ошибка: поле _image
закрыто (private по умолчанию)
закрыто
cm1.showComplex(); //Верно, метод открыт (public)
}
Доступ к полям и методам объекта, созданного оператором new (cm6),
производится с помощью не точки, а стрелки.
cm6->showComplex();
В классе, созданном с помощью ключевого слова class, по умолчанию все
члены закрыты. Если класс определяется с помощью слова struct, то по
умолчанию все члены public.
18
19.
ДеструкторДеструктор в классе всегда один. Он разрушает объект и, по определению, не может
иметь параметров. Деструктор имеет имя класса с предшествующим символом “~”.
Тело деструктора зависит от полей класса. Если среди них нет указателей и в
конструкторе или методах нет оператора new, их инициализирующего, то тело может
быть пустым. Такой деструктор называется неявным деструктором.
Если среди полей класса есть указатель и используется оператор new, то в деструкторе
следует задействовать оператор delete или delete[]
сlass Gamma {
int *ptr=nullptr;
Gamma() { ptr=new int[2];}
...
//Деструктор
~Gamma(){delete[] ptr; }
};
Деструктор объекта, заданного во время компиляции, вызывается автоматически при
выходе объекта из блока, в котором он был создан.
Деструктор объекта, созданного во время выполнения программы функцией new, надо
вызывать посредством оператора delete.
Деструктор может быть вызван явно
Gamma g1; . . . ~g1.Gamma();
19
20.
Замечания о полях и методах классаПоля
В созданном объекте поля должны быть инициализированы. Инициализация
выполняется конструктором с параметрами или, если такового нет,
значениями, записанными при объявлении поля в классе.
Методы
• У методов класса, реализующих операцию с несколькими операндами,
количество параметров на единицу меньше количества операндов, так как в
качестве одного из операндов выступает объект класса, вызывающий этот
метод. У дружественных функций и статических методов, которые
вызываются без объекта класса, количество параметров не уменьшается.
20
21.
• Современный С++ рекомендует в качестве параметра использовать ссылку наобъект. Объясняется это следующим. В С++ так же, как и в Си, операторы в теле
функции (метода) работают с копией аргумента. Для аргумента-объекта это
означает автоматический вызов конструктора копирования класса. Если среди
членов класса нет поля-указателя (как в классе Complex), то вызов метода с
использованием неявного конструктора копирования закончится успешно.
Если такое поле есть, то следует специально создать конструктор, копирующий
объект с учетом наличия в нем указателя.
Ссылка или указатель на параметр метода позволяет избежать вызова
конструктора копирования.
Если параметр не изменяется в теле функции, то следует использовать const:
double foo(const double& x, const int& y){ return x*y; }
• В методе, в сигнатуре которого const помещен перед телом, запрещены
изменения полей класса.
auto get() const {return ….}
21
22.
Перегрузка операцийтип_возвращаемого_значения operator знак_опеpации ([параметр]) {... }
Complex Complex::operator + (const Complex& t)
{
Complex tmp;
Complex Complex:: operator - ()
{
_real =-_real;
_image = -_image;
tmp._real = _real + t._real;
return *this;
tmp._image =_image + t._image;
}
return tmp;
Вызовы: cm1.operator-() или -cm1
}
Вызовы: cm1.operator + (cm2)
или cm1+cm2
При перегрузке остаются неизменными приоритет операции и количество ее операндов
22
23.
Динамический полиморфизм перегруженных операцийint x1=2, x2=3, x3;
Complex cm1(2,3), cm2(5,8), cm3;
x3=x1+x2;
cm3=cm1+cm2;
cout<<(x1<<2)
операция перегружена в классе ostream для аргументов стандартного типа
cout.operator<< (arg)
Перегрузка перегруженной операции << для аргумента типа Complex
friend ostream& operator << (ostream& out, Complex& t) {
out << t._real << " +i" << t._image << '\n’;
return out;
}
Вызов сout<<cm3;
23
24.
double Triangle::getPerimetr() {Отношения между классами
double a = sqrt(pow(xVertice[1] - xVertice[0], 2) +
pow(yVertice[1] - yVertice[0], 2));
//Класс треугольников – одиночный класс
class Triangle {
double b = sqrt(pow(xVertice[2] - xVertice[1], 2) +
pow(yVertice[2] - yVertice[1], 2));
//Координаты вершин
int xVertice[3] = {0};
int yVertice[3] = {0};
double c = sqrt(pow(xVertice[2] - xVertice[0], 2) +
pow(yVertice[2] - yVertice[0], 2));
return a + b + c;
public:
//Конструктор
Triangle() {};
}
int main() {
Triangle tr1;
cout << "Введите координаты\n";
tr1.setTriangle();
cout<<“Периметр\n"<<tr1.getPerimeter()<<endl;
//Установка координат вершин
void setTriangle();
//Определение периметра
double getPerimeter();
};
void Triangle::setTriangle() {
for (int i = 0; i < 3; i++) {
cin >> xVertice[i]>> yVertice[i];
}
}
}
Вывод
Введите координаты
1-й вершины 1 1
2-й вершины 2 2
3-й вершины 2 1
Периметр 3.41421
24
25.
Включениеstruct Point { double x=0, y=0; };
class Triangle1 {
Point* vert = nullptr;//Массив для координат
public:
//Конструктор
Triangle1() {vert = new Point[3];
~Triangle1() {delete vert;};
double getPerimeter() {
double a = sqrt(pow(vert[1].x - vert[0].x, 2) +
pow(vert[1].y - vert[0].y, 2));
double b = sqrt(pow(vert[2].x - vert[1].x, 2) +
pow(vert[2].y - vert[1].y, 2));
double c = sqrt(pow(vert[2].x - vert[0].x, 2) +
pow(vert[2].y - vert[0].y, 2));
return a + b + c;
}
Конец лекции
void setTriangle() {
for (int i = 0; i < 3; i++) {
cin >> vert[i].x >> vert[i].y;
}
}
~Triangle1() {
delete[] vert;
}
};
int main()
Triangle1 tr1;
tr1.setTriangle();
cout<<tr1.getPerimeter();
}
25
26.
Варианты включения1.
class A {
public:
int a = 0;
void fooA() { cout << a << endl; }
};
2. То же, но с указателями
class A {
public:
int a = 0;
void fooA() { cout << a << endl; }
};
class B {
public:
A a2;
B(){ }
void fooB(int x) { a2.a = x;
//Делегирование
a2.fooA(); }
};
class B {
public:
A* a2=nullptr;
B(){a2=new A;}
void fooB(int x) { a2->a = x;
a2->fooA(); }
~B(){delete a2;}
};
-----------------------B b1;
b1.fooB(5);
cout << b1.a2.a << endl;
------------------------------------B b1;
b1.fooB(5);
26
27.
3. С параметрическим конструкторомclass A {
public:
int a = 0;
void fooA() { cout << a << endl; }
};
class B {
public:
A* a2=nullptr;
B(A* a3) {
a2 = a3;
}
void fooB(int x) { a2->a = x; }
~B()[ delete a2;]
};
-----------------------------------------
1. A* a1 = new A;
B b1(a1); или B b1(new A);
b2.fooB(5);
2. B* b2=new B(new A);
b2->fooB(6)
27
28.
Зависимость (использование)//Класс Студент
class Student {
string name {}; //Имя студента
string group {}; //Группа
string notepad {}; //Блокнот
public:
//Конструктор
Student(string n, string g):name(n),group(g) {}
//Деструктор
~Student(){}
//Добавление записей в блокнот
void updateNotepad(string inf) { notepad +=inf; }
//Демонстрация содержимого блокнота
string showNotepad() { return notepad; }
};
//Класс Учитель
class Teacher {
string name {}; // Имя преподавателя
string desk = {}; //Доска
Student st;
public:
//Конструктор
Teacher(string n) : name(n) { }
//Деструктор
~Teacher() {}
//Изменяет состояние доски
void writeDesk(string inf) { desk = inf; }
//Показывает состояние доски
string showDesk() const { return desk; }
//Передает студенту информацию
void messageStudent(Student& st, string inf) {
st.updateNotepad(inf) ; }
};
28
29.
int main() {//Создаем преподавателя и студента
Teacher t("Сидоров");
Student s1 ("Иванов", "KTбо-1");
//Teacher записывает на доске
t.writeDesk("Привет, ");
//и передает информацию студенту
t.messageStudent(s1, t.showDesk());
// Можно проверить что записано в блокноте
cout << s1.showNotepad() << endl;
//Teacher добавляет информацию на доску
t.writeDesk("мир");
//и опять заставляет студента записывать
t.messageStudent(s1, t.showDesk());
//Окончательная запись в блокноте
cout << s1.showNotepad() << endl;
}
Привет,
Привет, мир
29
30.
Статические члены класса: поле и методclass Alpha {
int _al=0;
//Объявление статической переменной
static int count;
public:
Alpha(int al=0) {
_al = al;
count++;
}
//Статический метод
static int getCount( ) {
return count;
}
void setAl(int p) {
_al = p;
}
};
//Инициализация статической переменной выполняется
//вне класса
int Alpha::count = 1;
int main()
{
Alpha m1, m2(5), m3(7);
//Вызов метода с объектом класса
cout << m1.getCount()<< m2.getCount()<< m3.getCount();
//Вызов метода без объекта класса
cout << Alpha::getCount();
//Изменение нестатического поля
m3.setAl(8);
}
Статическая переменная count увеличивает свое
значение при каждом вызове конструктора и во всех
объектах класса имеет одно и то же значение, равное 4.
Локальное поле _al ведет себя обычным образом – в
объекте m3 имеет значение 8, в m2 -5,а в m1 – 0 по
умолчанию.
Статический метод может вызываться как через объект
класса, так и без него. В его теле используются
30
статические поля класса и глобальные поля любого типа.
31.
Вложенные классыNested class - класс, определение которого находится внутри другого класса. Обычно вложенные классы
применяются для описания таких сущностей, которые могут существовать только в рамках объекта
внешнего класса, особенно когда внешний класс работает с набором объектов вложенного класса.
class BufferedIO
{
public:
enum IOError { None, Access, General };
// Вложенный класс
class BufferedInput
{
public:
int read();
int good()
{
return _inputerror == None;
}
private:
IOError _inputerror;
};
// Вложенный класс
class BufferedOutput
{
// Member list
};
BufferedInput bI;
BufferedOutput bO;
};
int main() {
BufferedIO bIO;
bIO.bI.read();
}
31
32.
Обработка исключительных ситуацийИсключительная ситуация (исключение) рассматривается как ошибка выполнения;
различают логические ошибки (logic errors) и ошибки времени выполнения (runtime
errors). Различного рода исключения определены как типы в стандартном классе
exception. Cреди логических ошибок:
– invalid_argument – использование неверного аргумента при вызове функции;
– length_error – попытка создания слишком большого объекта;
– out of range – попытка обращения к элементу вне заданного диапазона.
К ошибкам времени выполнения относятся:
– range_error – слишком большое или слишком маленькое число с плавающей
точкой.
– overflow_error – слишком большое число с плавающей точкой.
Появление исключительной ситуации приводит к прекращению работы программы с
кодом, отличным от 0. Например, при выходе индекса цикла за пределы строки
программа прерывается с кодом завершения 3.
32
33.
В С++ работа с исключениями формализована с помощью блоков try, catch и оператора throw.В блок try помещается вся программа или ее фрагмент, в котором, по мнению программиста, может
возникнуть исключительная ситуация. Непосредственно за этим блоком следует блок catch с
параметром, в котором выполняются действия, определенные пользователем на этапе проектирования, и
которые носят название обработки исключения.
Оператор throw генерирует исключение и помещается в той точке программы, где создаются условия
возникновения ошибки (предупреждает исключительную ситуацию).
if (условие возникновения ошибки) throw выражение;
При выполнении условия возбуждается (выбрасывается) исключение, которое перехватывается и
обрабатывается в блоке catch. При этом тип выражения должен совпадать с типом параметра catch.
В методе changeChar сравнивается значение параметра index и количество символов в строке. Если
сравнение не проходит из-за значения параметра index, то возбуждается стандартное исключение типа
out_of_range с соответствующим сообщением. Параметром catch является объект e класса
exception, имеющий метод what. Результатом обработки является сообщение в потоке cerr,
предназначенном для вывода сообщений об ошибках, и прекращение работы программы с кодом –1.
В блоке try может быть размещено несколько операторов throw, реагирующих на различные типы
исключений. Количество блоков catch должно совпадать с количеством типов исключений,
генерируемых различными операторами throw.
Выражение в throw и тип параметра в catch могут иметь не только тип стандартного исключения, но и
любой другой тип, в том числе и пользовательский. Если вместо параметра в catch используются три
точки, то обработчик перехватывает любое исключение
33
34.
class Greeting {public:
string str{};
void changeChar(char s, size_t index) {
if (index < str.size())
str[index] = s;
else throw out_of_range("Индекс за пределами строки");
}
};
int main()
{
try {
Greeting gr;
gr.str = "Привет!";
gr.changeChar('?’, 6);
}
catch (out_of_range& e) {
cerr << e.what();//Сообщение
return -1; //Обработка
}}
34
35.
Механизм обработки исключений, вообще говоря, обеспечивает корректное завершение программы: привозникновении исключительной ситуации в блоке try с помощью деструктора уничтожаются созданные в
начале блока объекты и сворачиваются соответствующие открытые стеки; при сбоях в работе с файлами
последние закрываются и т.д.
class MyException {
public:
int x=0;
MyException () {cout << "Конструктор\n"; }
~ MyException () { cout << "Деструктор\n"; }
};
void foo() {
try{
MyException a1;
cin >> a1.x;
if (!a1.x) throw 1;
cout << "Продолжение работы foo!\n";
}
catch (int ){
cout << "Обработка исключения!\n"; }
}
int main()
{
foo();
cout << "Продолжение программы\n";
}
Сообщения на мониторе:
• без исключения (x=1)
Конструктор
Продолжение работы foo!
Деструктор
Продолжение программы
• c исключением (x=0)
Конструктор
Деструктор
Обработка исключения!
Продолжение программы
35
36.
Класс fstreamНаиболее часто встречаемые операции:
• Операторы перенаправления ввода/вывода – << и >>
• Методы записи и чтения строк и символов getline(), get(), put()
• Потоковая запись и чтение методами write() и read()
• Методы открытия/создания и закрытия файлов open() и close()
• Методы проверки открыт ли файл is_open() и достигнут ли конец файла eof()
• Настройка форматированного вывода для >> с помощью width() и precision()
• Операции позиционирования tellg(), tellp() и seekg(), seekp()
36
37.
Класс ifstreamПредоставляет возможности для чтения файлов.
Открыть файл (связать файловый объект с реальным файлом) можно двумя способами:
вызвав метод open() или указав путь к нему в конструкторе класса
#include <iostream>
#include <ifstream> // подключаем библиотеку
int main()
{
ifstream inFile; // создаем объект класса ifstream
inFile.open("d:\\1\\test.txt“); // открываем файл
}
или
int main()
{ ifstream inFile ("d:\\1\\test.txt"); }// открываем файл в конструкторе
Проверка открытия
try{
if (inFile.is_open()) // вызов метода is_open()
if (!inFile) throw 1;
// Чтение содержимого
……
else
}
{
catch (int) {
cout << "Файл не открыт!\n";
cerr<<“File not\n”;
return -1;
return -1;
}
}
37
38.
Кроме имени при открытии файла могут указываться атрибуты в формате ios::режимstring filename;
inFile.open (fileName, ios::in | ios::binary);
Режим
Назначение
in
Открыть для ввода (выбирается по умолчанию для ifstream)
out
Открыть для вывода (выбирается по умолчанию для ofstream)
binary
Открыть файл в бинарном виде
aрр
Присоединять данные; новая запись фиксируется в конце файла
ate
Установить файловый указатель на конец файла
trunc
Уничтожить содержимое, если файл существует (выбирается по
умолчанию, если флаг out указан, а флаги ate и арр — нет)
38
39.
• Оператор считывания >> для текстового файла.Работает до конца строки файла.
double d;
int i;
string s;
inFile >> d >> i >> s;
• getline() Считывание целой строки до перевода
каретки
string s;
getline(inFile, s);//getline - функция
cout << s << endl;
read (char* s, int n); считывание последовательности
(массива) байтов из бинарных файлов; n – количество
байтов
Пример далее.
• Метод eof() Проверяет достигнут ли конец файла
при чтении.
• Метод close() Закрывает файл – разрывает связь
между файловым объектом и файлом.
• get() Считывание одного или нескольких символов
int n = 10;
//Создаем буфер для чтения
char* buffer = new char[n+1];
buffer[n]=0;
//Читаем n символов
inFile.get(buffer, n);
//Освобождаем буфер
delete [] buffer;
39
40.
• Метод seekg()Производит установку указателя файла с текущей позиции в нужную, указываемую числом.
ios_base::end – Отсчитать новую позицию с конца файла
ios_base::beg – Отсчитать новую позицию с начала файла
ios_base::cur – Перескочить на n байт начиная от текущей позиции в файле (по умолчанию)
inFile.seekg(0, ios_base::end); //Переходим в конец файла
inFile.seekg(10, ios_base::end); // Переходим на 10 байтов от конца
file.seekg(30, ios_base::beg); // Переходим на 31-й байт
inFile.seekg(3, ios_base::cur); // Перепрыгиваем через 3 байта от текущей позиции
или то же
inFile.seekg(3);
• Метод tellg() Возвращает число прочитанных байт
//Переходим в конец файла
inFile.seekg(0, ios_base::end);
cout << "Размер файла (в байтах): " << (int)inFile.tellg();
40
41.
Класс ofstreamИспользуется для записи в файл. Открытие, проверка открытия и закрытие файла выполняются аналогично ifstream.
Если указываемый файл не существует, то он будет создан. Используются режимы out, app, trunc.
• Оператор << Перенаправляет форматированный вывод в текстовый файл. Совместно с << могут использоваться
манипуляторы width, precision
#include <fstream>
#include <iomanip>
using namespace std;
ofstream outFile((“D:\\test.txt“);
int main(){
double d = 1;
for (int i = 0; i < 10; i++) {
d += sin(i / d);
// Указываем ширину ячейки для целого и выводим целое
outFile.width(10);
outFile << i;
// Указываем ширину ячейки для вещественного и
// кол-во знаков после запятой
outFile.width(10);
outFile.precision(5);
outFile << d;
outFile << endl;
}
outFile.close();
}
0
1
2
3
4
5
6
7
8
9
1
1.8415
2.7263
3.6177
4.5114
5.4064
6.302
7.198
8.0944
8.9909
41
42.
Метод write (char* s, int n) Используется в бинарных файлах для записи блока памяти (массивабайт) в файл. В ООП применяется для записи в файл состояния объекта класса. Для чтения
объекта из файла используется read().
class Point {
public:
int x, y = 0;
Point(int x, int y) {
this->x = x;
this->y = y;
}
Point() {}
};
Аналогичный процесс в С#
называется сериализацией
(serialize). Сериализация во
многих случаях экономит код по
сравнению со стандартными
средствами IO.
int main() {
Point p1(3,4); // Объекты класса
Point p2(7, 8);
fstream outFile1("D:\\Data.dat", ios::out| ios::binary);
//Записываем в файл
outFile1.write((char*)&p1, sizeof(Point));
outFile1.write((char*)&p2, sizeof(Point));
outFile1.close();
//Читаем из файла
fstream outFile1("D:\\Data.dat", ios::in | ios::binary);
Point p;
outFile1.read(reinterpret_cast<char*>&p, sizeof(Point));
cout << p.x << " "<< p.y;
outFile1.read((char*)&p, sizeof(Point));
cout << endl<<p.x << " " << p.y;
outFile1.close();
}
34
78
42
43.
• Метод put (char ch) заносит в файл один символ//Записываем в файл символы, вводимые с клавиатуры.
do {ch = std::cin.get();
outFile.put(ch);
} while (ch!='.’);
• Методы tellp, seekp используются с теми же целями, что и tellg, seekg при чтении файлов.
• После записи информации файл закрывать обязательно.
43
44.
Библиотека sstreamsstream — заголовочный файл с
классами, функциями и переменными
для организации работы со строками
через интерфейс потоков.
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
int val=123;
stringstream geek;
geek << val;
string x;
geek >> x;
cout<<x+"4"<<endl;
return 0;
}
1234
Временная сложность: O(n) ,
n — длина целого числа.
44
45.
Два принципа создания класса«Ключом к написанию хорошей программы является разработка
таких классов, чтобы каждый из них представлял одно основное
понятие. Обычно это означает, что программист должен
сосредоточиться на вопросах: Как создаются объекты этого класса?
Могут ли эти объекты копироваться и/или уничтожаться? Какие
действия можно производить над этими объектами? Если на такие
вопросы нет удовлетворительных ответов, то, во-первых, скорее
всего, понятие не было "ясно", и, может быть неплохо еще немного
подумать над задачей и предлагаемым решением вместо того,
чтобы сразу начинать "программировать вокруг" сложностей»
Б.Страуструп
45
46.
Принцип единственной ответственностиОзначает, что каждый класс работает только над одной целью, ответственен только за неё и
изменяется только по одной причине.
Мартин определяет ответственность как причину изменения и заключает, что классы должны
иметь одну и только одну причину для изменений. Например, существует класс, который
составляет и печатает отчёт. Такой класс может измениться по двум причинам:
• может измениться само содержимое отчёта
• может измениться форма отчёта.
Логично, что оба аспекта этих причин на самом деле являются двумя разными
ответственностями. В таком случае нужно разделить класс на два новых класса, для которых
будет характерна только одна ответственность. Причина, почему нужно сохранять
направленность классов на единственную цель в том, что это делает классы более
здоровыми. Что касается класса, упомянутого выше, то в случае изменения в процессе
составления отчёта есть большая вероятность, что в негодность придёт код, отвечающий за
печать.
Божественный объект ( God object) — антипаттерн объектно-ориентированного
программирования, описывающий объект, который хранит в себе «слишком много» или
делает «слишком много».
Мартин Роберт С. Быстрая разработка программ. Принципы, примеры, практика. 2004
46
47.
Принцип открытости/закрытостиКласс должен быть открытым для расширения, но закрытым для изменений.
Проще говоря, вы можете добавлять новую функциональность в класс, но не
можете редактировать существующие функции таким образом, что они будут
противоречить используемому коду
Принцип открытости/закрытости означает, что программные сущности
должны быть:
• открыты для расширения: поведение сущности может быть расширено
путём создания новых типов сущностей;
• закрыты для изменения: в результате расширения поведения сущности не
должны вноситься изменения в код, который эту сущность использует.
Однажды разработанная реализация класса в дальнейшем требует только
исправления ошибок, а новые или изменённые функции требуют создания
нового класса. Этот новый класс может повторно использовать код исходного
класса через механизм наследования.
47
48.
Умные указатели (smart pointers)Одним из основных источников ошибок в программировании на C/C++
является утечка памяти. Утечки часто возникают из-за невозможности вызвать
delete для памяти, выделенной с помощью new. Современный C++
придерживается принципа: получение ресурса есть инициализация (Resource
Acquisition Is Initialization (RAII)). Идея проста. Ресурсы (heap, дескрипторы
файлов, сокеты и т. д.) должны принадлежать объекту. Этот объект создает и
получает новый выделенный ресурс в конструкторе и удаляет его в его
деструкторе. Принцип RAII гарантирует, что все ресурсы должным образом
возвращаются операционной системе, когда объект-владелец выходит за
пределы области.
Одной из реализацией RAII являются умные указатели
Интеллектуальные указатели (современный C++):
https://learn.microsoft.com/ru-ru/cpp/cpp/smart-pointers-moderncpp?view=msvc-170
48
49.
Smart pointers - объекты шаблонных классов:•unique_ptr
•weak_ptr
•shared_ptr
Классы имеют деструкторы, разрушающие соответствующие объекты при выходе из области
видимости.
shared_ptr имеет следующие конструкторы:
•shared_ptr<Type> имя (new Type); //то же, что и тип *имя=new тип
•shared_ptr<Type[]>имя(new Type[целое]);
и методы:
•make_shared создает указатель на объект;
•reset сбрасывает или заменяет объект, которым владеет
STL
49
50.
include <memory>class A { public: int x; };
int main()
{
//Стандартные указатели
A* ptrA = new A;
ptrA->x = 9;
delete ptrA;
//Умные указатели
shared_ptr<A> ptrAs(new A);
ptrAs->x = 5;
shared_ptr<A[]> arr(new A[5]);
arr[0].x = 4;
//Умные указатели разрушаются автоматически
}
50
51.
Массив с задаваемой длинойclass Array1D {
public:
int length = 0;
shared_ptr<int[]> ptrD; //int* ptrD
//Конструкторы
Array1D(int);
Array1D(shared_ptr<int[]>, int);
Array1D(const Array1D&);
//Методы
public:
void viewArray();
void setArray();
Array1D operator+(Array1D&);
~Array1D(){}
};
Array1D::Array1D(shared_ptr<int[]> tmp, int n) {
length = n;
ptrD.reset(new int[length]);
for (int i = 0; i < length; i++)
ptrD[i] = tmp[i];
}
Array1D::Array1D(const Array1D& s) {
length = s.length;
ptrD.reset(new int[length]);
for (int i = 0; i < length; i++)
ptrD[i] = s.ptr[i];
}
Array1D::Array1D(int p) {
length = p;
ptrD.reset(new int[length]);
for (int i = 0; i < length; i++)
ptrD[i] = 0;
}
void Array1D::setArray() {
for (int i = 0; i < length; i++)
cin >> ptrD[i];
}
void Array1D::viewArray() {
for (int i = 0; i < length; i++)
cout << ptrD[i] << " ";
}
Array1D Array1D::operator+(Array1D& s) {
shared_ptr<int[]> tmp(new int[s.length]);
for (int i = 0; i < length; i++)
tmp[i] = s.ptrD[i] + ptrD[i];
Array1D ret(tmp, s.length);
return ret;
}
STL
51
52.
int main(){
Array1D ar1(3);
ar1.setArray();
Array1D ar2(3);
ar2.setArray();
Array1D ar3 = ar1 + ar2;
ar3.viewArray();
//Разрушение объектов выполняется автоматически
}
STL
52
53.
Дополнительные темы1. Указатели на функцию
2. Обратный вызов
Наследование и полиморфизм в С++
53
54.
Указатель на функциютип (*имя_указателя)(параметры);
указатель на функцию – ее имя
int main()
{
void (*message)();
void hello()
{
cout <<"Hello, World\n";
}
void goodbye()
{
cout <<"Good Bye, World\n";
}
message=hello;
message();
message = goodbye;
message();
}
return 0;
Hello, World
Good Bye, World
Указатель на функцию – адрес ее первого
выполняемого оператора.
STL
54
55.
Массив указателей на функциютип (*имя_массива[размер]) (параметры)
int main()
{
int a = 10;
int b = 5;
double (*operations[3])(int, int) = {add, subtract, multiply};
// возвращаем длину массива
int length = sizeof(operations)/sizeof(operations[0]);
for(int i=0; i < length;i++)
{ // Вызов функций
cout<<operations[i](a, b)<<‘n’; }
return 0;
Сигнатура следующих функций: double имя (int, int)
double add(int x, int y)
{
return x + y;
}
double subtract(int x, int y)
{
return x - y;
}
double multiply(int x, int y)
{
return x * y;
}
}
15
5
50
Здесь массив является контейнером для указателей на
функции
STL
55
56.
Обратный вызов (callback)имя функции используется в качестве параметра
Функция integral вычисляет определенный интеграл по методу прямоугольников.
Подинтегральная функция имеет сигнатуру double имя (double)
double integral(double a, double b, int M, double(*subIntegral)(double)) {
double sum = 0;
double step=(b - a) / M;
for (int i = 0; i < M; i++)
sum += step * (*funct)(a + i * step / 2);
return sum;
}
int main(){
integral (0,1,10, std::sin);
integral (2,3,12, std::cos);
}
STL
56