This commit is contained in:
nihonium 2022-09-14 19:05:27 +03:00
parent 46d1c64684
commit ab6732eded
Signed by: nihonium
GPG key ID: 0251623741027CFC
98 changed files with 10319 additions and 0 deletions

View file

@ -0,0 +1,34 @@
/*
Объектно-ориентированное программирование (ООП) основано на представлении программы в виде совокупности взаимодействующих объектов
Основные принципы ООП: абстракция, инкапсуляция, наследование и полиморфизм
Абстракция: использование только тех характеристик объекта, которые с достаточной точностью представляют его в данной системе.
В каком-то смысле обычные структуры из языка C являются примером абстракции
struct book
{
char title[100];
float price;
int pages;
};
Для описание книги в коде мы используем лишь некоторое её характеристики, достаточные для нашей задачи
Инкапсуляция: связывание данных некоторого абстрактного объекта и функций для работы с ним
Тесно связано с инкапсуляцией такое понятие как сокрытие
Сокрытие: разделение данных и функций абстрактного объекта на открытые (видимые извне) и скрытые (видимые только внутри самого объекта)
Наследование и Полиморфизм будут пройдены позже в курсе
*/
int main(){}

View file

@ -0,0 +1,54 @@
/*
Хоть язык C и не является объектно-ориентированным, некоторые зачатки подходов ООП в нём тоже есть
Например, структуры языка C являются примером Абстракции
Для работы со структурами мы обычно писали функции так, что каждая из этих функций принимает на вход первым аргументом указатель на наш объект.
Такой подход НЕ является примером Инкапсуляции, так как структура и функции для работы с ней являются независимыми друг от друга.
При желании или по ошибке можно первым аргументом в эти функции передать вообще объект другого типа.
Эта программа написана на языке C, для компиляции используйте gcc:
gcc 00book.c
Функции в этом примере делают следующее:
make_discount сделать скидку на книгу, но цена на книгу не может упасть ниже 0.
print_book напечатать информацию о книге на экран
*/
#include <stdio.h>
struct book
{
char title[100];
float price;
int pages;
};
typedef struct book Book;
void make_discount(Book* pb, int discount)
{
if (pb->price > discount)
pb->price -= discount;
else
pb->price = 0;
}
void print_book(const Book* pb)
{
printf("%s, price = %.2f, pages = %i\n", pb->title, pb->price, pb->pages);
}
int main()
{
Book b = {"War and Peace", 1700, 900};
print_book(&b);
make_discount(&b, 500);
print_book(&b);
}

View file

@ -0,0 +1,44 @@
/*
Эта программа написана на языке C++, для компиляции используйте g++:
g++ 01book.cpp
В языке C++ появились ссылки, которые могут немного упростить код из предыдущего файла
Тем не менее, структура Book и функции для работы с ней всё ещё являются независимыми друг от друга.
То есть тут тоже нет Инкапсуляции.
*/
#include <iostream>
struct Book
{
char title[100];
float price;
int pages;
};
void makeDiscount(Book& b, int discount)
{
if (b.price > discount)
b.price -= discount;
else
b.price = 0;
}
void printBook(const Book& b)
{
std::cout << b.title << ", price = " << b.price << ", pages = " << b.pages << std::endl;
}
int main()
{
Book b = {"War and Peace", 1700, 900};
printBook(b);
makeDiscount(b, 500);
printBook(b);
}

View file

@ -0,0 +1,91 @@
/*
Инкапсуляция - это объединение данных и функций для работы с ними
Объекты-данные, составляющие наш объект, называются полями
Функции для работы с этими данными называются методами
То есть у структуры Book из этого примера есть 3 поля (title, price и pages) и 2 метода (makeDiscount и print)
Сравните код в этом файле с кодом из предыдущего файла и обратите внимание на следующие моменты:
1) Функции для работы со структурой сейчас объявляются внутри структуры.
Получается методы как-бы принадлежат самой структуре
Это делает невозможным использование этих функций (случайно или намерено) для работы с объектами других типов.
2) Вызов методов осуществляется с помощью точки, то есть такой вызов из прошлого файла:
makeDiscount(b, 500);
заменился на такой:
b.makeDiscount(500);
То есть объект как бы сам вызывает функцию для работы со своими данными, а не передаётся первым аргументом в функцию.
3) Методы "знают" о том объекте, который их вызвал
Например, в методе makeDiscount используется поле price без указания объекта, которому принадлежит это поле
Но метод сам "знает" какой объект его вызвал, поэтому если его вызывает объект a вот так:
a.makeDiscount(500);
то в этом случае метод использует поле price объекта a
4) Константный метод не меняет полей вызывающего объекта.
Чтобы указать, что метод является константным нужно написать const в конце объявления метода
В предыдущем файле при передаче по константной ссылке передаваемый объект не мог измениться внутри функции
void printBook(const Book& b) -> printBook(b) не изменит b
Аналог этого для константного метода:
void print() const -> b.print() не изменит b
Следовательно, внутри константного метода нельзя менять поля объекта
*/
#include <iostream>
struct Book
{
char title[100];
float price;
int pages;
void makeDiscount(int discount)
{
if (price > discount)
price -= discount;
else
price = 0;
}
void print() const
{
std::cout << title << ", price = " << price << ", pages = " << pages << std::endl;
}
};
int main()
{
Book a = {"War and Peace", 1700, 900};
Book b = {"The Master and Margarita", 600, 400};
a.print();
a.makeDiscount(500);
a.print();
}
/*
Задачи:
1) Напечатайте книгу b
2) Сделайте скидку для книги b в 1000 рублей и напечатайте её ещё раз
3) Напишите метод void setPrice(float newPrice) который будет задавать новую цену книги
Вызовите этот метод для книги b и установите её цену в 1000 рублей. Напечатайте книгу ещё раз.
4) Попробуйте изменить поле внутри константного метода print, к какой ошибке это приведёт?
5) Можно ли вызвать метод makeDiscount из константного метода?
*/

View file

@ -0,0 +1,56 @@
/*
Задачи: Представлена структура Movie, описывающая фильм на Кинопоиске
title - название фильма
releaseYear - год выхода
numVotes - число оценок этого фильма на Кинопоиске
rating - рейтинт фильма на Кинопоиске
1) Напишите метод setReleaseYear, который будет принимать число и устанавливать новый год выхода фильма,
равный этому числу. Этот метод не должен ничего возвращать.
При этом, минимальный год выхода фильма должен быть 1900. При попытке установить меньший год выхода, метод
всё-равно должен устанавливать год, равный 1900.
2) Установите год выхода фильма a на 1998, используя метод setReleaseYear. Напечатайте фильм.
Попробуйте установить год выхода, равный 1600 Напечатайте фильм.
3) Напишите метод void addVote(float x), который будет имитировать проставление оценки x фильму одним пользователем
numVotes должен увеличиться на 1 и rating должен тоже изменится по формуле
новыйРейтинг = (старыйРейтиг * староеКоличествоГолосов + x) / (староеКоличествоГолосов + 1)
4) У данного фильма 4 голоса со средней оценкой 8.0. Добавьте ещё одну оценку, равную 10.0.
Напечатайте фильм, новый рейтинг фильма должен быть равен 8.4.
*/
#include <iostream>
using std::cout, std::endl;
struct Movie
{
char title[100];
int releaseYear;
int numVotes;
float rating;
void print() const
{
cout << title << ", releaseYear = " << releaseYear << ", rating = " << rating
<< " (" << numVotes << " votes)" << endl;
}
};
int main()
{
Movie a = {"Dark City", 2000, 4, 8.0};
a.print();
}

View file

@ -0,0 +1,49 @@
#include <iostream>
using std::cout, std::endl;
struct Movie
{
char title[100];
int releaseYear;
int numVotes;
float rating;
void setReleaseYear(int year)
{
releaseYear = year;
if (releaseYear < 1900)
releaseYear = 1900;
}
void addVote(int x)
{
rating = (rating * numVotes + x) / (numVotes + 1);
numVotes += 1;
}
void print() const
{
cout << title << ", releaseYear = " << releaseYear << ", rating = " << rating
<< " (" << numVotes << " votes)" << endl;
}
};
int main()
{
Movie a = {"Dark City", 2000, 4, 8.0};
a.print();
a.setReleaseYear(1998);
a.print();
a.setReleaseYear(1600);
a.print();
a.addVote(10.0);
a.print();
}

View file

@ -0,0 +1,53 @@
/*
Ключевое слово this
Используя указатель this внутри структуры, можно узнать адрес объекта.
Например, если метод был вызван таким образом:
a.printThis();
то внутри метода this будет означать адрес объекта a
С помощью этого указателя можно доступаться до полей класса.
Например, title и this->title это одно и то же внутри методов структуры.
this можно использовать, если имя аргумента метода совпадает с одним из полей, как, например, в методе setPrice
Внутри метода setPrice поле price перекрывается аргументом price. Но можно всё-равно доступиться до поля price, используя указатель this
*/
#include <iostream>
using std::cout, std::endl;
struct Book
{
char title[100];
float price;
int pages;
void printThis() const
{
cout << this << endl;
}
void printTitle() const
{
cout << title << endl;
cout << this->title << endl;
}
void setPrice(float price)
{
this->price = price;
}
};
int main()
{
Book a = {"War and Peace", 1700, 900};
cout << &a << endl;
a.printThis();
a.printTitle();
}

View file

@ -0,0 +1,86 @@
/*
Конструктор
Конструктор - это специальный метод, который вызывается при создании объекта
Конструктор Book(const char aTitle[], float aPrice, int aPages) принимает три аргумента и задаёт
этими аргументами поля класса, а также печатает на экран слово Constructor.
Конструктор вызывается в строке Book a = Book("War and Peace", 1700, 900).
В этом примере конструктор делает то же самое, что и обычная инициализация структуры: Book a = {"War and Peace", 1700, 900};
Преимущество конструктора по сравнению с обычной инициализации структур состоит в том, что программист может сам задать то,
что будет происходить при создании объекта.
Напоминание:
Функция strcpy из библиотеки string.h языка C принимает на вход 2 строки и просто копирует
содержимое второй строки в первую строку.
*/
#include <iostream>
#include <cmath>
#include <string.h>
using std::cout, std::endl;
struct Book
{
char title[100];
float price;
int pages;
Book(const char aTitle[], float aPrice, int aPages)
{
cout << "Constructor" << endl;
strcpy(title, aTitle);
price = aPrice;
pages = aPages;
}
void print() const
{
std::cout << title << ", price = " << price << ", pages = " << pages << std::endl;
}
};
int main()
{
Book a = Book("War and Peace", 1700, 900);
a.print();
Book b = Book("The Great Gatsby", 800, -600);
b.print();
}
/*
Задачи:
1) Предположим, что программист, работая с наши классом Book, ошибся в конструкторе и установил у книги отрицательное количество страниц.
Измените конструктор таким образом, чтобы программа не создавала объект с отрицательным числом страниц.
Вместо этого она должна писать сообщение об ошибке и выходить из программы.
Для выхода из программы можно использовать функцию std::exit(1) из библиотеки <cstdlib>
2) Конструкторы можно перегружать также, как и обычные функции и методы.
Добавьте новый конструктор, который не будет принимать никаких аргументов и будет создавать объект с полями равными
title: "Default" price: 0 pages: 0
Вызовите этот конструктор из main
Конструктор, который не принимает аргументов, называется конструктором по умолчанию.
3) Конструкторы можно перегружать также, как и обычные функции и методы.
Добавьте новый конструктор, который будет принимать объект типа Book (по константной ссылке)
и будет задавать поля текущего объекта, используя поля аргумента
Вызовите этот конструктор из main
Конструктор, который создаёт объект, используя объект такого-же типа, называется конструктором копирования.
*/

View file

@ -0,0 +1,69 @@
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <string.h>
using std::cout, std::endl;
struct Book
{
char title[100];
float price;
int pages;
Book(const char aTitle[], float aPrice, int aPages)
{
cout << "Constructor" << endl;
strcpy(title, aTitle);
price = aPrice;
pages = aPages;
if (pages < 0)
{
cout << "Error! Number of pages can't be negative!" << endl;
std::exit(1);
}
}
Book()
{
cout << "Default Construtor" << endl;
strcpy(title, "Default");
price = 0;
pages = 0;
}
Book(const Book& b)
{
cout << "Copy Construtor" << endl;
strcpy(title, b.title);
price = b.price;
pages = b.pages;
}
void print() const
{
cout << title << ", price = " << price << ", pages = " << pages << endl << endl;
}
};
int main()
{
Book a = Book("War and Peace", 1700, 900);
a.print();
Book b = Book();
b.print();
Book c = Book(a);
c.print();
Book d = Book("The Great Gatsby", 800, -600);
d.print();
}

View file

@ -0,0 +1,65 @@
/*
Сокрытие данных - это разделение данных и функций абстрактного объекта на открытые (видимые извне) и скрытые (видимые только внутри самого объекта)
В языке C++ это реализуется с помощью модификаторов доступа public и private
Все поля и методы объявленные в секции public называются публичными и могут быть доступны извне структуры
Все поля и методы объявленные в секции private называются приватными и не могут быть доступны извне структуры
Приватные поля и методы могут быть доступны только в методах самого структуры (а также в друзьях, но об этом позже)
Назначение сокрытия данных заключается в том, чтобы объекты нельзя было 'поломать' извне
'Поломать' тут означает задать поля объекта бессмысленным образом
Например, в нашем примере мы бы могли поломать объект просто сделав поля price или pages отрицательными
a.pages = -100;
но благодаря тому, что поле pages является приватным, это сделать нельзя.
Учитывая проверку в конструкторе, получается, что поля pages и price в принципе никогда не смогут стать отрицательными.
Таким образом мы уберегли себя от возникновения ошибок при неправильном задании полей структуры.
*/
#include <iostream>
#include <cmath>
#include <string.h>
#include <cstdlib>
using std::cout, std::endl;
struct Book
{
private:
char title[100];
float price;
int pages;
public:
Book(const char aTitle[], float aPrice, int aPages)
{
if (aPages < 0 || aPrice < 0 || strlen(aTitle) >= 100)
{
cout << "Error while creating Book!" << endl;
std::exit(1);
}
strcpy(title, aTitle);
price = aPrice;
pages = aPages;
}
void print() const
{
cout << title << ", price = " << price << ", pages = " << pages << endl;
}
};
int main()
{
Book a = Book("War and Peace", 1700, 900);
a.print();
a.pages = -100;
}

View file

@ -0,0 +1,77 @@
/*
Синтаксис инициализации с помощью коструктора
Язык C++ имеет очень длинную историю и на её протяжении в язык добавлялись новые возможности
В итоге в языке часто можно сделать одно и то же разными методами.
В частности, вызвать конструктор можно 5-ю разными способами.
В этой программе строки для создания книг a, b, c, d, e делают одно и то же, несмотря, что имеют разный синтаксис
*/
#include <iostream>
#include <cmath>
#include <string.h>
#include <cstdlib>
using std::cout, std::endl;
struct Book
{
private:
char title[100];
float price;
int pages;
public:
Book(const char aTitle[], float aPrice, int aPages)
{
cout << "Constructor" << endl;
strcpy(title, aTitle);
price = aPrice;
pages = aPages;
}
void print() const
{
cout << title << ", price = " << price << ", pages = " << pages << endl;
}
};
int main()
{
Book a = Book("War and Peace", 1000, 500);
Book b("War and Peace", 1000, 500);
Book c = {"War and Peace", 1000, 500};
Book d = Book{"War and Peace", 1000, 500};
Book e {"War and Peace", 1000, 500};
a.print();
b.print();
c.print();
d.print();
e.print();
}
/*
Задача:
1) Добавьте к классу конструктор по умолчанию:
Book()
{
cout << "Default Constructor" << endl;
strcpy(title, "default");
price = 0;
pages = 0;
}
Создайте с помощью этого конструктора 5 книг, вызвав его 5-ю разными способами
*/

View file

@ -0,0 +1,62 @@
#include <iostream>
#include <cmath>
#include <string.h>
#include <cstdlib>
using std::cout, std::endl;
struct Book
{
private:
char title[100];
float price;
int pages;
public:
Book(const char aTitle[], float aPrice, int aPages)
{
cout << "Constructor" << endl;
strcpy(title, aTitle);
price = aPrice;
pages = aPages;
}
Book()
{
cout << "Default Constructor" << endl;
strcpy(title, "default");
price = 0;
pages = 0;
}
void print() const
{
cout << title << ", price = " << price << ", pages = " << pages << endl;
}
};
int main()
{
Book a = Book();
// Book b(); // этот способ не работает, так как его невозможно отличить от объявления функции (зато добавился способ f)
Book c = {};
Book d = Book{};
Book e {};
Book f; // в отличии от переменных базовых типов, тут произойдёт инициализация (конструктором по умолчанию)
a.print();
c.print();
d.print();
e.print();
f.print();
}

View file

@ -0,0 +1,53 @@
/*
Классы. Ключевое слово class.
На самом деле классы мы уже прошли. Структуры с методоми из предыдущих файлов это и есть классы.
Для объявления класса может использоваться ключевое слово class.
Разница между этими ключевым словами минимальна
- при использовании struct все поля и методы по умолчанию публичны
- при использовании class все поля и методы по умолчанию приватны
Но, так как мы указываем private и public для всех членов, то разницы нет вообще.
*/
#include <iostream>
#include <cmath>
#include <string.h>
#include <cstdlib>
using std::cout, std::endl;
class Book
{
private:
char title[100];
float price;
int pages;
public:
Book(const char aTitle[], float aPrice, int aPages)
{
if (aPages < 0 || aPrice < 0 || strlen(aTitle) >= 100)
{
cout << "Error while creating Book!" << endl;
std::exit(1);
}
strcpy(title, aTitle);
price = aPrice;
pages = aPages;
}
void print() const
{
cout << title << ", price = " << price << ", pages = " << pages << endl;
}
};
int main()
{
Book a = Book("War and Peace", 1700, 900);
a.print();
}

View file

@ -0,0 +1,56 @@
/*
Некоторые замечания по оформлению
1) Как правило в классе сначала описываются публичные методы, а потом приватные
Так делают потому что если другой программист захочет воспользоваться вашим классом,
то его будет в первую очередь будет интересовать что ваш класс может делать
и уже потом его будет интересовать строение класса.
2) Приватные поля класса желательно называть так, чтобы их можно было отличить от обычных переменных
Это может сильно упростить понимание при написании/использовании больших програм и библиотек
В данном курсе мы будем называть приватные поля начиная с буквы m
Например, mTitle вместо title
*/
#include <iostream>
#include <cmath>
#include <string.h>
#include <cstdlib>
using std::cout, std::endl;
class Book
{
public:
Book(const char title[], float price, int pages)
{
if (pages < 0 || price < 0 || strlen(title) >= 100)
{
cout << "Error while creating Book!" << endl;
std::exit(1);
}
strcpy(mTitle, title);
mPrice = price;
mPages = pages;
}
void print() const
{
cout << mTitle << ", price = " << mPrice << ", pages = " << mPages << endl;
}
private:
char mTitle[100];
float mPrice;
int mPages;
};
int main()
{
Book a = Book("War and Peace", 1700, 900);
a.print();
}