diff --git a/seminar02_encapsulation/classroom_tasks/classroom_tasks_encapsulation.pdf b/seminar02_encapsulation/classroom_tasks/classroom_tasks_encapsulation.pdf new file mode 100644 index 0000000..cddf46a Binary files /dev/null and b/seminar02_encapsulation/classroom_tasks/classroom_tasks_encapsulation.pdf differ diff --git a/seminar02_encapsulation/classroom_tasks/classroom_tasks_encapsulation.tex b/seminar02_encapsulation/classroom_tasks/classroom_tasks_encapsulation.tex new file mode 100644 index 0000000..3b85f65 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/classroom_tasks_encapsulation.tex @@ -0,0 +1,405 @@ +\documentclass{article} +\usepackage[utf8x]{inputenc} +\usepackage{ucs} +\usepackage{amsmath} +\usepackage{amsfonts} +\usepackage{marvosym} +\usepackage{wasysym} +\usepackage{upgreek} +\usepackage[english,russian]{babel} +\usepackage{graphicx} +\usepackage{float} +\usepackage{textcomp} +\usepackage{hyperref} +\usepackage{geometry} + \geometry{left=2cm} + \geometry{right=1.5cm} + \geometry{top=1cm} + \geometry{bottom=2cm} +\usepackage{tikz} +\usepackage{ccaption} +\usepackage{multicol} +\usepackage{fancyvrb} + +\usepackage{listings} +%\setlength{\columnsep}{1.5cm} +%\setlength{\columnseprule}{0.2pt} + +\usepackage{colortbl,graphicx,tikz} +\definecolor{X}{rgb}{.5,.5,.5} + +\title{ДЗ. Работа с изображениями в формате \texttt{.ppm}} +\date{} +\begin{document} +\pagenumbering{gobble} + +\lstset{ + language=C++, % choose the language of the code + basicstyle=\linespread{1.1}\ttfamily, + columns=fixed, + fontadjust=true, + basewidth=0.5em, + keywordstyle=\color{blue}\bfseries, + commentstyle=\color{gray}, + stringstyle=\ttfamily\color{orange!50!black}, + showstringspaces=false, + %numbers=false, % where to put the line-numbers + numbersep=5pt, + numberstyle=\tiny\color{black}, + numberfirstline=true, + stepnumber=1, % the step between two line-numbers. + numbersep=10pt, % how far the line-numbers are from the code + backgroundcolor=\color{white}, % choose the background color. You must add \usepackage{color} + showstringspaces=false, % underline spaces within strings + captionpos=b, % sets the caption-position to bottom + breaklines=true, % sets automatic line breaking + breakatwhitespace=true, % sets if automatic breaks should only happen at whitespace + xleftmargin=.2in, + extendedchars=\true, + keepspaces = true, +} +\lstset{literate=% + *{0}{{{\color{red!20!violet}0}}}1 + {1}{{{\color{red!20!violet}1}}}1 + {2}{{{\color{red!20!violet}2}}}1 + {3}{{{\color{red!20!violet}3}}}1 + {4}{{{\color{red!20!violet}4}}}1 + {5}{{{\color{red!20!violet}5}}}1 + {6}{{{\color{red!20!violet}6}}}1 + {7}{{{\color{red!20!violet}7}}}1 + {8}{{{\color{red!20!violet}8}}}1 + {9}{{{\color{red!20!violet}9}}}1 + {~} {$\sim$}{1} +} + + +\title{Семинар \#2: Инкапсуляция. Классные задачи.\vspace{-5ex}}\date{}\maketitle +\section*{Инкапсуляция} +Инкапсуляция -- это размещение в классе/структуре данных и функций, которые с ними работают. Структуру с методами можно называть классом. Переменные класса называются полями, а функции класса -- методами. +\begin{multicols}{2}\setlength{\columnseprule}{0.4pt} +\textbf{Без инкапсуляции:} +\begin{lstlisting} +#include +#include + +struct Point +{ + float x, y; +}; + +float norm(const Point& p) +{ + return std::sqrt(p.x*p.x + p.y*p.y); +} + +void normalize(Point& p) +{ + float pnorm = norm(p); + p.x /= pnorm; + p.y /= pnorm; +} + +Point operator+(const Point& l, + const Point& r) +{ + Point result = {l.x + r.x, l.y + r.y}; + return result; +} + +int main() +{ + Point p = {1, 2}; + normalize(p); + std::cout << p.x << " " + << p.y << std::endl; +} + +\end{lstlisting} + +\textbf{С инкапсуляцией:} +\begin{lstlisting} +#include +#include + +struct Point +{ + float x, y; + + float norm() const + { + return stdsqrt(x*x + y*y); + } + + void normalize() + { + float pnorm = norm(); + x /= pnorm; + y /= pnorm; + } + + Point operator+(const Point& r) const + { + Point result = {x + r.x, y + r.y}; + return result; + } +}; + +int main() +{ + Point p = {1, 2}; + p.normalize(); + std::cout << p.x << " " + << p.y << std::endl; +} +\end{lstlisting} +\end{multicols} +Обратите внимание на следующие моменты: +\begin{itemize} +\item[--] Методы имеют прямой доступ к полям \texttt{x} и \texttt{y}. Передавать саму структуру(или ссылку на неё) в методах не нужно. +\item[--] Вызов метода класса осуществляется с помощью оператора \texttt{.}(точка). +\item[--] Спецификатор \texttt{const} после объявления метода (например, \texttt{float norm() const}) означает, что этот метод не будет менять поля. +Желательно указывать этот спецификатор для всех методов, которые не изменяют объект. +\item[--] При перегрузке бинарных операций объектом является левый аргумент, а параметром функции -- правый. Т.е. \texttt{p + q} превратится в \texttt{p.operator+(q)}. +\end{itemize} + + +\subsection*{Модификаторы доступа \texttt{public} и \texttt{private}} +Модификаторы доступа служат для ограничения доступа к полям и методам класса. +\begin{itemize} +\item[--] \texttt{public} -- поля и методы могут использоваться где угодно +\item[--] \texttt{private} -- поля и методы могут использовать только методы этого класса и друзья (особые функции и классы, объявленные с использованием ключевого слова \texttt{friend}) +\end{itemize} + +\subsection*{Конструкторы} +Конструктор -- это специальный метод, который вызывается автоматически при создании экземпляра класса. +\begin{lstlisting} +struct Point { +private: + float x, y; +public: + // Конструктор: + Point(float ax, float ay) + { + x = ax; + y = ay; + } + // другие методы +}; + +int main() +{ + // Если несколько разных синтаксов создания экземпляра класса с вызовом конструктора: + Point a = Point(7, 3); + Point b(7, 3); + Point c = {7, 3}; + Point d {7, 3}; + // Все они делают одно и то же - создают переменную на стеке и вызывают конструктор + // В современном C++ предпочтительным является способ d +} +\end{lstlisting} +Особым видом конструктора является конструктор копирования: +\begin{lstlisting} +Point(const Point& p) +\end{lstlisting} +Он используется для создание нового экземпляра класса по уже имеющемуся экземпляру.\\ +\textbf{Задача 3:} Сделайте задание в файле \texttt{4point\_constructors.cpp} + +\subsection*{Ключевое слово \texttt{this} и оператор присваивания} +Ключевое слово \texttt{this} - это указатель на экземпляр класса, который можно использовать в методах этого класса.\\ +Оператор присваивания -- это просто перегруженный оператор \texttt{=}. Оператор присваивания должен вернуть ссылку на текущий объект, то есть \texttt{*this}.\\ + +Нужно различать оператор присваивания и вызов конструктора: +\begin{lstlisting} +Point a = Point(7, 3); // Конструктор ( оператор присваивания не вызывается ) +Point b = a; // Конструктор копирования ( оператор присваивания не вызывается ) +Point c; // Конструктор по умолчанию +c = a; // Оператор присваивания +\end{lstlisting} +Оператор присваивания должен возвращать ссылку на левый аргумент. + +\newpage +\section*{Создаём свой класс строки} +Строки в языке \texttt{C} представляют собой просто массивы с элементами типа \texttt{char}(однобайтовое число). Работать с такими строками не очень удобно. Нужно выделять и удалять необходимую память, следить за тем, чтобы строка помещалась в эту память на всём этапе выполнения программы, для работы со этими строками нужно использовать специальные функции из библиотеки \texttt{string.h}. Это всё может привести к ошибкам. В этом разделе мы создадим класс \texttt{String} -- класс строки, с которым удобнее и безопаснее работать, чем со строками в стиле \texttt{C}. Заготовка класса выглядит так (Это далеко не самая эффективная реализация строки, более правильная реализация создаётся в примерах кода): +\begin{lstlisting} +#include + +class String +{ +private: + + size_t size; + char* data; + +public: + + String(const char* str) + { + size = 0; + while (str[size]) + size++; + + data = (char*)malloc(sizeof(char) * (size + 1)); + + for (int i = 0; str[i]; i++) + data[i] = str[i]; + data[size] = '\0'; + } + + size_t getSize() const + { + return size; + } + + const char* cStr() const + { + return data; + } +}; + +int main() +{ + String a = "Elephant"; +} +\end{lstlisting} + +Схематично это можно представить следующим образом: +\begin{center} +\includegraphics[scale=0.86]{../images/string_base.png} +\end{center} + +\newpage +\subsection*{Деструктор} +В коде выше выделяется память для массива \texttt{data}. Эта память выделяется при вызове конструктора (то есть при создании объекта). Однако она нигде не освобождается. Освободить её вручную мы не можем, так как поле \texttt{data} является приватным и это бы противоречило принципу сокрытия данных. Эта память должна освобождаться автоматически при удалении объекта. +\begin{lstlisting} +~ String() +{ + free(data); +} +\end{lstlisting} + +\subsection*{Перегруженные операторы класса \texttt{String}} +\begin{itemize} +\item \textbf{Оператор сложения:} \texttt{String operator+(const String\& right) const} \\ +Этот оператор должен создавать новый экземпляр, задавать его поля (в частности придётся выделить память под строку-сумму) и возвращать этот экземпляр. + +\item \textbf{Оператор присваивания сложения:} \texttt{String\& operator+=(const String\& right)}\\ +Этот оператор не должен создавать новый экземпляр. Он должен изменять левый операнд (т. е. сам объект), и возвращать ссылку на этот объект (т. е. \texttt{*this}). + +\item \textbf{Оператор присваивания:} \texttt{String\& operator=(const String\& right)}\\ +Этот оператор не должен создавать новый экземпляр. Он должен изменять левый операнд (т. е. сам объект), так чтобы он стал идентичен правому. Если размеры строк не совпадают, то в данной реализации строки вам придётся удалить память левой строки и снова выделить память нужного размера. При этом нужно отдельно рассмотреть случай когда левый и правый операнд это один и тот же объект. +\begin{lstlisting} +String a {"Cat"}; +a = a; +\end{lstlisting} +Конечно, в этом случае ничего удалять не нужно. + +\item \textbf{Оператор сравнения:} \texttt{bool operator==(const String\& right) const}\\ +Этот оператор должен сравнивать строки (массивы \texttt{data}) и возвращать \texttt{true} или \texttt{false}. + +\item \textbf{Оператор индексации:} \texttt{char\& operator[](unsigned int i)}\\ +Этот оператор должен возвращать ссылку на \texttt{i}-ый символ строки. + +\item \textbf{Индексация с проверкой на выход за границы:} \texttt{char\& at(unsigned int i)}\\ +Этот метод должен проверять, что индекс \texttt{i} не выходит за границы диапазона и, если это так, возвращать ссылку на \texttt{i}-ый символ строки. Иначе, этот метод должен печатать сообщение об ошибке и завершать программу. +\end{itemize} + +\newpage +\section*{Раздельная компиляция класса} +Методы можно вынести из определения класса следующим образом: +\begin{multicols}{2}\setlength{\columnseprule}{0.4pt} +\textbf{Определение методов в теле класса:} +\begin{lstlisting} +#include +#include + + +struct Point +{ + float x, y; + + float norm() const + { + return std::sqrt(x *x + y * y); + } + + void normalize() + { + float pnorm = norm(); + x /= pnorm; + y /= pnorm; + } + + Point operator+(const Point& r) const + { + Point result = {x + r.x, y + r.y}; + return result; + } +}; + + + + + +int main() +{ + Point p = {1, 2}; + p.normalize(); + std::cout << p.x << " " + << p.y << std::endl; +} +\end{lstlisting} +\vfill\null +\columnbreak + +\textbf{Определение методов вне тела класса:} +\begin{lstlisting} +#include +#include + +struct Point +{ + float x, y; + + float norm() const; + void normalize(); + Point operator+(const Point& r) const; +}; + +float Point::norm() const +{ + return sqrt(x*x + y*y); +} + +void Point::normalize() +{ + float pnorm = norm(); + x /= pnorm; + y /= pnorm; +} + +Point Point::operator+(const Point& r) const +{ + Point result = {x + r.x, y + r.y}; + return result; +} + +int main() +{ + Point p = {1, 2}; + p.normalize(); + std::cout << p.x << " " + << p.y << std::endl; +} +\end{lstlisting} +\end{multicols} + +Теперь эти методы можно скомпилировать отдельно. Для этого их нужно вынести в отдельный компилируемый файл \texttt{point.cpp}, а определение класса в отдельный файл \texttt{point.h}. Так называемый заголовочный файл \texttt{point.h} нужен, так как определение класса нужно и файле \texttt{point.cpp} и в файле \texttt{main.cpp}. Для компиляции используем: +\begin{verbatim} +g++ main.cpp point.cpp +\end{verbatim} + +\begin{center} +\includegraphics[scale=0.65]{../images/sepcompilation.png} +\end{center} + +\end{document} \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/0book/00oop.cpp b/seminar02_encapsulation/classroom_tasks/code/0book/00oop.cpp new file mode 100644 index 0000000..e980ef0 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/0book/00oop.cpp @@ -0,0 +1,34 @@ +/* + Объектно-ориентированное программирование (ООП) основано на представлении программы в виде совокупности взаимодействующих объектов + + Основные принципы ООП: абстракция, инкапсуляция, наследование и полиморфизм + + + Абстракция: использование только тех характеристик объекта, которые с достаточной точностью представляют его в данной системе. + + В каком-то смысле обычные структуры из языка C являются примером абстракции + + struct book + { + char title[100]; + float price; + int pages; + }; + + Для описание книги в коде мы используем лишь некоторое её характеристики, достаточные для нашей задачи + + + + Инкапсуляция: связывание данных некоторого абстрактного объекта и функций для работы с ним + Тесно связано с инкапсуляцией такое понятие как сокрытие + + Сокрытие: разделение данных и функций абстрактного объекта на открытые (видимые извне) и скрытые (видимые только внутри самого объекта) + + + + Наследование и Полиморфизм будут пройдены позже в курсе +*/ + + + +int main(){} \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/0book/01book.c b/seminar02_encapsulation/classroom_tasks/code/0book/01book.c new file mode 100644 index 0000000..592e979 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/0book/01book.c @@ -0,0 +1,54 @@ +/* + Хоть язык C и не является объектно-ориентированным, некоторые зачатки подходов ООП в нём тоже есть + Например, структуры языка C являются примером Абстракции + + Для работы со структурами мы обычно писали функции так, что каждая из этих функций принимает на вход первым аргументом указатель на наш объект. + Такой подход НЕ является примером Инкапсуляции, так как структура и функции для работы с ней являются независимыми друг от друга. + При желании или по ошибке можно первым аргументом в эти функции передать вообще объект другого типа. + + + + Эта программа написана на языке C, для компиляции используйте gcc: + gcc 00book.c + + Функции в этом примере делают следующее: + + make_discount сделать скидку на книгу, но цена на книгу не может упасть ниже 0. + print_book напечатать информацию о книге на экран +*/ + +#include + + +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); +} diff --git a/seminar02_encapsulation/classroom_tasks/code/0book/02book.cpp b/seminar02_encapsulation/classroom_tasks/code/0book/02book.cpp new file mode 100644 index 0000000..76c53c1 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/0book/02book.cpp @@ -0,0 +1,44 @@ +/* + Эта программа написана на языке C++, для компиляции используйте g++: + g++ 01book.cpp + + В языке C++ появились ссылки, которые могут немного упростить код из предыдущего файла + + Тем не менее, структура Book и функции для работы с ней всё ещё являются независимыми друг от друга. + То есть тут тоже нет Инкапсуляции. +*/ + +#include + + +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); +} diff --git a/seminar02_encapsulation/classroom_tasks/code/0book/03book_encapsulation.cpp b/seminar02_encapsulation/classroom_tasks/code/0book/03book_encapsulation.cpp new file mode 100644 index 0000000..6b3cfce --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/0book/03book_encapsulation.cpp @@ -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 + +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 из константного метода? + +*/ diff --git a/seminar02_encapsulation/classroom_tasks/code/0book/04problem_movie.cpp b/seminar02_encapsulation/classroom_tasks/code/0book/04problem_movie.cpp new file mode 100644 index 0000000..cf02e21 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/0book/04problem_movie.cpp @@ -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 +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(); +} + diff --git a/seminar02_encapsulation/classroom_tasks/code/0book/04problem_movie_solution.cpp b/seminar02_encapsulation/classroom_tasks/code/0book/04problem_movie_solution.cpp new file mode 100644 index 0000000..975b00b --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/0book/04problem_movie_solution.cpp @@ -0,0 +1,49 @@ +#include +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(); +} + diff --git a/seminar02_encapsulation/classroom_tasks/code/0book/05this_pointer.cpp b/seminar02_encapsulation/classroom_tasks/code/0book/05this_pointer.cpp new file mode 100644 index 0000000..c3f4bd7 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/0book/05this_pointer.cpp @@ -0,0 +1,53 @@ +/* + Ключевое слово this + + Используя указатель this внутри структуры, можно узнать адрес объекта. + Например, если метод был вызван таким образом: + a.printThis(); + то внутри метода this будет означать адрес объекта a + + С помощью этого указателя можно доступаться до полей класса. + Например, title и this->title это одно и то же внутри методов структуры. + + this можно использовать, если имя аргумента метода совпадает с одним из полей, как, например, в методе setPrice + Внутри метода setPrice поле price перекрывается аргументом price. Но можно всё-равно доступиться до поля price, используя указатель this +*/ + +#include +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(); +} + diff --git a/seminar02_encapsulation/classroom_tasks/code/0book/06constructor.cpp b/seminar02_encapsulation/classroom_tasks/code/0book/06constructor.cpp new file mode 100644 index 0000000..073fd86 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/0book/06constructor.cpp @@ -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 +#include +#include +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) из библиотеки + + + 2) Конструкторы можно перегружать также, как и обычные функции и методы. + Добавьте новый конструктор, который не будет принимать никаких аргументов и будет создавать объект с полями равными + title: "Default" price: 0 pages: 0 + Вызовите этот конструктор из main + + Конструктор, который не принимает аргументов, называется конструктором по умолчанию. + + + + 3) Конструкторы можно перегружать также, как и обычные функции и методы. + Добавьте новый конструктор, который будет принимать объект типа Book (по константной ссылке) + и будет задавать поля текущего объекта, используя поля аргумента + Вызовите этот конструктор из main + + Конструктор, который создаёт объект, используя объект такого-же типа, называется конструктором копирования. +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/0book/06constructor_solution.cpp b/seminar02_encapsulation/classroom_tasks/code/0book/06constructor_solution.cpp new file mode 100644 index 0000000..7d423e9 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/0book/06constructor_solution.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +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(); + +} diff --git a/seminar02_encapsulation/classroom_tasks/code/0book/07public_private.cpp b/seminar02_encapsulation/classroom_tasks/code/0book/07public_private.cpp new file mode 100644 index 0000000..7c0209f --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/0book/07public_private.cpp @@ -0,0 +1,65 @@ +/* + Сокрытие данных - это разделение данных и функций абстрактного объекта на открытые (видимые извне) и скрытые (видимые только внутри самого объекта) + В языке C++ это реализуется с помощью модификаторов доступа public и private + + Все поля и методы объявленные в секции public называются публичными и могут быть доступны извне структуры + Все поля и методы объявленные в секции private называются приватными и не могут быть доступны извне структуры + Приватные поля и методы могут быть доступны только в методах самого структуры (а также в друзьях, но об этом позже) + + + Назначение сокрытия данных заключается в том, чтобы объекты нельзя было 'поломать' извне + 'Поломать' тут означает задать поля объекта бессмысленным образом + + Например, в нашем примере мы бы могли поломать объект просто сделав поля price или pages отрицательными + a.pages = -100; + но благодаря тому, что поле pages является приватным, это сделать нельзя. + + + Учитывая проверку в конструкторе, получается, что поля pages и price в принципе никогда не смогут стать отрицательными. + Таким образом мы уберегли себя от возникновения ошибок при неправильном задании полей структуры. +*/ + +#include +#include +#include +#include +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; +} diff --git a/seminar02_encapsulation/classroom_tasks/code/0book/08constructor_calls.cpp b/seminar02_encapsulation/classroom_tasks/code/0book/08constructor_calls.cpp new file mode 100644 index 0000000..edc10b4 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/0book/08constructor_calls.cpp @@ -0,0 +1,77 @@ +/* + Синтаксис инициализации с помощью коструктора + + Язык C++ имеет очень длинную историю и на её протяжении в язык добавлялись новые возможности + В итоге в языке часто можно сделать одно и то же разными методами. + + В частности, вызвать конструктор можно 5-ю разными способами. + В этой программе строки для создания книг a, b, c, d, e делают одно и то же, несмотря, что имеют разный синтаксис +*/ + +#include +#include +#include +#include +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-ю разными способами + +*/ diff --git a/seminar02_encapsulation/classroom_tasks/code/0book/08constructor_calls_solution.cpp b/seminar02_encapsulation/classroom_tasks/code/0book/08constructor_calls_solution.cpp new file mode 100644 index 0000000..8ce1003 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/0book/08constructor_calls_solution.cpp @@ -0,0 +1,62 @@ +#include +#include +#include +#include +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(); +} + + diff --git a/seminar02_encapsulation/classroom_tasks/code/0book/09class.cpp b/seminar02_encapsulation/classroom_tasks/code/0book/09class.cpp new file mode 100644 index 0000000..d1b1701 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/0book/09class.cpp @@ -0,0 +1,53 @@ +/* + Классы. Ключевое слово class. + + На самом деле классы мы уже прошли. Структуры с методоми из предыдущих файлов это и есть классы. + Для объявления класса может использоваться ключевое слово class. + + Разница между этими ключевым словами минимальна + - при использовании struct все поля и методы по умолчанию публичны + - при использовании class все поля и методы по умолчанию приватны + Но, так как мы указываем private и public для всех членов, то разницы нет вообще. +*/ + +#include +#include +#include +#include +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(); +} diff --git a/seminar02_encapsulation/classroom_tasks/code/0book/10m.cpp b/seminar02_encapsulation/classroom_tasks/code/0book/10m.cpp new file mode 100644 index 0000000..8966466 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/0book/10m.cpp @@ -0,0 +1,56 @@ +/* + Некоторые замечания по оформлению + + 1) Как правило в классе сначала описываются публичные методы, а потом приватные + Так делают потому что если другой программист захочет воспользоваться вашим классом, + то его будет в первую очередь будет интересовать что ваш класс может делать + и уже потом его будет интересовать строение класса. + + + 2) Приватные поля класса желательно называть так, чтобы их можно было отличить от обычных переменных + Это может сильно упростить понимание при написании/использовании больших програм и библиотек + В данном курсе мы будем называть приватные поля начиная с буквы m + Например, mTitle вместо title +*/ + +#include +#include +#include +#include +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(); +} diff --git a/seminar02_encapsulation/classroom_tasks/code/1point/00point.c b/seminar02_encapsulation/classroom_tasks/code/1point/00point.c new file mode 100644 index 0000000..9469d8f --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/1point/00point.c @@ -0,0 +1,56 @@ +/* + Это программа на языке C, для компиляции: + gcc 00point.c + ./a.out + + Опишем структуру точки в двумерном пространстве на языке C + + Точка задаётся двумя координатами x и y + Так как эта структура имеет очень маленький размер (всего 8 байт), то в функции + её можно передавать по значению, а не по константному указателю. +*/ + +#include +#include + +struct point +{ + float x, y; +}; +typedef struct point Point; + + +void point_print(Point a) +{ + printf("(%.2f, %.2f)\n", a.x, a.y); +} + + +int main() +{ + Point a = {7.2, 3.1}; + Point b = {-4.6, 2.4}; + + point_print(a); + point_print(b); +} + +/* + Задачи: + + 1) Напишите функцию point_add, + которая будет принимать две точки и возвращать их сумму + + 2) Напишите функцию point_norm, + которая будет принимать точку и возвращать расстояние до этой точки от начала координат + Будем называть расстояние от точки до начала координат нормой точки + Для вычисления корня числа можно использовать функцию sqrt из math.h + + 3) Напишите функцию point_mult, + которая будет принимать на вход точку и число k типа float и возвращать точку, координаты которой + равны координатам изначальной точки, умноженные на число k + + 4) Напишите функцию point_normalize, + которая будет принимать точку по указателю и делить координаты точки на норму точки + Эта функция не должна ничего возвращать +*/ diff --git a/seminar02_encapsulation/classroom_tasks/code/1point/00point_solution.c b/seminar02_encapsulation/classroom_tasks/code/1point/00point_solution.c new file mode 100644 index 0000000..6533acb --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/1point/00point_solution.c @@ -0,0 +1,63 @@ +/* + Это программа на языке C, для компиляции: + gcc 00point_solution.c + ./a.out +*/ + +#include +#include + + +struct point +{ + float x, y; +}; +typedef struct point Point; + + +void point_print(Point a) +{ + printf("(%.2f, %.2f)\n", a.x, a.y); +} + +Point point_add(Point a, Point b) +{ + Point result = {a.x + b.x, a.y + b.y}; + return result; +} + +float point_norm(Point a) +{ + return sqrtf(a.x * a.x + a.y * a.y); +} + +Point point_mult(Point a, float k) +{ + Point result = {k * a.x, k * a.y}; + return result; +} + +void point_normalize(Point* pa) +{ + float norm = point_norm(*pa); + pa->x /= norm; + pa->y /= norm; +} + + +int main() +{ + Point a = {7.2, 3.1}; + Point b = {-4.6, 2.4}; + + point_print(a); + point_print(b); + + Point c = point_add(a, b); + point_print(c); + + point_print(point_mult(c, 1.5f)); + + point_normalize(&c); + point_print(c); +} \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/1point/01point.cpp b/seminar02_encapsulation/classroom_tasks/code/1point/01point.cpp new file mode 100644 index 0000000..62d5ae0 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/1point/01point.cpp @@ -0,0 +1,84 @@ +/* + Это программа на языке C++, для компиляции: + g++ 01point.cpp + + Та же самая точка, но на языке C++ + + В этом файле была видоизменена программа из предыдущего файла. + Были использованы перегруженные операторы для более удобного сложения и умножения точек. + Также была использована ссылка вместо указателя в функции pointNormalize. +*/ + +#include +#include +#include +using std::cout, std::endl; + + +struct Point +{ + float x, y; +}; + +void pointPrint(Point a) +{ + cout << std::setprecision(2) << "(" << a.x << ", " << a.y << ")" << endl; +} + +Point operator+(Point a, Point b) +{ + Point result = {a.x + b.x, a.y + b.y}; + return result; +} + +float pointNorm(Point a) +{ + return std::sqrt(a.x * a.x + a.y * a.y); +} + +Point operator*(Point a, float k) +{ + Point result = {k * a.x, k * a.y}; + return result; +} + +void pointNormalize(Point& a) +{ + float norm = pointNorm(a); + a.x /= norm; + a.y /= norm; +} + + + + +int main() +{ + Point a = {7.2, 3.1}; + Point b = {-4.6, 2.4}; + + pointPrint(a); + pointPrint(b); + + Point c = a + b; + pointPrint(c); + + pointPrint(c * 1.5f); + + pointNormalize(c); + pointPrint(c); +} + + + + +/* + Задача: + + 1) Инкапсулируйте функции operator+, pointNorm, operator* и pointNormalize + Их нужно сделать методами, то есть положить внутрь структуры Point + Не забудьте сделать соответствующие методы константными + + 2) Можно сделать то же самое с функцией printPoint, а можно поступить по-другому + и перегрузить оператор << для типов std::ostream и Point +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/1point/01point_solution.cpp b/seminar02_encapsulation/classroom_tasks/code/1point/01point_solution.cpp new file mode 100644 index 0000000..1cbfbbe --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/1point/01point_solution.cpp @@ -0,0 +1,97 @@ +/* + Обратите внимание на следующие моменты в этом решении: + + 1) При переносе функций внутрь класса у них стало на 1 аргумент меньше + Просто потому что все методы неявно принимают вызывающий их объект + + Например, если a это точка, то при вызове: + a.norm() + + метод norm 'знает', что его вызвала имено точка a и может доступаться до её полей x и y + + + 2) Перегруженные операторы тоже могут быть методами + При этом оператор преобразуется следующим образом: + + a @ b -> a.operator@(b) + где на месте @ может быть любой бинарный оператор + + Например, сложение преобразуется так: + a + b -> a.operator+(b) + + Обратите внимание, что перегруженный оператор может стать методом только первого аргумента + + + 3) Перегрузка оператора << для типов std::ostream и Point + Для более удобного вывода на экран можно перегрузить этот оператор + + Когда компилятор встретит выражение cout << a где cout имеет тип std::ostream, а имеет тип Point + то он вызовет эту функцию. + Эта функция должна вызывать ссылку на cout так как результатом cout << a тоже должен быть cout + чтобы мы могли выводить цепочкой, например так: cout << a << b << endl +*/ + +#include +#include +#include +using std::cout, std::endl; + + +struct Point +{ + float x, y; + + Point operator+(Point b) const + { + Point result = {x + b.x, y + b.y}; + return result; + } + + float norm() const + { + return std::sqrt(x * x + y * y); + } + + Point operator*(float k) const + { + Point result = {k * x, k * y}; + return result; + } + + void normalize() + { + float normv = norm(); + x /= normv; + y /= normv; + } +}; + +std::ostream& operator<<(std::ostream& out, Point a) +{ + out << std::setprecision(2) << "(" << a.x << ", " << a.y << ")"; + return out; +} + + + + +int main() +{ + Point a = {7.2, 3.1}; + Point b = {-4.6, 2.4}; + + cout << a << endl; + cout << b << endl; + + Point c = a + b; + cout << c << endl; + + cout << c * 1.5f << endl; + + c.normalize(); + cout << c << endl; +} + + + + diff --git a/seminar02_encapsulation/classroom_tasks/code/1point/02operator_order.cpp b/seminar02_encapsulation/classroom_tasks/code/1point/02operator_order.cpp new file mode 100644 index 0000000..5a5798f --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/1point/02operator_order.cpp @@ -0,0 +1,48 @@ +#include +#include +#include +using std::cout, std::endl; + + +struct Point +{ + float x, y; + + Point operator*(float k) const + { + Point result = {k * x, k * y}; + return result; + } + +}; + +std::ostream& operator<<(std::ostream& out, Point a) +{ + out << std::setprecision(2) << "(" << a.x << ", " << a.y << ")"; + return out; +} + + +int main() +{ + Point a = {2.1, 1.5}; + + cout << a * 2 << endl; + cout << 2 * a << endl; +} + + + +/* + Задачи: + + 1) В этой программе выражение a * 2 вычисляется правильно, но + выражение 2 * a даёт ошибку. + + Из-за чего это происходит? Исправьте ситуацию так, чтобы выражение 2 * a также вычислялось. + + + 2) Можно ли сделать перегруженный оператор << методом класса Point? +*/ + + diff --git a/seminar02_encapsulation/classroom_tasks/code/1point/02operator_order_solution.cpp b/seminar02_encapsulation/classroom_tasks/code/1point/02operator_order_solution.cpp new file mode 100644 index 0000000..8675d08 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/1point/02operator_order_solution.cpp @@ -0,0 +1,64 @@ +/* + Решения: + + 1) Выражение a * 2 вычисляется так как есть перегруженный оператор a.operator*(2) + Выражение 2 * a даёт ошибку так как не было перегруженного оператора operator*(2, a) + + Но его можно просто написать, как это сделано ниже. + + Сделать этот оператор методом мы не можем, так как перегруженный оператор может быть методом + только первого аргумента, первый аргумент в данном случае это число целочисленного типа float. + Добавить метод в тип float мы не можем, так как float это вообще не класс. + + Замечание: Литерал 2 на самом деле имеет тип int, но int может конвертироваться во float если нужно + + + 2) Можно ли сделать перегруженный оператор << методом класса Point? + + Нет, нельзя. Перегруженный оператор может быть методом только первого аргумента. +*/ + +#include +#include +#include +using std::cout, std::endl; + + + +struct Point +{ + float x, y; + + Point operator*(float k) const + { + Point result = {k * x, k * y}; + return result; + } + +}; + +Point operator*(float k, Point a) +{ + return a * k; +} + + +std::ostream& operator<<(std::ostream& out, Point a) +{ + out << std::setprecision(2) << "(" << a.x << ", " << a.y << ")"; + return out; +} + + +int main() +{ + Point a = {2.1, 1.5}; + + cout << a * 2 << endl; + cout << 2 * a << endl; +} + + + + + diff --git a/seminar02_encapsulation/classroom_tasks/code/1point/03setters_getters.cpp b/seminar02_encapsulation/classroom_tasks/code/1point/03setters_getters.cpp new file mode 100644 index 0000000..d9269a0 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/1point/03setters_getters.cpp @@ -0,0 +1,124 @@ +/* + До этого момента поля x и y класса Point были публичными + + Обычно, мы не хотим делать поля публичными, так как мы не хотим, чтобы поля могли бы быть заданы некоректным значением извне класса. + Однако, в случае класса Point некорректных значений для x и y просто не существует - любое вещественное число будет + корректным для значения координаты точки. + Поэтому нет ничего плохого, чтобы сделать x и y публичными для класса Point + + + Но давайте сделаем класс с немного более сложным поведением. + Точка RestrictedPoint - это будет точка, которая может находится только в квадрате [0,1]x[0,1] + То есть поле x может принимать значения только от 0 до 1 и поле y может принимать значения только от 0 до 1. + + Сделаем поля x и y приватными (и назовём их mx и my) + Теперь до них можно будет доступиться только в методах класса RestrictedPoint и в друзьях. + + Чтобы можно было работать с этими полями вне класса напишем методы + getx, gety, setx, sety + Такие методы для получения полей и записи в поля класса называются геттерами и сеттерами + Функции getx и gety просто возвращают соответствующие координаты + Функции setx и sety меняют соответствующие координаты и следят, чтобы они находились в диапазоне от 0 до 1 + + + Нам понадобится стандартная функция std::clamp из , которая принимает на вход три числа и + если первое число находится в промежутке между вторым и третьим, то clamp возвращает первое число + если первое число меньше, чем второе, то clamp возвращает второе число + если первое число больше, чем третье, то clamp возвращает третье число + Грубо говоря clamp ограничивает число в заданых пределах +*/ + +#include +#include +#include +using std::cout, std::endl; + + + +class RestrictedPoint +{ +private: + float mx, my; + +public: + + RestrictedPoint(float x, float y) + { + mx = std::clamp(x, 0.0f, 1.0f); + my = std::clamp(y, 0.0f, 1.0f); + } + + RestrictedPoint() + { + mx = 0; + my = 0; + } + + float getx() const + { + return mx; + } + + float gety() const + { + return my; + } + + void setx(float x) + { + mx = std::clamp(x, 0.0f, 1.0f); + } + + void sety(float y) + { + my = std::clamp(y, 0.0f, 1.0f); + } + + float norm() const + { + return std::sqrt(mx*mx + my*my); + } + + RestrictedPoint operator+(const RestrictedPoint& right) const + { + RestrictedPoint result; + result.mx = std::clamp(mx + right.mx, 0.0f, 1.0f); + result.my = std::clamp(my + right.my, 0.0f, 1.0f); + return result; + } +}; + + +std::ostream& operator<<(std::ostream& out, const RestrictedPoint& a) +{ + out << "(" << a.getx() << ", " << a.gety() << ")"; + return out; +} + + +int main() +{ + RestrictedPoint a = RestrictedPoint(0.5, 1.2); + cout << a << endl; + + a.setx(2); + cout << a << endl; + + a.sety(-5); + cout << a << endl; + + + RestrictedPoint b = RestrictedPoint(0.4, 0.2); + RestrictedPoint c = RestrictedPoint(0.8, 0.4); + cout << c + b << endl; +} + + + + +/* + Задача: + + 1) Добавьте к классу RestrictedPoint оператор умножения на число типа float + +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/00string.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/00string.cpp new file mode 100644 index 0000000..04955a2 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/00string.cpp @@ -0,0 +1,45 @@ +/* + Создадим свою строку + + Один из самых главных недостатков языка C это работа со строками. + Строки в языке C это просто массивы элементов типа char + char str[100]; или char* p = malloc(100); + + В языке C работать со строками очень неудобно по многим причинам: + - Нужно постоянно следить за тем, чтобы строка умещалась в памяти, которая под нею выделена. + - Строку можно выделить на Стеке, используя обычный массив, и тогда её вместимость нельзя будет увеличить, + а можно выделить в Куче, но тогда придётся самостоятельно выделять и освобождать память + и следить, чтобы не произошли утечки памяти. + - Строки нельзя просто копировать, сравнивать, складывать и т. д. Для этого нужно использовать + специальные функции типа strcpy и другие функции из библиотеки . + + + Создадим же удобный класс строки + Такой чтобы можно было удобно создавать строки, приравнивать, складывать и сравнивать. + Не заботясь о выделении/удалении памяти, и о том, что строка помещается в выделенную память. + + Чтобы можно было писать вот такой код: + + String a = "Cat"; + String b = "Dog"; + cout << a << " " << b << endl; + + String c = "Mouse"; + c = a + b; + c += "Bear"; + + if (c == "CatDogBear") + cout << "Yes" << endl; + + c = a; + cout << c << endl; + + + (String в переводе с английского это Строка) +*/ + + + + +int main() {} + diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/01constructor.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/01constructor.cpp new file mode 100644 index 0000000..11603f4 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/01constructor.cpp @@ -0,0 +1,165 @@ +/* + + Создадим строку, которая при создании (т.е в конструкторе) будет автоматически выделять необходимую память в Куче. + В чём-то реализация этой строки будет похожа на реализацию динамического массива из прошлого семестра. + + У класса строки 3 поля: + + mSize - размер - количество символов в строке (без учёта \0) + mCapacity - вместимость - количество выделенной памяти в Куче + mpData - указатель на выделенную память в Куче. В этой памяти строка будет иметь такой же вид, как и строка языка C. + В частности она будет иметь символ '\0' на конце. + Символ '\0' на конце оставим, чтобы было удобно конвертировать нашу строку в строку в стиле C. + + + Размер и вместимость это разные величины. + + Например, для хранения строки "Cat" может быть выделено памяти под 10 символов, хоть и под эту строку было бы достаточно всего 4. + Представьте, что у вас есть длинная строка и вы хотите удалить последний символ в ней. + Если бы мы не хранили вместимость, а всегда выделяли памяти в притык, то в этом простом случае нам бы пришлось + перевыделять память и копировать всю строку в новую память. + Если же мы храним вместимость, то достаточно всего лишь уменьшить размер на 1 и поставит \0 в новый конец строки. + + + + Теперь создадим конструктор, который будет выделять память и заполнять его нужным образом + + Конструктор String(const char* str) конструирует нашу строку из строки в стиле C. + Принимаем на вход именно константную строку, так как в этом случае в конструктор можно будет + передать как константные, так и неконстантые строки. + Если бы мы написали так String(char* str) , то в конструктор нельзя было бы передать константные строки. + + + В строках: + + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + мы находим размер переданной строки (strlen не используем, чтобы не связываться со старой библиотекой) + Вместимость на 1 больше, так как нужно учесть память под символ /0 + + + В строке: + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + выделяем необходимую память + + + В строках: + + for (size_t i = 0; str[i] != '\0'; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + + копируем содержимое переданной строки в только что выделенную память. + + + Другие методы класса String: + + getSize - возвращает размер строки + getCapacity - возвращает вместимость строки + cStr - возвращает строку в стиле C, то есть указатель на массив из char-ов с конечным символом \0 +*/ + +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i] != '\0'; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + + size_t getSize() const + { + return mSize; + } + + size_t getCapacity() const + { + return mCapacity; + } + + const char* cStr() const + { + return mpData; + } +}; + + +std::ostream& operator<<(std::ostream& out, const String& s) +{ + out << s.cStr(); + return out; +} + + +int main() +{ + String a = "Cat"; + String b = "Dog"; + String c = "Lion"; + + cout << a << endl << b << endl << c << endl; +} + + +/* + Задание: + + 1) Создайте конструктор String(), который будет создавать пустую строку + (mSize = 0, mCapacity = 1, строка mpData содержит в себе 1 символ ('\0')) + + Конструктор, который не принимает аргументов называется конструктором по умолчанию + + + 2) Создайте конструктор String(size_t n, char a), который будет создавать строку из n символов a + (mSize = n, mCapacity = n + 1, строка mpData содержит в себе n + 1 символ (n раз a и '\0')) + + + 2) Создайте конструктор String(const String& s), который будет создавать строку String из другой строки String + (mSize = s.mSize, mCapacity = s.mCapacity, строка mpData содержит в себе копию строки s.mpData) + + Конструктор, который создаёт объект по другому объекту такого же типа называется конструктором копирования. + + + Протестируйте эти конструкторы: + String a; + cout << a << endl; + + String b(10, 'q'); + cout << b << endl; + + String c("Cat"); + cout << c << endl; + + String d(c); + cout << d << endl; +*/ diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/01constructor_solution.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/01constructor_solution.cpp new file mode 100644 index 0000000..e3e6699 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/01constructor_solution.cpp @@ -0,0 +1,104 @@ +#include +#include +using std::cout, std::endl, std::size_t; + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + cout << "Construtor from const char*" << endl; + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i] != '\0'; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() + { + cout << "Default Construtor" << endl; + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + mpData[0] = '\0'; + } + + String(size_t n, char a) + { + cout << "Construtor n equal characters" << endl; + mSize = n; + mCapacity = n + 1; + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + mpData[i] = a; + mpData[mSize] = '\0'; + } + + + String(const String& s) + { + cout << "Copy Constructor" << endl; + mSize = s.mSize; + mCapacity = s.mCapacity; + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + mpData[i] = s.mpData[i]; + mpData[mSize] = '\0'; + } + + size_t getSize() const + { + return mSize; + } + + size_t getCapacity() const + { + return mCapacity; + } + + const char* cStr() const + { + return mpData; + } +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + +int main() +{ + String a; + cout << a << endl << endl; + + String b(10, 'q'); + cout << b << endl << endl; + + + String c = "Cat"; + cout << c << endl << endl; + + + String d(c); + cout << c << endl << endl; +} diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/02delegated_constructor.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/02delegated_constructor.cpp new file mode 100644 index 0000000..fbee670 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/02delegated_constructor.cpp @@ -0,0 +1,109 @@ +/* + Делегирующий конструктор + + В разных конструкторах может быть повторяющийся код. + Повторений кода иногда можно избежать если писать один конструктор на основе уже написанного. + + Это можно сделать с помощью синтаксиса так называемого делегирующего конструктора + После объявления конструктора, но перед его телом можно написать двоеточие и вызвать другой конструктор + + Например: + + String() : String("") + { + cout << "Default Constructor" << endl; + } + + Этот конструктор сначала вызовет конструктор String(const char* str) с аргументом "" (то есть пустой строкой) + а потом исполнит тело данного коструктора (в данном случае напечатает Default Constructor). +*/ + +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + cout << "Constructor" << endl; + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i] != '\0'; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") + { + cout << "Default Constructor" << endl; + } + + + String(const String& s) : String(s.cStr()) + { + cout << "Copy Constructor" << endl; + } + + size_t getSize() const + { + return mSize; + } + + size_t getCapacity() const + { + return mCapacity; + } + + const char* cStr() const + { + return mpData; + } +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + +int main() +{ + String a; + cout << "a = " << a << endl << endl; + + + String b = "Cat"; + cout << "b = " << b << endl << endl; + + + String c(b); + cout << "c = " << c << endl << endl; +} + + + + + +/* + Задача: + + Попробуйте понять без запуска, что напечатает данная программа +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/03destructor.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/03destructor.cpp new file mode 100644 index 0000000..6557d30 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/03destructor.cpp @@ -0,0 +1,113 @@ +/* + В конструкторе мы выделили память с malloc, но нигде в программе её не освободили + Соответственно, в предыдущей программе у нас есть очень серьёзная ошибка - утечка памяти. + + Где же нужно освобождать память? + Если память была выделена в конструкторе при создании объекта, то выделять её нужно при удалении объекта. + Для того, чтобы испольнить ваш код при удалении объекта существует специальный метод, который называется Деструктор. + + + Деструктор - это специальный метод, который вызывается тогда, когда объект уничтожается + Объекты, созданные на стеке удаляются при выходе из области видимости + + + Синтаксис деструктора такой: + + ~String() + { + ... + } +*/ + +#include +#include +using std::cout, std::endl, std::size_t; + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + + String(const char* str) + { + cout << "Constructor of " << str << endl; + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i] != '\0'; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + + ~String() + { + cout << "Destructor of " << mpData << endl; + std::free(mpData); + } + + + size_t getSize() const + { + return mSize; + } + + size_t getCapacity() const + { + return mCapacity; + } + + const char* cStr() const + { + return mpData; + } +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + +int main() +{ + String a = "Cat"; + String b = "Dog"; + + if (true) + { + String c = "Lion"; + } + + String c = "Bear"; +} + + + +/* + Задание: + + 1) Что напечатает данная программа? + В каком порядке вызовутся конструкторы + + + 2) Если создать строку String в цикле, то будут ли каждую итерацию вызываться конструкторы и деструкторы? + + for (int i = 0; i < 10; ++i) + { + String s = "Elephant"; + } +*/ diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/04operator_addition.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/04operator_addition.cpp new file mode 100644 index 0000000..7dc87fb --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/04operator_addition.cpp @@ -0,0 +1,94 @@ +/* + Оператор сложения + + Оператор сложения для строк должен принимать 2 строки и возвращать новую строку, равную результату конкатенации двух строк + Если строка a = "Cat" , а строка b = "Mouse" , то их конкатенация это a + b = "CatMouse" + + Оператор сложения должен принимать свои аргументы по константной ссылке + по ссылке, чтобы не копировать объект лишний раз при передаче в функцию + по константной, потому что мы не будем менять принимаемые объекты внутри функции + + + То есть прототип оператора сложения, если его делать с помощью обычной функции, должен выглядеть так: + + String operator+(const String& a, const String& b) + + + Прототип оператора сложения, если его делать с помощью метода класса String, должен выглядеть так: + + String operator+(const String& b) const +*/ + +#include +#include +using std::cout, std::endl, std::size_t; + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + cout << "Constructor of " << str << endl; + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i] != '\0'; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + + ~String() + { + cout << "Destructor of " << mpData << endl; + std::free(mpData); + } + + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + +int main() +{ + String a = "Cat"; + String b = "Mouse"; + + cout << a + b << endl; +} + + + +/* + Задание: + + 1) Написать оператор сложения для строки String в виде свободной функции + В этом случае эту функцию нужно сделать дружественной классу String, + чтобы она имела доступ к приватным полям класса + + 2) Написать оператор сложения для строки String в виде метода класса String +*/ diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/04operator_addition_solution1.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/04operator_addition_solution1.cpp new file mode 100644 index 0000000..b59f25e --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/04operator_addition_solution1.cpp @@ -0,0 +1,86 @@ +#include +#include +using std::cout, std::endl, std::size_t; + + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i] != '\0'; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + + ~String() + { + std::free(mpData); + } + + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} + + friend String operator+(const String& a, const String& b); +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + +String operator+(const String& a, const String& b) +{ + String result; + + result.mSize = a.mSize + b.mSize; + result.mCapacity = result.mSize + 1; + result.mpData = (char*)std::malloc(sizeof(char) * result.mCapacity); + + for (size_t i = 0; i < a.mSize; ++i) + result.mpData[i] = a.mpData[i]; + + for (size_t i = 0; i < b.mSize; ++i) + result.mpData[a.mSize + i] = b.mpData[i]; + + result.mpData[result.mSize] = '\0'; + + return result; +} + + +int main() +{ + String a = "Cat"; + String b = "Mouse"; + + cout << a + b << endl; +} + + + diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/04operator_addition_solution2.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/04operator_addition_solution2.cpp new file mode 100644 index 0000000..c3b3eaf --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/04operator_addition_solution2.cpp @@ -0,0 +1,86 @@ +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + String operator+(const String& b) + { + String result; + + result.mSize = mSize + b.mSize; + result.mCapacity = result.mSize + 1; + result.mpData = (char*)std::malloc(sizeof(char) * result.mCapacity); + + for (size_t i = 0; i < mSize; ++i) + result.mpData[i] = mpData[i]; + + for (size_t i = 0; i < b.mSize; ++i) + result.mpData[mSize + i] = b.mpData[i]; + + result.mpData[result.mSize] = '\0'; + + return result; + } + + + ~String() + { + std::free(mpData); + } + + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} + + friend String operator+(const String& a, const String& b); +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + + +int main() +{ + String a = "Cat"; + String b = "Mouse"; + + cout << a + b << endl; +} + + + diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/05operator_assignment.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/05operator_assignment.cpp new file mode 100644 index 0000000..4775647 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/05operator_assignment.cpp @@ -0,0 +1,126 @@ +/* + Оператор присваивания + + Оператор присваивания можно сделать только в виде метода + Такой метод должен + - принимать один аргумент - это правый аргумент оператора присваивания, + - менять объект, вызвавший оператор (то есть левый аргумент оператора присваивания) + - возвращать ссылку на объект, вызвавший оператор (то есть объект слева от оператора присваивания) + + + Почему оператор присваивания должен возвращать ссылку на левый аргумент? + Чтобы оператор присваивания работал аналогично тому как работает оператор присваивания для обычных типов + + Рассмотрим следующий пример: + + int a, b, c; + a = b = c = 123; // все переменные станут равны 123, операторы выполняются справа налево + + (a = 1) = 2; // a станет равной 2 + + + Мы хотим, чтобы и такой код работал: + + String a, b, c; + a = b = c = "Cat"; + + (a = "Dog") = "Mouse"; + + + Прототип оператора присваивания как метода класса String должен выглядеть так: + + String& operator=(const String& b) +*/ + +#include +#include +using std::cout, std::endl, std::size_t; + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + + ~String() + { + std::free(mpData); + } + + + String& operator=(const String& right) + { + // Ваш код нужно написать здесь + } + + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + +int main() +{ + String a = "Cat"; + String b = "Mouse"; + String c; + + a = b; + cout << a << endl; + + a = b = c = "Elephant"; + cout << a << endl; + cout << b << endl; + cout << c << endl; + + (a = "Dog") = "Axolotl"; + cout << a << endl; + + a = a; + cout << a << endl; +} + + + +/* + Задание: + + 1) Написать оператор присваивания для строки String в виде метода класса String + + Не забудьте учесть случай: + + String a = "Cat"; + a = a; +*/ diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/05operator_assignment_solution.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/05operator_assignment_solution.cpp new file mode 100644 index 0000000..9f74f17 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/05operator_assignment_solution.cpp @@ -0,0 +1,92 @@ +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + + ~String() + { + std::free(mpData); + } + + + String& operator=(const String& right) + { + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + +int main() +{ + String a = "Cat"; + String b = "Mouse"; + String c; + + a = b; + cout << a << endl; + + a = b = c = "Elephant"; + cout << a << endl; + cout << b << endl; + cout << c << endl; + + (a = "Dog") = "Axolotl"; + cout << a << endl; + +} diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/06operator_addition_assignment.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/06operator_addition_assignment.cpp new file mode 100644 index 0000000..c00d277 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/06operator_addition_assignment.cpp @@ -0,0 +1,132 @@ +/* + Оператор присваивания сложения += + + Очень похож на оператор присваивания, разница только в том, каким станет левый операнд после применения этого оператора + К левому операнду в этом случае должна прибавиться копия правого оператора + + Если a = "Cat" , b = "Dog" , то после применения a += b строка a будет равна "CatDog" + + + + Прототип оператора присваивания сложения как метода класса String должен выглядеть так: + + String& operator+=(const String& b) +*/ + +#include +#include +using std::cout, std::endl, std::size_t; + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + + ~String() + { + std::free(mpData); + } + + String operator+(const String& b) + { + String result; + + result.mSize = mSize + b.mSize; + result.mCapacity = result.mSize + 1; + result.mpData = (char*)std::malloc(sizeof(char) * result.mCapacity); + + for (size_t i = 0; i < mSize; ++i) + result.mpData[i] = mpData[i]; + + for (size_t i = 0; i < b.mSize; ++i) + result.mpData[mSize + i] = b.mpData[i]; + + result.mpData[result.mSize] = '\0'; + + return result; + } + + + String& operator=(const String& right) + { + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + + + String& operator+=(const String& right) + { + // Ваш код нужно написать здесь + } + + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + +int main() +{ + String a = "Mouse"; + String b = "Elephant"; + + b += a; + + cout << b << endl; +} + + + +/* + Задание: + + 1) Написать оператор присваивания сложения для строки String в виде метода класса String + + Подсказка: можно использовать уже написанные операторы, чтобы реализовать этот +*/ diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/06operator_addition_assignment_solution.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/06operator_addition_assignment_solution.cpp new file mode 100644 index 0000000..8b06b3a --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/06operator_addition_assignment_solution.cpp @@ -0,0 +1,110 @@ +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + + ~String() + { + std::free(mpData); + } + + String operator+(const String& b) + { + String result; + + result.mSize = mSize + b.mSize; + result.mCapacity = result.mSize + 1; + result.mpData = (char*)std::malloc(sizeof(char) * result.mCapacity); + + for (size_t i = 0; i < mSize; ++i) + result.mpData[i] = mpData[i]; + + for (size_t i = 0; i < b.mSize; ++i) + result.mpData[mSize + i] = b.mpData[i]; + + result.mpData[result.mSize] = '\0'; + + return result; + } + + + String& operator=(const String& right) + { + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + + + String& operator+=(const String& right) + { + *this = *this + right; + return *this; + } + + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + +int main() +{ + String a = "Mouse"; + String b = "Elephant"; + + b += a; + + cout << b << endl; +} + diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/07operator_addition_alt.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/07operator_addition_alt.cpp new file mode 100644 index 0000000..31dc20c --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/07operator_addition_alt.cpp @@ -0,0 +1,122 @@ +/* + По решению предыдущего занятия понятно, что операторы + = и += взаимосвязаны. + + Если реализованы операторы + и = можно, используя их, реализовать оператор += + Если реализованы операторы = и += можно, используя их, реализовать оператор + +*/ + +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + + ~String() + { + std::free(mpData); + } + + + String& operator=(const String& right) + { + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + String& operator+=(const String& right) + { + if (mCapacity < mSize + right.mSize + 1) + { + mCapacity = mSize + right.mSize + 1; + char* pNewData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + pNewData[i] = mpData[i]; + + std::free(mpData); + mpData = pNewData; + } + + for (size_t i = 0; i < right.mSize; ++i) + mpData[mSize + i] = right.mpData[i]; + + mSize += right.mSize; + mpData[mSize] = '\0'; + + return *this; + } + + String operator+(const String& b) + { + + } + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + +int main() +{ + String a = "Mouse"; + String b = "Elephant"; + + cout << a + b << endl; +} + + + +/* + Задача: + + 1) Напишите оператор + , используя операторы = и += + +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/07operator_addition_alt_solution.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/07operator_addition_alt_solution.cpp new file mode 100644 index 0000000..9a51e3c --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/07operator_addition_alt_solution.cpp @@ -0,0 +1,108 @@ +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + + ~String() + { + std::free(mpData); + } + + + String& operator=(const String& right) + { + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + String& operator+=(const String& right) + { + if (mCapacity < mSize + right.mSize + 1) + { + mCapacity = mSize + right.mSize + 1; + char* pNewData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + pNewData[i] = mpData[i]; + + std::free(mpData); + mpData = pNewData; + } + + for (size_t i = 0; i < right.mSize; ++i) + mpData[mSize + i] = right.mpData[i]; + + mSize += right.mSize; + mpData[mSize] = '\0'; + + return *this; + } + + String operator+(const String& b) + { + String result = *this; + result += b; + return result; + } + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + +int main() +{ + String a = "Mouse"; + String b = "Elephant"; + + cout << a + b << endl; +} \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/08comparasion_operators.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/08comparasion_operators.cpp new file mode 100644 index 0000000..74563db --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/08comparasion_operators.cpp @@ -0,0 +1,120 @@ +/* + Операторы сравнения == != > >= < <= + + К строкам можно применять операторы сравнения. + + Очевидно, когда строки равны: должны быть равны размеры строк (mSize) и все символы от 0 до mSize + При этом вместимость (mCapacity) у равных строк может отличаться + + Как сравнивать строки на больше/меньше? В этом случае сравниваем лексикографически, то есть, по алфавиту. + То слово, которое находилось бы в орфографическом словаре позже и будет большим. + Например, "Cat" > "Camel" так как первые 2 буквы совпадают, а третья буква у слова Cat идёт дальше по алфавиту + + + Более точное сравнение такое: мы сравниваем посимвольно до первого несовпадающего символа + Если мы нашли первые несовпадающий символ и не дошли до конца в обоих строках, то та строка будет больше, + у которой этот символ больше. + +*/ + +#include +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + ~String() + { + std::free(mpData); + } + + String& operator=(const String& right) + { + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + +int main() +{ + cout << std::boolalpha; + + String a = "Cat"; + String b = "Camel"; + + cout << (a > b) << endl; + cout << (a < b) << endl; + cout << (a == b) << endl; + cout << (a == a) << endl; + + + String c = "Catharsis"; + + cout << (a > c) << endl; + cout << (a < c) << endl; + cout << (a == c) << endl; + cout << (a != c) << endl; +} + + + +/* + Задача: + + 1) Напишите операторы == != > >= < <= для класса String + Подсказка: можно использовать уже написанные сравнения + Например, если вы написали оператор > , то очень просто написать оператор <= , используя оператор > + +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/08comparasion_operators_solution.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/08comparasion_operators_solution.cpp new file mode 100644 index 0000000..43e2026 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/08comparasion_operators_solution.cpp @@ -0,0 +1,140 @@ +#include +#include +#include +using std::cout, std::endl, std::size_t; + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + + ~String() + { + std::free(mpData); + } + + String& operator=(const String& right) + { + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + + bool operator==(const String& right) const + { + if (mSize != right.mSize) + return false; + + size_t i = 0; + while (i < mSize && mpData[i] == right.mpData[i]) + i++; + + return i == mSize; + } + + bool operator<(const String& right) const + { + size_t i = 0; + while (i < mSize && i < right.mSize && mpData[i] == right.mpData[i]) + i++; + + return mpData[i] < right.mpData[i]; + } + + bool operator<=(const String& right) const + { + size_t i = 0; + while (i < mSize && i < right.mSize && mpData[i] == right.mpData[i]) + i++; + + return mpData[i] <= right.mpData[i]; + } + + bool operator!=(const String& right) const + { + return !(*this == right); + } + + bool operator>(const String& right) const + { + return !(*this <= right); + } + + bool operator>=(const String& right) const + { + return !(*this < right); + } + + + + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + +int main() +{ + cout << std::boolalpha; + + String a = "Cat"; + String b = "Camel"; + + cout << (a > b) << endl; + cout << (a < b) << endl; + cout << (a == b) << endl; + cout << (a == a) << endl; + + + String c = "Catharsis"; + + cout << (a > c) << endl; + cout << (a < c) << endl; + cout << (a == c) << endl; + cout << (a != c) << endl; +} \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/09subscript_operator.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/09subscript_operator.cpp new file mode 100644 index 0000000..2dfb9b5 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/09subscript_operator.cpp @@ -0,0 +1,122 @@ +/* + Операторы индексации [] + + Чтобы получить доступ к одному символу у строки в стиле C можно использовать оператор индексации (квадратные скобочки). + Хотелось бы иметь такую же возможность и для нашей строки. + + Для этого можно перегрузить оператор индексации: + + char& operator[](size_t i) + + Этот оператор вызавется при взятии символа по индексу, например, если a это строка типа String, + + a[i] будет восприниматься компилятором как a.operator[](i) + + + Оператор индексации должен возвращать ссылку на символ, чтобы можно было менять соответствующий символ + + a[i] = 'A'; +*/ + +#include +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + + ~String() + { + std::free(mpData); + } + + String& operator=(const String& right) + { + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + +int main() +{ + String a = "Cat"; + + cout << a[0] << endl; + cout << a[2] << endl; + cout << a[4] << endl; + + + cout << a.at(0) << endl; + cout << a.at(2) << endl; + cout << a.at(4) << endl; + +} + + + +/* + Задача: + + 1) Напишите оператор индексации для класса String + + + 2) Напишите метод at, который будет работать аналогично оператору индексации, только с тем отличием, что + если на вход приходит неправильный индекс (т. е. индекс >= mSize), то метод at должен печатать сообщение + об ошибке и завершать программу. + Для завершения программы используйте функцию std::exit(1) из библиотеки . + +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/09subscript_operator_solution.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/09subscript_operator_solution.cpp new file mode 100644 index 0000000..f7ed062 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/09subscript_operator_solution.cpp @@ -0,0 +1,102 @@ +#include +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + + ~String() + { + std::free(mpData); + } + + String& operator=(const String& right) + { + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + char& at(size_t i) + { + if (i >= mSize) + { + cout << "Error! Index is out of bounds." << endl; + std::exit(1); + } + return mpData[i]; + } + + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + +int main() +{ + String a = "Cat"; + + cout << a[0] << endl; + cout << a[2] << endl; + cout << a[4] << endl; + + + cout << a.at(0) << endl; + cout << a.at(2) << endl; + cout << a.at(4) << endl; + +} diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/10reserve_and_resize.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/10reserve_and_resize.cpp new file mode 100644 index 0000000..694065f --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/10reserve_and_resize.cpp @@ -0,0 +1,145 @@ +/* + Напишем ещё 2 очень полезных метода: + + 1) reserve - увеличивает вместимость строки, на вход методу передаётся новая вместмость + если новая вместимость меньше старой, то ничего не происходит (вместимость не уменьшается) + размер и содержимое строки не меняется + + + 2) resize - изменяет размер строки, на вход методу передаётся новый размер + если новый размер меньше старого, то строка усекается + при необходимости увеличивает вместимость + + Используя эти два метода можно немного упростить код для операторов сложения и присваивания. + Эти методы могут быть полезны и для программиста, который будет работать с нашей строкой, поэтому сделаем их публичными. +*/ + +#include +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + + ~String() + { + std::free(mpData); + } + + + void reserve(size_t capacity) + { + if (capacity <= mCapacity) + return; + + mCapacity = capacity; + char* newData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + newData[i] = mpData[i]; + newData[mSize] = '\0'; + + std::free(mpData); + mpData = newData; + } + + + void resize(size_t size) + { + reserve(size + 1); + mSize = size; + mpData[mSize] = '\0'; + } + + + String& operator=(const String& right) + { + if (this == &right) + return *this; + + mSize = right.mSize; + resize(mSize); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + + String operator+(const String& b) + { + String result; + result.resize(mSize + b.mSize); + + for (size_t i = 0; i < mSize; ++i) + result.mpData[i] = mpData[i]; + + for (size_t i = 0; i < b.mSize; ++i) + result.mpData[mSize + i] = b.mpData[i]; + + result.mpData[result.mSize] = '\0'; + + return result; + } + + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + +int main() +{ + String a = "Cat"; + String b = "Dog"; + + cout << a.getCapacity() << endl; + a.reserve(10); + cout << a.getCapacity() << endl; + + cout << a + b << endl; + + + String c = "Sapere Aude"; + cout << c << endl; + + c.resize(6); + cout << c << endl; +} diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/10reserve_and_resize_solution.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/10reserve_and_resize_solution.cpp new file mode 100644 index 0000000..5f704fd --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/10reserve_and_resize_solution.cpp @@ -0,0 +1,134 @@ +#include +#include +#include +using std::cout, std::cin, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + + ~String() + { + std::free(mpData); + } + + + void reserve(size_t capacity) + { + if (capacity <= mCapacity) + return; + + mCapacity = capacity; + char* newData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + newData[i] = mpData[i]; + newData[mSize] = '\0'; + + std::free(mpData); + mpData = newData; + } + + + void resize(size_t size) + { + reserve(size + 1); + mSize = size; + mpData[mSize] = '\0'; + } + + + String& operator=(const String& right) + { + if (this == &right) + return *this; + + mSize = right.mSize; + resize(mSize); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + + String operator+(const String& b) + { + String result; + result.resize(mSize + b.mSize); + + for (size_t i = 0; i < mSize; ++i) + result.mpData[i] = mpData[i]; + + for (size_t i = 0; i < b.mSize; ++i) + result.mpData[mSize + i] = b.mpData[i]; + + result.mpData[result.mSize] = '\0'; + + return result; + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + +int main() +{ + String a = "Cat"; + String b = "Dog"; + + cout << a.getCapacity() << endl; + a.reserve(10); + cout << a.getCapacity() << endl; + + cout << a + b << endl; + + + String c = "Sapere Aude"; + cout << c << endl; + + c.resize(6); + cout << c << endl; +} diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/11cin_string_overload.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/11cin_string_overload.cpp new file mode 100644 index 0000000..d91d9ee --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/11cin_string_overload.cpp @@ -0,0 +1,166 @@ +/* + Считывание строки с экрана + + Помимо удобной печати на экран с помощью объекта std::cout, хотелось бы добавить удобное считываие строки + с помощью объекта std::cin. + + Для этого нужно перегрузить оператор >> с объектами типа std::istream и String, то есть написать функцию: + + std::istream& operator>>(std::istream& in, String& str) + + Эта функция должна считывать символы из стандартного входа и добавлять в строку + Основная проблема в том, что мы не знаем сколько символов нужно считать, поэтому нужно считывать + посимвольно и добавлять символы по одному в строку пока не встретим пробельный символ (' ' или '\n' или '\t') + + Для упрощения программы можно написать дополнительные методы + + void clear() - метод, который будет очищать строку + mSize = 0, mCapacity = 1, строка по адресу mpData равна "\0" + + void addCharacter(char c) - добавляет символ в конец строки + если у строки не хватает вместимости, то удваивает вместимость. + + + Для того, чтобы считать символ из стандартного входа можно использовать метод get класса istream + Этот метод возвращает следующий символ из стандартного входа + + char x = cin.get(); +*/ + +#include +#include +#include +using std::cout, std::cin, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + + ~String() + { + std::free(mpData); + } + + + void reserve(size_t capacity) + { + if (capacity <= mCapacity) + return; + + mCapacity = capacity; + char* newData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + newData[i] = mpData[i]; + newData[mSize] = '\0'; + + std::free(mpData); + mpData = newData; + } + + + void resize(size_t size) + { + reserve(size + 1); + mSize = size; + mpData[mSize] = '\0'; + } + + + String& operator=(const String& right) + { + if (this == &right) + return *this; + + mSize = right.mSize; + resize(mSize); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + + String operator+(const String& b) + { + String result; + result.resize(mSize + b.mSize); + + for (size_t i = 0; i < mSize; ++i) + result.mpData[i] = mpData[i]; + + for (size_t i = 0; i < b.mSize; ++i) + result.mpData[mSize + i] = b.mpData[i]; + + result.mpData[result.mSize] = '\0'; + + return result; + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + +int main() +{ + String a, b; + cin >> a >> b; + cout << a + b; + + a.addCharacter('!'); + cout << a << endl; +} + + +/* + Задача: + + 1) Напишите метод clear. + + 2) Напишите метод addCharacter. + + 3) Напишите перегруженный оператор << для считывания строки с экрана. + +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/11cin_string_overload_solution.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/11cin_string_overload_solution.cpp new file mode 100644 index 0000000..0afe413 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/11cin_string_overload_solution.cpp @@ -0,0 +1,158 @@ +#include +#include +#include +using std::cout, std::cin, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + + ~String() + { + std::free(mpData); + } + + + void reserve(size_t capacity) + { + if (capacity <= mCapacity) + return; + + mCapacity = capacity; + char* newData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + newData[i] = mpData[i]; + newData[mSize] = '\0'; + + std::free(mpData); + mpData = newData; + } + + + void resize(size_t size) + { + reserve(size + 1); + mSize = size; + mpData[mSize] = '\0'; + } + + + String& operator=(const String& right) + { + if (this == &right) + return *this; + + mSize = right.mSize; + resize(mSize); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + + String operator+(const String& b) + { + String result; + result.resize(mSize + b.mSize); + + for (size_t i = 0; i < mSize; ++i) + result.mpData[i] = mpData[i]; + + for (size_t i = 0; i < b.mSize; ++i) + result.mpData[mSize + i] = b.mpData[i]; + + result.mpData[result.mSize] = '\0'; + + return result; + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + void clear() + { + std::free(mpData); + + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + mpData[0] = '\0'; + } + + void addCharacter(char c) + { + if (mSize + 1 == mCapacity) + reserve(2 * mCapacity); + + mpData[mSize] = c; + resize(mSize + 1); + } + + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& out, const String& s) +{ + out << s.cStr(); + return out; +} + + +std::istream& operator>>(std::istream& in, String& s) +{ + s.clear(); + while (true) + { + char x = in.get(); + if (x == ' ' || x == '\n' || x == '\t') + break; + s.addCharacter(x); + } + return in; +} + + +int main() +{ + String a, b; + cin >> a >> b; + cout << a + b << endl; + + a.addCharacter('!'); + cout << a << endl; +} \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/12full_string.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/12full_string.cpp new file mode 100644 index 0000000..1868129 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/12full_string.cpp @@ -0,0 +1,262 @@ +/* + Собираем все методы вместе. Получилась строка, которой можно удобно пользоваться и не задумываться о выделении памяти. +*/ + +#include +#include +using std::cout, std::cin, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + String(size_t n, char a) + { + mSize = n; + mCapacity = n + 1; + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + mpData[i] = a; + mpData[mSize] = '\0'; + } + + ~String() + { + std::free(mpData); + } + + void reserve(size_t capacity) + { + if (capacity <= mCapacity) + return; + + mCapacity = capacity; + char* newData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + newData[i] = mpData[i]; + newData[mSize] = '\0'; + + std::free(mpData); + mpData = newData; + } + + + void resize(size_t size) + { + reserve(size + 1); + mSize = size; + mpData[mSize] = '\0'; + } + + + String& operator=(const String& right) + { + if (this == &right) + return *this; + + mSize = right.mSize; + resize(mSize); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + + String operator+(const String& b) + { + String result; + result.resize(mSize + b.mSize); + + for (size_t i = 0; i < mSize; ++i) + result.mpData[i] = mpData[i]; + + for (size_t i = 0; i < b.mSize; ++i) + result.mpData[mSize + i] = b.mpData[i]; + + result.mpData[result.mSize] = '\0'; + + return result; + } + + String& operator+=(const String& right) + { + *this = *this + right; + return *this; + } + + bool operator==(const String& right) const + { + if (mSize != right.mSize) + return false; + + size_t i = 0; + while (i < mSize && mpData[i] == right.mpData[i]) + i++; + + return i == mSize; + } + + bool operator<(const String& right) const + { + size_t i = 0; + while (i < mSize && i < right.mSize && mpData[i] == right.mpData[i]) + i++; + + return mpData[i] < right.mpData[i]; + } + + bool operator<=(const String& right) const + { + size_t i = 0; + while (i < mSize && i < right.mSize && mpData[i] == right.mpData[i]) + i++; + + return mpData[i] <= right.mpData[i]; + } + + bool operator!=(const String& right) const + { + return !(*this == right); + } + + bool operator>(const String& right) const + { + return !(*this <= right); + } + + bool operator>=(const String& right) const + { + return !(*this < right); + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + char& at(size_t i) + { + if (i >= mSize) + { + cout << "Error! Index is out of bounds." << endl; + std::exit(1); + } + return mpData[i]; + } + + void clear() + { + std::free(mpData); + + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + mpData[0] = '\0'; + } + + void addCharacter(char c) + { + if (mSize + 1 == mCapacity) + reserve(2 * mCapacity); + + mpData[mSize] = c; + resize(mSize + 1); + } + + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& out, const String& s) +{ + out << s.cStr(); + return out; +} + +std::istream& operator>>(std::istream& in, String& s) +{ + s.clear(); + while (true) + { + char x = in.get(); + if (x == ' ' || x == '\n' || x == '\t') + break; + s.addCharacter(x); + } + return in; +} + + +int main() +{ + String a = "Mouse"; + String b; + cin >> b; + String c = b; + + if (a + c == "MouseLion") + cout << "Yes" << endl; + else + cout << "No" << endl; + + + if (a > "Mice") + cout << "Yes" << endl; + else + cout << "No" << endl; + + + c[0] = 'P'; + cout << c << endl; + + c += a; + cout << c << endl; + + c = c + String(10, 'q'); + cout << c << endl; +} + + + +/* + Задача: + + + 1) Напищите программу, которая будет считывать слова (используйте cin) в бесконечном цикле и каждый + раз печатать сумму всех слов. Например, если пользователь ввёл Hello, то программа должна напечатать + Hello и запросить следующее слово. Если затем пользователь введёт World, то программа должна будет + напечатать HelloWorld и запросить следуещее слово и так далее. + Программа должна останавливаться если пользователь ввёл слово "quit". +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/12full_string_solution.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/12full_string_solution.cpp new file mode 100644 index 0000000..7a2022e --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/12full_string_solution.cpp @@ -0,0 +1,233 @@ +#include +#include +using std::cout, std::cin, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + String(size_t n, char a) + { + mSize = n; + mCapacity = n + 1; + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + mpData[i] = a; + mpData[mSize] = '\0'; + } + + ~String() + { + std::free(mpData); + } + + void reserve(size_t capacity) + { + if (capacity <= mCapacity) + return; + + mCapacity = capacity; + char* newData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + newData[i] = mpData[i]; + newData[mSize] = '\0'; + + std::free(mpData); + mpData = newData; + } + + + void resize(size_t size) + { + reserve(size + 1); + mSize = size; + mpData[mSize] = '\0'; + } + + + String& operator=(const String& right) + { + if (this == &right) + return *this; + + mSize = right.mSize; + resize(mSize); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + + String operator+(const String& b) + { + String result; + result.resize(mSize + b.mSize); + + for (size_t i = 0; i < mSize; ++i) + result.mpData[i] = mpData[i]; + + for (size_t i = 0; i < b.mSize; ++i) + result.mpData[mSize + i] = b.mpData[i]; + + result.mpData[result.mSize] = '\0'; + + return result; + } + + String& operator+=(const String& right) + { + *this = *this + right; + return *this; + } + + bool operator==(const String& right) const + { + if (mSize != right.mSize) + return false; + + size_t i = 0; + while (i < mSize && mpData[i] == right.mpData[i]) + i++; + + return i == mSize; + } + + bool operator<(const String& right) const + { + size_t i = 0; + while (i < mSize && i < right.mSize && mpData[i] == right.mpData[i]) + i++; + + return mpData[i] < right.mpData[i]; + } + + bool operator<=(const String& right) const + { + size_t i = 0; + while (i < mSize && i < right.mSize && mpData[i] == right.mpData[i]) + i++; + + return mpData[i] <= right.mpData[i]; + } + + bool operator!=(const String& right) const + { + return !(*this == right); + } + + bool operator>(const String& right) const + { + return !(*this <= right); + } + + bool operator>=(const String& right) const + { + return !(*this < right); + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + char& at(size_t i) + { + if (i >= mSize) + { + cout << "Error! Index is out of bounds." << endl; + std::exit(1); + } + return mpData[i]; + } + + + void clear() + { + std::free(mpData); + + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + mpData[0] = '\0'; + } + + void addCharacter(char c) + { + if (mSize + 1 == mCapacity) + reserve(2 * mCapacity); + + mpData[mSize] = c; + resize(mSize + 1); + } + + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& out, const String& s) +{ + out << s.cStr(); + return out; +} + +std::istream& operator>>(std::istream& in, String& s) +{ + s.clear(); + while (true) + { + char x = in.get(); + if (x == ' ' || x == '\n' || x == '\t') + break; + s.addCharacter(x); + } + return in; +} + + +int main() +{ + String all; + String nextWord; + + while (true) + { + cin >> nextWord; + if (nextWord == "quit") + break; + + all += nextWord; + cout << all << endl; + } +} \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/13problem_init_vs_assignment.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/13problem_init_vs_assignment.cpp new file mode 100644 index 0000000..1157041 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/13problem_init_vs_assignment.cpp @@ -0,0 +1,142 @@ +/* + Рассмотрим при каких условиях происходит вызов того или иного метода. + + Для этого будем использовать класс String, в котором была добавлена печать на экран для конструкторов, + деструктора и оператора присваивания. + Например, конструктор по умолчанию будет печатать Default Constructor и т. д. +*/ + +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + cout << "String Constructor from const char* (" << str << ")" << endl; + + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; ++i) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() + { + cout << "String Default Constructor" << endl; + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char)); + mpData[0] = '\0'; + } + + String(const String& s) + { + cout << "String Copy Constructor (" << s.mpData << ")" << endl; + + size_t i = 0; + mSize = s.mSize; + mCapacity = mSize + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + mpData[i] = s.mpData[i]; + mpData[mSize] = '\0'; + } + + ~String() + { + cout << "String Destructor (" << mpData << ")" << endl; + std::free(mpData); + } + + String& operator=(const String& right) + { + cout << "String Assignment Operator (" << right.mpData << ")" << endl; + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + + +int main() +{ + String a = "Cat"; + String b = a; +} + +/* + Задачи: + + 1) Какие методы вызовутся в строке + + String a = "Cat"; + + Выберите один из вариантов: + + а) Только конструктор из const char* + б) Только оператор присваивания + в) И конструктор из const char* и оператор присваивания + + + 2) Какие методы вызовутся в строке + + String b = a; + + Выберите один из вариантов: + + а) Только конструктор копирования + б) Только оператор присваивания + в) И конструктор копирования и оператор присваивания + + + + 3) Проверьте ваши догадки скомпилировав и запустив программу + +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/14problem_init_vs_assignment.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/14problem_init_vs_assignment.cpp new file mode 100644 index 0000000..4a6108b --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/14problem_init_vs_assignment.cpp @@ -0,0 +1,121 @@ +/* + Рассмотрим при каких условиях происходит вызов того или иного метода. + + Для этого будем использовать класс String, в котором была добавлена печать на экран для конструкторов, + деструктора и оператора присваивания. + Например, конструктор по умолчанию будет печатать Default Constructor и т. д. +*/ + +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + cout << "String Constructor from const char* (" << str << ")" << endl; + + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; ++i) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() + { + cout << "String Default Constructor" << endl; + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char)); + mpData[0] = '\0'; + } + + String(const String& s) + { + cout << "String Copy Constructor (" << s.mpData << ")" << endl; + + size_t i = 0; + mSize = s.mSize; + mCapacity = mSize + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + mpData[i] = s.mpData[i]; + mpData[mSize] = '\0'; + } + + ~String() + { + cout << "String Destructor (" << mpData << ")" << endl; + std::free(mpData); + } + + String& operator=(const String& right) + { + cout << "String Assignment Operator (" << right.mpData << ")" << endl; + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + + +int main() +{ + String a = "Cat"; + String c; + c = a; +} + +/* + Задачи: + + 1) Какие методы класса String вызовутся в данной программе. + + 2) Проверьте ваши догадки, скомпилировав и запустив программу +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/14problem_init_vs_assignment_solution.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/14problem_init_vs_assignment_solution.cpp new file mode 100644 index 0000000..0fdd67a --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/14problem_init_vs_assignment_solution.cpp @@ -0,0 +1,36 @@ +/* + Решения: + + 1) String a = "Cat"; + + Будет вызван только конструктор из const char* + + + + 2) String b = a; + + Будет вызван только конструктор копирования + + Несмотря на то, что в этих выражениях присутствует символ = оператор присваивания в этих случаях не вызывается. + + + + + 3) String c; + c = a; + + Будет вызван конструктор по умолчанию в строке String c; + А потом будет вызван оператор присваивания в строке c = a; + + + Получается символ = может использоваться для обозначения двух разных вещей: инициализации и присваивания. + + + String a = "Cat"; // Инициализация строкой "Cat" (вызов коструктора от const char*) + + String b = a; // Инициализация объектом a (вызов коструктора копирования) + + String c; // Инициализация по умолчанию (вызов конструктора по умолчанию) + c = a; // Присваивание (вызов метода operator=) + +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/15problem_pass_to_funciton.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/15problem_pass_to_funciton.cpp new file mode 100644 index 0000000..f6651cf --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/15problem_pass_to_funciton.cpp @@ -0,0 +1,135 @@ +/* + Посмотрим какие особые методы вызываются при передаче объекта в функцию. +*/ + +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + cout << "String Constructor from const char* (" << str << ")" << endl; + + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; ++i) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() + { + cout << "String Default Constructor" << endl; + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char)); + mpData[0] = '\0'; + } + + String(const String& s) + { + cout << "String Copy Constructor (" << s.mpData << ")" << endl; + + size_t i = 0; + mSize = s.mSize; + mCapacity = mSize + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + mpData[i] = s.mpData[i]; + mpData[mSize] = '\0'; + } + + ~String() + { + cout << "String Destructor (" << mpData << ")" << endl; + std::free(mpData); + } + + String& operator=(const String& right) + { + cout << "String Assignment Operator (" << right.mpData << ")" << endl; + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + + +void print(String s) +{ + cout << s << endl; +} + + +int main() +{ + String a = "Cat"; + print(a); +} + +/* + Задачи: + + 1) Какие методы класса String вызовутся в данной программе. + + + 2) Проверьте ваши догадки скомпилировав и запустив программу + + + 3) Что если мы будем передавать объект String в функцию print не по значению, а по ссылке, то есть изменим + функцию print на следующую: + + void print(const String& s) + { + cout << s << endl; + } + + Какие методы будут вызваны теперь? + +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/15problem_pass_to_funciton_solution.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/15problem_pass_to_funciton_solution.cpp new file mode 100644 index 0000000..3226cfd --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/15problem_pass_to_funciton_solution.cpp @@ -0,0 +1,64 @@ +/* + Решения: + + 1) В случае: + + void print(String s) + { + cout << s << endl; + } + + int main() + { + String a = "Cat"; + print(a); + } + + Вызовутся следующие методы: + + 1) Конструктор строки из "Cat" + + + 2) При передаче объекта в функцию по значению, он должен быть скопирован. + Соответственно при вызове print(a) объект a должен скопироваться в объект s функции print. + Для обычных типов вроде int тут бы произошло побайтовое копирование, но для классов вызывается конструктор копирования. + + + 3) Внутри функции print объект s печатается на экран. Затем мы выходим из функции print и уничтожаем все локальные объекты этой функции. + Соответственно вызовется деструктор для объекта s. + + + 4) Затем мы выходим из функции main и уничтожаем все объекты, локальные для функции main. + Соответственно вызовется деструктор для объекта a. + + + + + 3) В случае: + + void print(const String& s) + { + cout << s << endl; + } + + int main() + { + String a = "Cat"; + print(a); + } + + Вызовутся следующие методы: + + 1) Конструктор строки из "Cat" + + + При передаче объекта в функцию по ссылке, этот объект в функцию не копируется. + Поэтому никаких конструкторов копирования не вызывается. + + + 2) Деструктор для строки a из функии main. + + + Получается, что обычно передавать объекты в функции более эффективно по ссылке, а не по значению. + +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/16problem_pass_to_funciton.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/16problem_pass_to_funciton.cpp new file mode 100644 index 0000000..317e2b2 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/16problem_pass_to_funciton.cpp @@ -0,0 +1,131 @@ +/* + Особые методы при передачах в функцию + + Посмотрим какие особые методы вызываются при передаче объекта в функцию +*/ + +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + cout << "String Constructor from const char* (" << str << ")" << endl; + + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; ++i) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() + { + cout << "String Default Constructor" << endl; + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char)); + mpData[0] = '\0'; + } + + String(const String& s) + { + cout << "String Copy Constructor (" << s.mpData << ")" << endl; + + size_t i = 0; + mSize = s.mSize; + mCapacity = mSize + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + mpData[i] = s.mpData[i]; + mpData[mSize] = '\0'; + } + + ~String() + { + cout << "String Destructor (" << mpData << ")" << endl; + std::free(mpData); + } + + String& operator=(const String& right) + { + cout << "String Assignment Operator (" << right.mpData << ")" << endl; + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + + +void changeFirstLetter(String s) +{ + s[0] = 'B'; +} + + +int main() +{ + String a = "Cat"; + cout << a << endl; + + changeFirstLetter(a); + cout << a << endl; +} + +/* + Задачи: + + 1) Какие методы класса String вызовутся в данной программе. + + 2) Проверьте ваши догадки скомпилировав и запустив программу + + 3) Функция changeFirstLetter должна была менять первую букву нашей строки на букву 'B', но это не происходит и + строка a после вызова changeFirstLetter(a) остаётся неизменной. + Почему это происходит и как изменить функцию changeFirstLetter чтобы она меняла первую букву переданной строки. +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/16problem_pass_to_funciton_solution.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/16problem_pass_to_funciton_solution.cpp new file mode 100644 index 0000000..c689bb1 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/16problem_pass_to_funciton_solution.cpp @@ -0,0 +1,136 @@ +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + cout << "String Constructor from const char* (" << str << ")" << endl; + + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; ++i) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() + { + cout << "String Default Constructor" << endl; + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char)); + mpData[0] = '\0'; + } + + String(const String& s) + { + cout << "String Copy Constructor (" << s.mpData << ")" << endl; + + size_t i = 0; + mSize = s.mSize; + mCapacity = mSize + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + mpData[i] = s.mpData[i]; + mpData[mSize] = '\0'; + } + + ~String() + { + cout << "String Destructor (" << mpData << ")" << endl; + std::free(mpData); + } + + String& operator=(const String& right) + { + cout << "String Assignment Operator (" << right.mpData << ")" << endl; + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + + +void changeFirstLetter(String& s) +{ + s[0] = 'B'; +} + + +int main() +{ + String a = "Cat"; + cout << a << endl; + + changeFirstLetter(a); + cout << a << endl; +} + +/* + Решение: + + В такую функцию: + + void changeFirstLetter(String s) + { + s[0] = 'B'; + } + + строка передавалась по значению и, следовательно, происходило копирование этой строки в объект s функции changeFirstLetter. + Функция changeFirstLetter меняла первую букву копии нашей строки, но оригинальная строка не менялась. + + + Для того, чтобы функция changeFirstLetter меняла оригинальную строку ей нужно передавать объект не по значению, а по ссылке вот так: + + void changeFirstLetter(String& s) + { + s[0] = 'B'; + } + +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/17problem_constructor_calls.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/17problem_constructor_calls.cpp new file mode 100644 index 0000000..63fd799 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/17problem_constructor_calls.cpp @@ -0,0 +1,149 @@ +/* + Различный синтаксис вызова конструкторов +*/ + +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + cout << "String Constructor from const char* (" << str << ")" << endl; + + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; ++i) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() + { + cout << "String Default Constructor" << endl; + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char)); + mpData[0] = '\0'; + } + + String(const String& s) + { + cout << "String Copy Constructor (" << s.mpData << ")" << endl; + + size_t i = 0; + mSize = s.mSize; + mCapacity = mSize + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + mpData[i] = s.mpData[i]; + mpData[mSize] = '\0'; + } + + ~String() + { + cout << "String Destructor (" << mpData << ")" << endl; + std::free(mpData); + } + + String& operator=(const String& right) + { + cout << "String Assignment Operator (" << right.mpData << ")" << endl; + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + + +int main() +{ + String a = "Cat"; + + String b = String("Dog"); + + String c("Mouse"); + + String d = {"Tiger"}; + + String e = String{"Axolotl"}; + + String f {"Lion"}; +} + +/* + Ввиду того, что язык C++ имеет длинную историю, на протяжении которой в язык добавлялись новые возможности, + в языке существует множество способов сделать одно и то же разными способами. + + Один из ярких примеров этого является инициализация объекта. В этом примере создаются 6 строк. Синтаксис различается, + но в данном случае все эти строки по сути делают одно и то же: cоздают объект с помощью конструктора от const char*. + При этом не вызывается никаких конструкторов копирования или операторов присваивания. + + В современном языке C++ предпочтительным способом инициализации является вариант f: + + String f {"Lion"}; + + + + Задачи: + + 1) Что напечатает данная программа? + В каком порядке вызовутся конструкторы и в каком порядке вызовутся деструкторы? + + 2) Скомпилируйте программу и запустите, чтобы проверить ваши догадки. + + 3) Создайте 5 объектов типа String с помощью конструктора по умолчанию, используя разный синтаксис вызова конструктора. + + 4) Пусть есть объект x типа String: + + String x = "Cat"; + + Создайте 6 объектов типа String с помощью конструктора копирования, используя разный синтаксис вызова конструктора. + Все новые объекты должны копировать объект x. + +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/17problem_constructor_calls_solution1.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/17problem_constructor_calls_solution1.cpp new file mode 100644 index 0000000..1880951 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/17problem_constructor_calls_solution1.cpp @@ -0,0 +1,124 @@ +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + cout << "String Constructor from const char* (" << str << ")" << endl; + + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; ++i) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() + { + cout << "String Default Constructor" << endl; + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char)); + mpData[0] = '\0'; + } + + String(const String& s) + { + cout << "String Copy Constructor (" << s.mpData << ")" << endl; + + size_t i = 0; + mSize = s.mSize; + mCapacity = mSize + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + mpData[i] = s.mpData[i]; + mpData[mSize] = '\0'; + } + + ~String() + { + cout << "String Destructor (" << mpData << ")" << endl; + std::free(mpData); + } + + String& operator=(const String& right) + { + cout << "String Assignment Operator (" << right.mpData << ")" << endl; + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + + +int main() +{ + String a; + + String b = String(); + + // String c(); + + String d = {}; + + String e = String{}; + + String f {}; +} + +/* + Аналогично есть множество вариантов синтаксиса вызова конструктора по умолчанию. + + Только такой вариант вызова конструктора не работает: + + String c(); + + Потому что в этом случае компилятор считает, что это объявление функции по имени c, которая ничего не принимает и возвращает объект типа String. + +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/2string/17problem_constructor_calls_solution2.cpp b/seminar02_encapsulation/classroom_tasks/code/2string/17problem_constructor_calls_solution2.cpp new file mode 100644 index 0000000..812c132 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/2string/17problem_constructor_calls_solution2.cpp @@ -0,0 +1,116 @@ +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + cout << "String Constructor from const char* (" << str << ")" << endl; + + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; ++i) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() + { + cout << "String Default Constructor" << endl; + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char)); + mpData[0] = '\0'; + } + + String(const String& s) + { + cout << "String Copy Constructor (" << s.mpData << ")" << endl; + + size_t i = 0; + mSize = s.mSize; + mCapacity = mSize + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + mpData[i] = s.mpData[i]; + mpData[mSize] = '\0'; + } + + ~String() + { + cout << "String Destructor (" << mpData << ")" << endl; + std::free(mpData); + } + + String& operator=(const String& right) + { + cout << "String Assignment Operator (" << right.mpData << ")" << endl; + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + + +int main() +{ + String x = "Cat"; + + + String a = x; + + String b = String(x); + + String c(x); + + String d = {x}; + + String e = String{x}; + + String f {x}; +} \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/3default_methods/00special_methods.cpp b/seminar02_encapsulation/classroom_tasks/code/3default_methods/00special_methods.cpp new file mode 100644 index 0000000..4e2e462 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/3default_methods/00special_methods.cpp @@ -0,0 +1,197 @@ +/* + Особые методы + + В прошлой части мы рассмотрели различные методы классов. Некоторые из этих методов называются особыми. + Они выделяются по сравнению с другими методами тем, что они будут создаваться автоматически даже если вы их не напишите. + К особым методам класса относятся: + + + 1) Конструктор по умолчанию. То есть конструктор, который не принимает ни одного аргумента. + В случае со строкой String это контруктор: + + String() + + + 2) Конструктор копирования. То есть конструктор, который создаёт объект из другого объекта такого же типа, что и данный. + В случае со строкой String это контруктор: + + String(const String& s) + + + 3) Деструктор. Это метод, который вызывается при удалении объекта. В отличии от конструкторов, деструктор у объекта всегда один. + В случае со строкой String это: + + ~String() + + + 4) Оператор присваивания. Это метод, который вызывается при присваивании одного метода класса String другому методу класса String. + В случае со строкой String это: + + String& operator=(const String& right) + + + + 5,6) Есть ещё 2 особых метода, которые мы пройдём позже. + + + Все остальные методы, включаю другие конструкторы и перегруженные операторы, к особым методам не относятся. + То есть остальные методы не могут создаваться автоматически. + + + В этой части мы рассмотрим при каких условиях происходит вызов того или иного особого метода. + Для этого будем использовать класс String, в котором была добавлена печать на экран для каждого особого метода и для коструктора из const char*. + Например, конструктор по умолчанию будет печатать Default Constructor и т. д. +*/ + +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + cout << "String Constructor from const char* (" << str << ")" << endl; + + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; ++i) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() + { + cout << "String Default Constructor" << endl; + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char)); + mpData[0] = '\0'; + } + + String(const String& s) + { + cout << "String Copy Constructor (" << s.mpData << ")" << endl; + + size_t i = 0; + mSize = s.mSize; + mCapacity = mSize + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + mpData[i] = s.mpData[i]; + mpData[mSize] = '\0'; + } + + ~String() + { + cout << "String Destructor (" << mpData << ")" << endl; + std::free(mpData); + } + + String& operator=(const String& right) + { + cout << "String Assignment Operator (" << right.mpData << ")" << endl; + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + + +class Book +{ +public: + String title; + int pages; + float price; +}; + + +int main() +{ + Book a = Book(); + cout << a.title << " " << a.pages << " " << a.price << endl; +} + + + +/* + У класса Book есть 3 поля, одно из которых имеет тип String. + Но у класса Book не определён ни один метод. У него нет никаких конструкторов или перегруженных операторов. + + Теперь посмотрим на код: + + Book a = Book(); + + Тут мы создаём объект типа Book с помощью конструктора по умолчанию. Но у Book не написан конструктор по умолчанию! + Что же произойдёт в этом случае? Ошибка? Нет, на самом деле в этом случае конструктор будет создан автоматически. Вот такой: + + Book() {} + + Автоматически-сгенерированный конструктор по умолчанию ничего не делает. + Тем не менее, перед вызовом этого конструктора, компилятор должен вызвать конструктор по умолчанию для всех полей класса, + у которых есть конструктор по умолчанию. + В этом случае, при создании объекта класса Book с помощью конструктора по умолчанию, будет вызван конструктор по умолчанию класса String. + + + + Аналогично, автоматически сгенерируется деструктор по умолчанию, вот такой: + + ~Book() {} + + Он пустой, но нужно помнить, что после вызова деструктора класса, автоматически вызываются деструкторы для все его полей (у которых есть деструкторы) + В этом случае, после вызова деструктора класса Book вызовется деструктор класса String. + + + + + + Задачи: + + 1) Скомпилируйте программу, запустите и убедитесь, что в этом примере вызываются + конструктор по умолчанию и деструктор класса String. +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/3default_methods/01special_methods.cpp b/seminar02_encapsulation/classroom_tasks/code/3default_methods/01special_methods.cpp new file mode 100644 index 0000000..820ab32 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/3default_methods/01special_methods.cpp @@ -0,0 +1,191 @@ +/* + Особые методы + + Проверим, что компилятор автоматически генерирует конструктор копирования и оператор присваивания. +*/ + +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + cout << "String Constructor from const char* (" << str << ")" << endl; + + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; ++i) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() + { + cout << "String Default Constructor" << endl; + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char)); + mpData[0] = '\0'; + } + + String(const String& s) + { + cout << "String Copy Constructor (" << s.mpData << ")" << endl; + + size_t i = 0; + mSize = s.mSize; + mCapacity = mSize + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + mpData[i] = s.mpData[i]; + mpData[mSize] = '\0'; + } + + ~String() + { + cout << "String Destructor (" << mpData << ")" << endl; + std::free(mpData); + } + + String& operator=(const String& right) + { + cout << "String Assignment Operator (" << right.mpData << ")" << endl; + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + + +class Book +{ +public: + String title; + int pages; + float price; +}; + +void print(Book x) +{ + cout << x.title << " " << x.pages << " " << x.price << endl; +} + +int main() +{ + Book a; + + a.title = "War and Peace"; + a.pages = 1000; + a.price = 1100; + + Book b; + b = a; + print(b); +} + + + +/* + Что напечатает данная программа? + + Разберём код из функции main подробно: + + 1) Вызывается автоматически сгенерированный конструктор по умолчанию в строке: + + Book a; + + При этом перед вызовом этого конструктора вызовется и конструктор класса String поля title. + + + + 2) Полям объекта a присваиваются некоторые значения: + + a.title = "War and Peace"; + a.pages = 1000; + a.price = 1100; + + Интересно отметить, что строка a.title = "War and Peace" работает, несмотря на то, что слева от знака + присваивания стоит объект типа String, а справа от знака присваивания стоит объект типа const char[14]. + Типы не совпадают, но это работает, так как у класса String есть конструктор от const char*. + Таким образом, сначала вызовется этот конструктор и создастся временный объект типа String, а потом + вызовется оператор присваивания между двумя объектами класса String. + После этого временный объект удалится и, соответственно, вызовется его деструктор. + + + + 3) Вызывается автоматически сгенерированный конструктор по умолчанию в строке: + + Book b; + + + + 4) Вызывается автоматически сгенерированный перегруженный оператор присваивания в строке: + + b = a; + + Автоматически сгенерированный оператор присваивания применяет оператор присваивания для каждого поля. + + + + 5) Передача в функцию print осуществляется по значению. Следовательно в строке + + print(b); + + объект b должен быть скопирован внутрь функции. Для этого должен быть вызван конструктор копирования. + Конструктор копирования у класса Book не написан, поэтому компилятор автоматически его сгенерирует. + + Автоматически сгенерированный конструктор копирования копирует каждое поле в соответствующее поле нового объекта. + Если у поля есть конструктор копирования (как например у класса String), то вызывается конструктор копирования этого поля. + Если у поля нет конструктора копирования (как например у int или float), то поле просто копируется побайтово. + + + 6) Выходим из функции print и вызываем деструктор для объекта x. + + 7) Выходим из функции main и вызываем деструкторы для объектов a и b. +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/3default_methods/02default_constructor_remark.cpp b/seminar02_encapsulation/classroom_tasks/code/3default_methods/02default_constructor_remark.cpp new file mode 100644 index 0000000..ca9eb59 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/3default_methods/02default_constructor_remark.cpp @@ -0,0 +1,148 @@ +/* + Замечание по поводу автоматической генерации конструктора по умолчанию +*/ + +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + cout << "String Constructor from const char* (" << str << ")" << endl; + + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; ++i) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() + { + cout << "String Default Constructor" << endl; + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char)); + mpData[0] = '\0'; + } + + String(const String& s) + { + cout << "String Copy Constructor (" << s.mpData << ")" << endl; + + size_t i = 0; + mSize = s.mSize; + mCapacity = mSize + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + mpData[i] = s.mpData[i]; + mpData[mSize] = '\0'; + } + + ~String() + { + cout << "String Destructor (" << mpData << ")" << endl; + std::free(mpData); + } + + String& operator=(const String& right) + { + cout << "String Assignment Operator (" << right.mpData << ")" << endl; + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + + +class Book +{ +public: + String title; + int pages; + float price; + + Book(const String& aTitle, int aPages, float aPrice) + { + title = aTitle; + pages = aPages; + price = aPrice; + } +}; + + +int main() +{ + Book a; +} + + + +/* + Конструктор по умолчанию не генерируется автоматически если у класса написан хотя бы один конструктор (любой). + Например, в этом пример у класса Book написан один конструктор: + + Book(const String& aTitle, int aPages, float aPrice) + + Поэтому в данном случае конструктор по умочанию автоматически генерироваться не будет. + + + + В строке Book a; должен быть вызван конструктор по умолчанию. + Но у класса Book такого конструктора нет и автоматически он не был создан. Поэтому эта строка приведёт к ошибке. + + + + Но конструктор копирования, оператор присваивания и деструктор автоматически генерироваться будут. + + + Задача: + + Исправьте ошибку, написав конструктор по умолчанию самостоятельно. +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/3default_methods/03delete_keyword.cpp b/seminar02_encapsulation/classroom_tasks/code/3default_methods/03delete_keyword.cpp new file mode 100644 index 0000000..03916df --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/3default_methods/03delete_keyword.cpp @@ -0,0 +1,164 @@ +/* + Удалённые методы +*/ + +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + cout << "String Constructor from const char* (" << str << ")" << endl; + + size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; str[i]; ++i) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() + { + cout << "String Default Constructor" << endl; + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char)); + mpData[0] = '\0'; + } + + String(const String& s) + { + cout << "String Copy Constructor (" << s.mpData << ")" << endl; + + size_t i = 0; + mSize = s.mSize; + mCapacity = mSize + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + mpData[i] = s.mpData[i]; + mpData[mSize] = '\0'; + } + + ~String() + { + cout << "String Destructor (" << mpData << ")" << endl; + std::free(mpData); + } + + String& operator=(const String& right) + { + cout << "String Assignment Operator (" << right.mpData << ")" << endl; + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + + +class Book +{ +public: + String title; + int pages; + float price; + + Book() {}; + + Book(const String& aTitle, int aPages, float aPrice) + { + title = aTitle; + pages = aPages; + price = aPrice; + } + + Book(const Book& b) = delete; +}; + + +int main() +{ + Book a; + + Book b = a; +} + + + +/* + Если же вы не хотите создавать какой-либо метод и не хотите чтобы он создавался автоматически, + то его можно удалить с помощью ключевого слова delete. + + + Например, в этом примере у класса Book удалён конструктор копирования вот так: + + Book(const Book& b) = delete; + + Это означает, что конструктор копирования не создастся автоматически. + Поэтому в строке: + + Book b = a; + + произойдёт ошибка компиляции. + + + + Задача: + + 1) Можно ли передать объект класса с удалённым конструктором копирования в функцию по значению? + + Например, если есть функция: + + void print(Book b) + { + cout << b.title << " "<< b.pages << " " << b.price << endl; + } + + Можно ли туда передать что-нибудь? + +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/3default_methods/04implicit_cast_constructor.cpp b/seminar02_encapsulation/classroom_tasks/code/3default_methods/04implicit_cast_constructor.cpp new file mode 100644 index 0000000..7d42352 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/3default_methods/04implicit_cast_constructor.cpp @@ -0,0 +1,188 @@ +/* + Неявное приведение типа с помощью конструктора от одного параметра. +*/ + +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + String(const char* s) + { + cout << "String Constructor from const char* (" << s << ")" << endl; + + size_t i = 0; + while (s[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; s[i]; ++i) + mpData[i] = s[i]; + mpData[mSize] = '\0'; + } + + String() + { + cout << "String Default Constructor" << endl; + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char)); + mpData[0] = '\0'; + } + + String(const String& s) + { + cout << "String Copy Constructor (" << s.mpData << ")" << endl; + + size_t i = 0; + mSize = s.mSize; + mCapacity = mSize + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + mpData[i] = s.mpData[i]; + mpData[mSize] = '\0'; + } + + ~String() + { + cout << "String Destructor (" << mpData << ")" << endl; + std::free(mpData); + } + + String& operator=(const String& right) + { + cout << "String Assignment Operator (" << right.mpData << ")" << endl; + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + + +void print(String s) +{ + cout << s << endl; +} + + +int main() +{ + String a; + a = "Dog"; + + print("Mouse"); +} + + + +/* + Одна из скрытых вещей, где используются конструкторы, это для неявного приведения типов. + + Рассмотрим следующие строки: + + String a; + a = "Dog"; + + Во второй строке должен вызваться оператор присваивания. + Слева от оператора присваивания стоит объект типа String, а справа от оператора присваивания стоит объект типа const char[4]. + Поэтому должен вызваться метод operator=(const char* s) класса String. (при передаче в функцию массив автоматически конвертируется в указатель) + Но такого метода в классе String нет. Что же тогда будет сделано? + + В этом случае будет сделано следующее: + + 1) Будет создан временный объект типа String с использованием конструтора String(const char* s). + 2) Будет вызван оператор присваивания operator=(const String& s). Объекту a присвоится временный объект. + 3) Временный объект будет уничтожен, при этом вызовется деструктор класса String. + + + + + Рассмотрим строку: + + print("Mouse"); + + Функция print должна принимать объект типа String, но на вход ей приходит объект типа const char[6]. + Что будет сделано в этом случае? + И в этом случае всё сработает, так как объект s функции print будет создан с помощью конструктора класса String от const char*. + + + + Таким образом конструктор от одного аргумента автоматически используется для неявного приведения одного типа в другой. + + + + Задачи: + + 1) Что если мы напишем конструктор класса String от числа типа int. + Этот конструктор будет принимать число n и создавать строку, состоящую из n символов 'a' + + + String(int n) + { + cout << "String Constructor from int (" << n << ")" << endl; + + mSize = n; + mCapacity = mSize + 1; + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + for (size_t i = 0; i < mSize; ++i) + mpData[i] = 'a'; + mpData[mSize] = '\0'; + } + + + + Будет ли этот конструктор использоваться для неявного приведения чисел типа int в строки типа String? + Например, будет ли работать следующий код: + + String b; + b = 5; + + (b будет строкой состоящей из 5 символов 'a' ?) + + + print(10); + + (напечатает строку, состоящую из 10 символов 'a') +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/3default_methods/05explicit.cpp b/seminar02_encapsulation/classroom_tasks/code/3default_methods/05explicit.cpp new file mode 100644 index 0000000..ff73301 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/3default_methods/05explicit.cpp @@ -0,0 +1,147 @@ +/* + Ключевое слово explicit. +*/ + +#include +#include +using std::cout, std::endl, std::size_t; + + +class String +{ +private: + + size_t mSize; + size_t mCapacity; + char* mpData; + +public: + + explicit String(const char* s) + { + cout << "String Constructor from const char* (" << s << ")" << endl; + + size_t i = 0; + while (s[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; s[i]; ++i) + mpData[i] = s[i]; + mpData[mSize] = '\0'; + } + + explicit String(int n) + { + cout << "String Constructor from int (" << n << ")" << endl; + + mSize = n; + mCapacity = mSize + 1; + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + for (size_t i = 0; i < mSize; ++i) + mpData[i] = 'a'; + mpData[mSize] = '\0'; + } + + String() + { + cout << "String Default Constructor" << endl; + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char)); + mpData[0] = '\0'; + } + + String(const String& s) + { + cout << "String Copy Constructor (" << s.mpData << ")" << endl; + + size_t i = 0; + mSize = s.mSize; + mCapacity = mSize + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i < mSize; ++i) + mpData[i] = s.mpData[i]; + mpData[mSize] = '\0'; + } + + ~String() + { + cout << "String Destructor (" << mpData << ")" << endl; + std::free(mpData); + } + + String& operator=(const String& right) + { + cout << "String Assignment Operator (" << right.mpData << ")" << endl; + if (this == &right) + return *this; + + + mSize = right.mSize; + mCapacity = right.mCapacity; + + std::free(mpData); + mpData = (char*)malloc(sizeof(char) * mCapacity); + + for (size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + char& operator[](size_t i) + { + return mpData[i]; + } + + size_t getSize() const {return mSize;} + size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + +std::ostream& operator<<(std::ostream& left, const String& right) +{ + left << right.cStr(); + return left; +} + + + +void print(String s) +{ + cout << s << endl; +} + + +int main() +{ + String a; + a = "Dog"; + + print("Mouse"); + print(10); +} + + + +/* + Иногда всё-таки не хочется, чтобы конструкторы использовались для неявного приведения типов. + Ведь такое приведение типов может произойти там, где мы этого не хотим. + + Чтобы конструктор не использовался для неявного приведения, его нужно пометить с помощью ключевого слова explicit. + В этом примере, конструкторы String(const char* s) и String(int n) помечены как explicit. + Поэтому эти конструкторы не будут использоваться для неявного приведения типов и код в функции main выдаст ошибку. + Но эти конструкторы всё равно можно вызывать явно, вот так: + + String a; + a = String("Dog"); + + print(String("Mouse")); + print(String(10)); +*/ \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/00point/main.cpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/00point/main.cpp new file mode 100644 index 0000000..9921eee --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/00point/main.cpp @@ -0,0 +1,62 @@ +/* + Раздельная компиляция. + + В этой части мы вынесем весь код, связанный с нашим классом Point в отдельные файлы. + А также скомпилируем отдельно код, относящийся к нашему классу Point. + + Это будет проделано в следующий примерах, а пока тут просто лежит код + класса Point из предыдущих частей. +*/ + + +#include +#include +#include +using std::cout, std::endl; + + +struct Point +{ + float x, y; + + Point operator+(Point b) const + { + Point result = {x + b.x, y + b.y}; + return result; + } + + float norm() const + { + return std::sqrt(x * x + y * y); + } + + Point operator*(float k) const + { + Point result = {k * x, k * y}; + return result; + } + + void normalize() + { + float normv = norm(); + x /= normv; + y /= normv; + } +}; + +std::ostream& operator<<(std::ostream& out, Point a) +{ + out << std::setprecision(2) << "(" << a.x << ", " << a.y << ")"; + return out; +} + + +int main() +{ + Point a = {7.2, 3.1}; + cout << a.norm() << endl; +} + + + + diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/01point_separate_methods/main.cpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/01point_separate_methods/main.cpp new file mode 100644 index 0000000..d55993e --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/01point_separate_methods/main.cpp @@ -0,0 +1,81 @@ +/* + Вынос определений методов вне класса. + + Методы внутри класса можно только объявить, а определить их можно вне класса. + Например, метод norm объявлен внутри класса: + + float norm() const; + + а определён вне класса: + + float Point::norm() const + { + return std::sqrt(x * x + y * y); + } + + + Чтобы компилятор понимал к какому классу относится тот или иной метод, + к его имени вне класса нужно добавить название класса и два двоеточия. + Поэтому метод norm вне класса Point называется как Point::norm. + +*/ + + +#include +#include +#include +using std::cout, std::endl; + + +struct Point +{ + float x, y; + + Point operator+(Point b) const; + float norm() const; + Point operator*(float k) const; + void normalize(); +}; + + +Point Point::operator+(Point b) const +{ + Point result = {x + b.x, y + b.y}; + return result; +} + +float Point::norm() const +{ + return std::sqrt(x * x + y * y); +} + +Point Point::operator*(float k) const +{ + Point result = {k * x, k * y}; + return result; +} + +void Point::normalize() +{ + float normv = norm(); + x /= normv; + y /= normv; +} + + +std::ostream& operator<<(std::ostream& out, Point a) +{ + out << std::setprecision(2) << "(" << a.x << ", " << a.y << ")"; + return out; +} + + +int main() +{ + Point a = {7.2, 3.1}; + cout << a.norm() << endl; +} + + + + diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/02point_headers/main.cpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/02point_headers/main.cpp new file mode 100644 index 0000000..1becf58 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/02point_headers/main.cpp @@ -0,0 +1,61 @@ +/* + Перенос кода в заголовочный файл + + В этом примере мы перенесли весь код, связанный с классом Point в отдельный файл point.hpp. + Файл point.hpp подключается к файлу main.cpp с помощью директивы #include в строке: + + #include "point.hpp" + + На самом деле директива #include делает очень простую вещь: она просто берёт всё содержимое + передаваемого ей файла и вставляет это содержимое на место директивы. + То есть в этом примере за место #include "point.hpp" подставится всё содержимое файла point.hpp. + + Так как директива #include уже сама вставила файл point.hpp в файл main.cpp, то дополнительно + указывать файл point.hpp при компиляции не нужно. Скомпилировать этот пример можно так: + + g++ main.cpp + + + Стандартные библиотеки типа iostream подключаются так же. То есть где-то в системе, в какой-то папке + хранится стандартная библиотека C++ и, соответственно, есть файл под название iostream (без расширения). + Этот файл и подставляется за место строки #include . + Чтобы посмотреть в каких папках компилятор ищет файлы стандартной библиотеки можно скомпилировать программу так: + + g++ -v main.cpp + + Он напечатает множество информации о компиляции, в числе прочего пути где происходит поиск при исполнении #include. + Вы могли заметить, что стандартные библиотеки в директиве #include пишутся в треугольных скобочках, а нашу + библиотеку point.hpp мы написали в двойных кавычках. В чём разница между этими вариантами? + На самом деле разница лишь в том, в каких папках компилятор ищет данные файлы. + Если мы пишем библиотеку в треугольных скобках, то компилятор ищет её в папках стандартной библиотеки. + Если мы пишем библиотеку в кавычках, то компилятор ищет её в папках текущей директории. + + + + Защита от двойного включения + + Используя директивы #include, очень легко включить один и тот же файл 2 и более раз. + Например, в этом примере файл iostream включается 2 раза: один раз непосредственно в файле main.cpp, а второй раз + он включится после того, как включиться файл point.hpp. Внутри файла point.hpp тоже есть #include . + Если не предпринимать никаких действий, то произойдёт ошибка, так как одинаковые функции и классы + будут повторяться в программе несколько раз. + + Чтобы защититься от этой проблемы нужно в начале заголовочного файла написать директиву + + #pragma once + + Таким образом компилятор будет понимать, что вы не хотите включать файл более одного раза и включит только одну копию файла. + Эта директива была использовани и в файле point.hpp. +*/ + + +#include +#include "point.hpp" + +using std::cout, std::endl; + +int main() +{ + Point a = {7.2, 3.1}; + cout << a.norm() << endl; +} \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/02point_headers/point.hpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/02point_headers/point.hpp new file mode 100644 index 0000000..f92184c --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/02point_headers/point.hpp @@ -0,0 +1,39 @@ +#pragma once +#include +#include +#include + +struct Point +{ + float x, y; + + Point operator+(Point b) const + { + Point result = {x + b.x, y + b.y}; + return result; + } + + float norm() const + { + return std::sqrt(x * x + y * y); + } + + Point operator*(float k) const + { + Point result = {k * x, k * y}; + return result; + } + + void normalize() + { + float normv = norm(); + x /= normv; + y /= normv; + } +}; + +std::ostream& operator<<(std::ostream& out, Point a) +{ + out << std::setprecision(2) << "(" << a.x << ", " << a.y << ")"; + return out; +} \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/03point_headers/main.cpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/03point_headers/main.cpp new file mode 100644 index 0000000..3d5c875 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/03point_headers/main.cpp @@ -0,0 +1,17 @@ +/* + Всё что относится к классу Point мы перенесли в отдельный файл point.hpp. + А также мы разделили объявления и определения методов в файле point.hpp. +*/ + +#include +#include +#include +#include "point.hpp" + +using std::cout, std::endl; + +int main() +{ + Point a = {7.2, 3.1}; + cout << a.norm() << endl; +} \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/03point_headers/point.hpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/03point_headers/point.hpp new file mode 100644 index 0000000..e60bfbe --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/03point_headers/point.hpp @@ -0,0 +1,45 @@ +#pragma once +#include + + +struct Point +{ + float x, y; + + Point operator+(Point b) const; + float norm() const; + Point operator*(float k) const; + void normalize(); +}; + + +Point Point::operator+(Point b) const +{ + Point result = {x + b.x, y + b.y}; + return result; +} + +float Point::norm() const +{ + return std::sqrt(x * x + y * y); +} + +Point Point::operator*(float k) const +{ + Point result = {k * x, k * y}; + return result; +} + +void Point::normalize() +{ + float normv = norm(); + x /= normv; + y /= normv; +} + + +std::ostream& operator<<(std::ostream& out, Point a) +{ + out << std::setprecision(2) << "(" << a.x << ", " << a.y << ")"; + return out; +} diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/04point_separate_compilation/main.cpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/04point_separate_compilation/main.cpp new file mode 100644 index 0000000..926aecf --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/04point_separate_compilation/main.cpp @@ -0,0 +1,40 @@ +/* + Раздельная компиляция + + Теперь мы разделим объявления и определения всех методов класса Point. + Все объявления класса Point поместим в файл point.hpp, а определения методов в файл point.cpp. + Таким образом у нас получилось 2 компилируемых файла: main.cpp и point.cpp и 1 заголовочный файл point.hpp. + + При этом заголовочный файл point.hpp должен подключаться везде, где используется класс Point. + То есть в данном случае он должен подключаться и в файл main.cpp и в файл point.cpp. + + + Для компиляции программы нужно указать компилятору на все компилируемые файлы вот так: + + g++ main.cpp point.cpp + + Если это не сделать и скомпилировать только файл main.cpp, то возникнет ошибка undefined reference to norm(). + Грубо говоря программа будет знать, что есть класс Point и знать его методы (так как это всё описано в point.hpp), + но любые попытки вызвать эти методы будут приводить к ошибке, так как они не были скомпилированы. + + + Преемущество раздельной компиляции заключается в том, что компилировать разные .cpp файлы можно поотдельности. + Представьте, что у вас огромный проект, содержащий тысячи файлов и миллионы строк кода. Такой проект может компилироваться часами. + Теперь вы сделали небольшое изменение в одном файле этого проекта. Если бы нельзя было бы компилировать файлы поотдельности, + то после любого изменения вам бы пришлось компилировать весь проект заново и ждать часы пока компиляция закончится. + К счастью, .cpp файлы можно компилировать поотдельности и, если вы сделали изменение в одном файле, то скомпилировать + потребуется только тот файл, который вы изменили. +*/ + + + +#include +#include "point.hpp" + +using std::cout, std::endl; + +int main() +{ + Point a = {7.2, 3.1}; + cout << a.norm() << endl; +} \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/04point_separate_compilation/point.cpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/04point_separate_compilation/point.cpp new file mode 100644 index 0000000..143dd83 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/04point_separate_compilation/point.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include "point.hpp" + +Point Point::operator+(Point b) const +{ + Point result = {x + b.x, y + b.y}; + return result; +} + +float Point::norm() const +{ + return std::sqrt(x * x + y * y); +} + +Point Point::operator*(float k) const +{ + Point result = {k * x, k * y}; + return result; +} + +void Point::normalize() +{ + float normv = norm(); + x /= normv; + y /= normv; +} + + +std::ostream& operator<<(std::ostream& out, Point a) +{ + out << std::setprecision(2) << "(" << a.x << ", " << a.y << ")"; + return out; +} diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/04point_separate_compilation/point.hpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/04point_separate_compilation/point.hpp new file mode 100644 index 0000000..1e48146 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/04point_separate_compilation/point.hpp @@ -0,0 +1,15 @@ +#pragma once +#include + + +struct Point +{ + float x, y; + + Point operator+(Point b) const; + float norm() const; + Point operator*(float k) const; + void normalize(); +}; + +std::ostream& operator<<(std::ostream& out, Point a); diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/05point_namespace/main.cpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/05point_namespace/main.cpp new file mode 100644 index 0000000..d248d06 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/05point_namespace/main.cpp @@ -0,0 +1,30 @@ +/* + Пространства имён + + + Имя Point достаточно распространённое и есть очень большая вероятность того, что при подключении нескольких библиотек + в некоторых из них тоже будет класс с именем Point. Конечно, если ничего не предпринять, это приведёт к ошибке. + + Чтобы избежать ошибок, связанных с конфликтами имён, положим весь код нашего класса Point в пространство имён mipt. + Для этого обернём наш код в файлах point.hpp и point.cpp + + namespace mipt + { + ... + } + + Теперь наш класс Point будет лежать в пространстве имён mipt. + Для его использования вне пространства имён mipt нужно писать mipt::Point (ну либо использовать using mipt::Point;) +*/ + + +#include +#include "point.hpp" + +using std::cout, std::endl; + +int main() +{ + mipt::Point a = {7.2, 3.1}; + cout << a.norm() << endl; +} \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/05point_namespace/point.cpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/05point_namespace/point.cpp new file mode 100644 index 0000000..7a3ade1 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/05point_namespace/point.cpp @@ -0,0 +1,40 @@ +#include +#include +#include +#include "point.hpp" + +namespace mipt +{ + +Point Point::operator+(Point b) const +{ + Point result = {x + b.x, y + b.y}; + return result; +} + +float Point::norm() const +{ + return std::sqrt(x * x + y * y); +} + +Point Point::operator*(float k) const +{ + Point result = {k * x, k * y}; + return result; +} + +void Point::normalize() +{ + float normv = norm(); + x /= normv; + y /= normv; +} + + +std::ostream& operator<<(std::ostream& out, Point a) +{ + out << std::setprecision(2) << "(" << a.x << ", " << a.y << ")"; + return out; +} + +} \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/05point_namespace/point.hpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/05point_namespace/point.hpp new file mode 100644 index 0000000..4f0944a --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/05point_namespace/point.hpp @@ -0,0 +1,20 @@ +#pragma once +#include + + +namespace mipt +{ + +struct Point +{ + float x, y; + + Point operator+(Point b) const; + float norm() const; + Point operator*(float k) const; + void normalize(); +}; + +std::ostream& operator<<(std::ostream& out, Point a); + +} diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string/main.cpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string/main.cpp new file mode 100644 index 0000000..414a000 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string/main.cpp @@ -0,0 +1,265 @@ +/* + В данной программе содержится класс String, написанный нами в одной из предыдущих частей. + + Задачи: + + 1) Вынесите класс String в отдельный заголовочный файл string.hpp и скомпилируйте программу. + + + 2) Вынесите объявления методов класса String в заголовочный файл string.hpp, а определения + методов в компилируемый файл string.cpp. Скомпилируйте программу. + + + 3) Вынесите объявления методов класса String в заголовочный файл string.hpp, а определения + методов в компилируемый файл string.cpp. А также поместите весь код класса String в пространство имён mipt. + Скомпилируйте программу. +*/ + + +#include +#include + + +class String +{ +private: + + std::size_t mSize; + std::size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + std::size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (std::size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + String(std::size_t n, char a) + { + mSize = n; + mCapacity = n + 1; + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (std::size_t i = 0; i < mSize; ++i) + mpData[i] = a; + mpData[mSize] = '\0'; + } + + ~String() + { + std::free(mpData); + } + + void reserve(std::size_t capacity) + { + if (capacity <= mCapacity) + return; + + mCapacity = capacity; + char* newData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (std::size_t i = 0; i < mSize; ++i) + newData[i] = mpData[i]; + newData[mSize] = '\0'; + + std::free(mpData); + mpData = newData; + } + + + void resize(std::size_t size) + { + reserve(size + 1); + mSize = size; + mpData[mSize] = '\0'; + } + + + String& operator=(const String& right) + { + if (this == &right) + return *this; + + mSize = right.mSize; + resize(mSize); + + for (std::size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + + String operator+(const String& b) + { + String result; + result.resize(mSize + b.mSize); + + for (std::size_t i = 0; i < mSize; ++i) + result.mpData[i] = mpData[i]; + + for (std::size_t i = 0; i < b.mSize; ++i) + result.mpData[mSize + i] = b.mpData[i]; + + result.mpData[result.mSize] = '\0'; + + return result; + } + + String& operator+=(const String& right) + { + *this = *this + right; + return *this; + } + + bool operator==(const String& right) const + { + if (mSize != right.mSize) + return false; + + std::size_t i = 0; + while (i < mSize && mpData[i] == right.mpData[i]) + i++; + + return i == mSize; + } + + bool operator<(const String& right) const + { + std::size_t i = 0; + while (i < mSize && i < right.mSize && mpData[i] == right.mpData[i]) + i++; + + return mpData[i] < right.mpData[i]; + } + + bool operator<=(const String& right) const + { + std::size_t i = 0; + while (i < mSize && i < right.mSize && mpData[i] == right.mpData[i]) + i++; + + return mpData[i] <= right.mpData[i]; + } + + bool operator!=(const String& right) const + { + return !(*this == right); + } + + bool operator>(const String& right) const + { + return !(*this <= right); + } + + bool operator>=(const String& right) const + { + return !(*this < right); + } + + char& operator[](std::size_t i) + { + return mpData[i]; + } + + char& at(std::size_t i) + { + if (i >= mSize) + { + std::cout << "Error! Index is out of bounds." << std::endl; + std::exit(1); + } + return mpData[i]; + } + + void clear() + { + std::free(mpData); + + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + mpData[0] = '\0'; + } + + void addCharacter(char c) + { + if (mSize + 1 == mCapacity) + reserve(2 * mCapacity); + + mpData[mSize] = c; + resize(mSize + 1); + } + + + std::size_t getSize() const {return mSize;} + std::size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& out, const String& s) +{ + out << s.cStr(); + return out; +} + +std::istream& operator>>(std::istream& in, String& s) +{ + s.clear(); + while (true) + { + char x = in.get(); + if (x == ' ' || x == '\n' || x == '\t') + break; + s.addCharacter(x); + } + return in; +} + + +int main() +{ + String a = "Mouse"; + String b; + std::cin >> b; + String c = b; + + if (a + c == "MouseLion") + std::cout << "Yes" << std::endl; + else + std::cout << "No" << std::endl; + + + if (a > "Mice") + std::cout << "Yes" << std::endl; + else + std::cout << "No" << std::endl; + + + c[0] = 'P'; + std::cout << c << std::endl; + + c += a; + std::cout << c << std::endl; + + c = c + String(10, 'q'); + std::cout << c << std::endl; +} + + + diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution1/main.cpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution1/main.cpp new file mode 100644 index 0000000..3a98561 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution1/main.cpp @@ -0,0 +1,37 @@ +#include +#include +#include "string.hpp" + +using std::cout, std::cin, std::endl; + +int main() +{ + String a = "Mouse"; + String b; + cin >> b; + String c = b; + + if (a + c == "MouseLion") + cout << "Yes" << endl; + else + cout << "No" << endl; + + + if (a > "Mice") + cout << "Yes" << endl; + else + cout << "No" << endl; + + + c[0] = 'P'; + cout << c << endl; + + c += a; + cout << c << endl; + + c = c + String(10, 'q'); + cout << c << endl; +} + + + diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution1/string.hpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution1/string.hpp new file mode 100644 index 0000000..c082dd6 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution1/string.hpp @@ -0,0 +1,214 @@ +#include +#include + + +class String +{ +private: + + std::size_t mSize; + std::size_t mCapacity; + char* mpData; + +public: + + String(const char* str) + { + std::size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (std::size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; + } + + String() : String("") {} + String(const String& s) : String(s.cStr()) {} + + String(std::size_t n, char a) + { + mSize = n; + mCapacity = n + 1; + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (std::size_t i = 0; i < mSize; ++i) + mpData[i] = a; + mpData[mSize] = '\0'; + } + + ~String() + { + std::free(mpData); + } + + void reserve(std::size_t capacity) + { + if (capacity <= mCapacity) + return; + + mCapacity = capacity; + char* newData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (std::size_t i = 0; i < mSize; ++i) + newData[i] = mpData[i]; + newData[mSize] = '\0'; + + std::free(mpData); + mpData = newData; + } + + + void resize(std::size_t size) + { + reserve(size + 1); + mSize = size; + mpData[mSize] = '\0'; + } + + + String& operator=(const String& right) + { + if (this == &right) + return *this; + + mSize = right.mSize; + resize(mSize); + + for (std::size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; + } + + + String operator+(const String& b) + { + String result; + result.resize(mSize + b.mSize); + + for (std::size_t i = 0; i < mSize; ++i) + result.mpData[i] = mpData[i]; + + for (std::size_t i = 0; i < b.mSize; ++i) + result.mpData[mSize + i] = b.mpData[i]; + + result.mpData[result.mSize] = '\0'; + + return result; + } + + String& operator+=(const String& right) + { + *this = *this + right; + return *this; + } + + bool operator==(const String& right) const + { + if (mSize != right.mSize) + return false; + + std::size_t i = 0; + while (i < mSize && mpData[i] == right.mpData[i]) + i++; + + return i == mSize; + } + + bool operator<(const String& right) const + { + std::size_t i = 0; + while (i < mSize && i < right.mSize && mpData[i] == right.mpData[i]) + i++; + + return mpData[i] < right.mpData[i]; + } + + bool operator<=(const String& right) const + { + std::size_t i = 0; + while (i < mSize && i < right.mSize && mpData[i] == right.mpData[i]) + i++; + + return mpData[i] <= right.mpData[i]; + } + + bool operator!=(const String& right) const + { + return !(*this == right); + } + + bool operator>(const String& right) const + { + return !(*this <= right); + } + + bool operator>=(const String& right) const + { + return !(*this < right); + } + + char& operator[](std::size_t i) + { + return mpData[i]; + } + + char& at(std::size_t i) + { + if (i >= mSize) + { + std::cout << "Error! Index is out of bounds." << std::endl; + std::exit(1); + } + return mpData[i]; + } + + void clear() + { + std::free(mpData); + + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + mpData[0] = '\0'; + } + + void addCharacter(char c) + { + if (mSize + 1 == mCapacity) + reserve(2 * mCapacity); + + mpData[mSize] = c; + resize(mSize + 1); + } + + + std::size_t getSize() const {return mSize;} + std::size_t getCapacity() const {return mCapacity;} + const char* cStr() const {return mpData;} +}; + + +std::ostream& operator<<(std::ostream& out, const String& s) +{ + out << s.cStr(); + return out; +} + +std::istream& operator>>(std::istream& in, String& s) +{ + s.clear(); + while (true) + { + char x = in.get(); + if (x == ' ' || x == '\n' || x == '\t') + break; + s.addCharacter(x); + } + return in; +} \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution2/main.cpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution2/main.cpp new file mode 100644 index 0000000..19996c2 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution2/main.cpp @@ -0,0 +1,39 @@ +/* + Компиляция: + + g++ main.cpp string.cpp + +*/ + +#include +#include "string.hpp" +using std::cout, std::cin, std::endl; + +int main() +{ + String a = "Mouse"; + String b; + cin >> b; + String c = b; + + if (a + c == "MouseLion") + cout << "Yes" << endl; + else + cout << "No" << endl; + + + if (a > "Mice") + cout << "Yes" << endl; + else + cout << "No" << endl; + + + c[0] = 'P'; + cout << c << endl; + + c += a; + cout << c << endl; + + c = c + String(10, 'q'); + cout << c << endl; +} diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution2/string.cpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution2/string.cpp new file mode 100644 index 0000000..f685590 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution2/string.cpp @@ -0,0 +1,204 @@ +#include +#include +#include "string.hpp" + + +String::String(const char* str) +{ + std::size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (std::size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; +} + +String::String() : String("") {} +String::String(const String& s) : String(s.cStr()) {} + +String::String(std::size_t n, char a) +{ + mSize = n; + mCapacity = n + 1; + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (std::size_t i = 0; i < mSize; ++i) + mpData[i] = a; + mpData[mSize] = '\0'; +} + +String::~String() +{ + std::free(mpData); +} + +void String::reserve(std::size_t capacity) +{ + if (capacity <= mCapacity) + return; + + mCapacity = capacity; + char* newData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (std::size_t i = 0; i < mSize; ++i) + newData[i] = mpData[i]; + newData[mSize] = '\0'; + + std::free(mpData); + mpData = newData; +} + + +void String::resize(std::size_t size) +{ + reserve(size + 1); + mSize = size; + mpData[mSize] = '\0'; +} + + +String& String::operator=(const String& right) +{ + if (this == &right) + return *this; + + mSize = right.mSize; + resize(mSize); + + for (std::size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; +} + + +String String::operator+(const String& b) +{ + String result; + result.resize(mSize + b.mSize); + + for (std::size_t i = 0; i < mSize; ++i) + result.mpData[i] = mpData[i]; + + for (std::size_t i = 0; i < b.mSize; ++i) + result.mpData[mSize + i] = b.mpData[i]; + + result.mpData[result.mSize] = '\0'; + + return result; +} + +String& String::operator+=(const String& right) +{ + *this = *this + right; + return *this; +} + +bool String::operator==(const String& right) const +{ + if (mSize != right.mSize) + return false; + + std::size_t i = 0; + while (i < mSize && mpData[i] == right.mpData[i]) + i++; + + return i == mSize; +} + +bool String::operator<(const String& right) const +{ + std::size_t i = 0; + while (i < mSize && i < right.mSize && mpData[i] == right.mpData[i]) + i++; + + return mpData[i] < right.mpData[i]; +} + +bool String::operator<=(const String& right) const +{ + std::size_t i = 0; + while (i < mSize && i < right.mSize && mpData[i] == right.mpData[i]) + i++; + + return mpData[i] <= right.mpData[i]; +} + +bool String::operator!=(const String& right) const +{ + return !(*this == right); +} + +bool String::operator>(const String& right) const +{ + return !(*this <= right); +} + +bool String::operator>=(const String& right) const +{ + return !(*this < right); +} + +char& String::operator[](std::size_t i) +{ + return mpData[i]; +} + +char& String::at(std::size_t i) +{ + if (i >= mSize) + { + std::cout << "Error! Index is out of bounds." << std::endl; + std::exit(1); + } + return mpData[i]; +} + +void String::clear() +{ + std::free(mpData); + + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + mpData[0] = '\0'; +} + +void String::addCharacter(char c) +{ + if (mSize + 1 == mCapacity) + reserve(2 * mCapacity); + + mpData[mSize] = c; + resize(mSize + 1); +} + + +std::size_t String::getSize() const {return mSize;} +std::size_t String::getCapacity() const {return mCapacity;} +const char* String::cStr() const {return mpData;} + + +std::ostream& operator<<(std::ostream& out, const String& s) +{ + out << s.cStr(); + return out; +} + +std::istream& operator>>(std::istream& in, String& s) +{ + s.clear(); + while (true) + { + char x = in.get(); + if (x == ' ' || x == '\n' || x == '\t') + break; + s.addCharacter(x); + } + return in; +} \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution2/string.hpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution2/string.hpp new file mode 100644 index 0000000..3f87a04 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution2/string.hpp @@ -0,0 +1,51 @@ +#pragma once +#include +#include + + +class String +{ +private: + + std::size_t mSize; + std::size_t mCapacity; + char* mpData; + +public: + + String(const char* str); + + String(); + String(const String& s); + + String(std::size_t n, char a); + ~String(); + + void reserve(std::size_t capacity); + void resize(std::size_t size); + + String& operator=(const String& right); + String operator+(const String& b); + String& operator+=(const String& right); + + bool operator==(const String& right) const; + bool operator<(const String& right) const; + bool operator<=(const String& right) const; + bool operator!=(const String& right) const; + bool operator>(const String& right) const; + bool operator>=(const String& right) const; + + char& operator[](std::size_t i); + char& at(std::size_t i); + + void clear(); + void addCharacter(char c); + + std::size_t getSize() const; + std::size_t getCapacity() const; + const char* cStr() const; +}; + + +std::ostream& operator<<(std::ostream& out, const String& s); +std::istream& operator>>(std::istream& in, String& s); diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution3/main.cpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution3/main.cpp new file mode 100644 index 0000000..ccd3472 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution3/main.cpp @@ -0,0 +1,32 @@ +#include +#include "string.hpp" +using std::cout, std::cin, std::endl; + +int main() +{ + mipt::String a = "Mouse"; + mipt::String b; + cin >> b; + mipt::String c = b; + + if (a + c == "MouseLion") + cout << "Yes" << endl; + else + cout << "No" << endl; + + + if (a > "Mice") + cout << "Yes" << endl; + else + cout << "No" << endl; + + + c[0] = 'P'; + cout << c << endl; + + c += a; + cout << c << endl; + + c = c + mipt::String(10, 'q'); + cout << c << endl; +} diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution3/string.cpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution3/string.cpp new file mode 100644 index 0000000..bdf2abf --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution3/string.cpp @@ -0,0 +1,207 @@ +#include +#include +#include "string.hpp" + +namespace mipt { + +String::String(const char* str) +{ + std::size_t i = 0; + while (str[i] != '\0') + i++; + mSize = i; + mCapacity = i + 1; + + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (std::size_t i = 0; str[i]; i++) + mpData[i] = str[i]; + mpData[mSize] = '\0'; +} + +String::String() : String("") {} +String::String(const String& s) : String(s.cStr()) {} + +String::String(std::size_t n, char a) +{ + mSize = n; + mCapacity = n + 1; + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (std::size_t i = 0; i < mSize; ++i) + mpData[i] = a; + mpData[mSize] = '\0'; +} + +String::~String() +{ + std::free(mpData); +} + +void String::reserve(std::size_t capacity) +{ + if (capacity <= mCapacity) + return; + + mCapacity = capacity; + char* newData = (char*)std::malloc(sizeof(char) * mCapacity); + + for (std::size_t i = 0; i < mSize; ++i) + newData[i] = mpData[i]; + newData[mSize] = '\0'; + + std::free(mpData); + mpData = newData; +} + + +void String::resize(std::size_t size) +{ + reserve(size + 1); + mSize = size; + mpData[mSize] = '\0'; +} + + +String& String::operator=(const String& right) +{ + if (this == &right) + return *this; + + mSize = right.mSize; + resize(mSize); + + for (std::size_t i = 0; i <= mSize; ++i) + mpData[i] = right.mpData[i]; + + return *this; +} + + +String String::operator+(const String& b) +{ + String result; + result.resize(mSize + b.mSize); + + for (std::size_t i = 0; i < mSize; ++i) + result.mpData[i] = mpData[i]; + + for (std::size_t i = 0; i < b.mSize; ++i) + result.mpData[mSize + i] = b.mpData[i]; + + result.mpData[result.mSize] = '\0'; + + return result; +} + +String& String::operator+=(const String& right) +{ + *this = *this + right; + return *this; +} + +bool String::operator==(const String& right) const +{ + if (mSize != right.mSize) + return false; + + std::size_t i = 0; + while (i < mSize && mpData[i] == right.mpData[i]) + i++; + + return i == mSize; +} + +bool String::operator<(const String& right) const +{ + std::size_t i = 0; + while (i < mSize && i < right.mSize && mpData[i] == right.mpData[i]) + i++; + + return mpData[i] < right.mpData[i]; +} + +bool String::operator<=(const String& right) const +{ + std::size_t i = 0; + while (i < mSize && i < right.mSize && mpData[i] == right.mpData[i]) + i++; + + return mpData[i] <= right.mpData[i]; +} + +bool String::operator!=(const String& right) const +{ + return !(*this == right); +} + +bool String::operator>(const String& right) const +{ + return !(*this <= right); +} + +bool String::operator>=(const String& right) const +{ + return !(*this < right); +} + +char& String::operator[](std::size_t i) +{ + return mpData[i]; +} + +char& String::at(std::size_t i) +{ + if (i >= mSize) + { + std::cout << "Error! Index is out of bounds." << std::endl; + std::exit(1); + } + return mpData[i]; +} + +void String::clear() +{ + std::free(mpData); + + mSize = 0; + mCapacity = 1; + mpData = (char*)std::malloc(sizeof(char) * mCapacity); + mpData[0] = '\0'; +} + +void String::addCharacter(char c) +{ + if (mSize + 1 == mCapacity) + reserve(2 * mCapacity); + + mpData[mSize] = c; + resize(mSize + 1); +} + + +std::size_t String::getSize() const {return mSize;} +std::size_t String::getCapacity() const {return mCapacity;} +const char* String::cStr() const {return mpData;} + + +std::ostream& operator<<(std::ostream& out, const String& s) +{ + out << s.cStr(); + return out; +} + +std::istream& operator>>(std::istream& in, String& s) +{ + s.clear(); + while (true) + { + char x = in.get(); + if (x == ' ' || x == '\n' || x == '\t') + break; + s.addCharacter(x); + } + return in; +} + +} \ No newline at end of file diff --git a/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution3/string.hpp b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution3/string.hpp new file mode 100644 index 0000000..ca0d683 --- /dev/null +++ b/seminar02_encapsulation/classroom_tasks/code/4separate_compilation/06problem_string_solution3/string.hpp @@ -0,0 +1,54 @@ +#pragma once +#include +#include + +namespace mipt { + +class String +{ +private: + + std::size_t mSize; + std::size_t mCapacity; + char* mpData; + +public: + + String(const char* str); + + String(); + String(const String& s); + + String(std::size_t n, char a); + ~String(); + + void reserve(std::size_t capacity); + void resize(std::size_t size); + + String& operator=(const String& right); + String operator+(const String& b); + String& operator+=(const String& right); + + bool operator==(const String& right) const; + bool operator<(const String& right) const; + bool operator<=(const String& right) const; + bool operator!=(const String& right) const; + bool operator>(const String& right) const; + bool operator>=(const String& right) const; + + char& operator[](std::size_t i); + char& at(std::size_t i); + + void clear(); + void addCharacter(char c); + + std::size_t getSize() const; + std::size_t getCapacity() const; + const char* cStr() const; +}; + + +std::ostream& operator<<(std::ostream& out, const String& s); +std::istream& operator>>(std::istream& in, String& s); + +} \ No newline at end of file diff --git a/seminar02_encapsulation/homework/code/0circle/circle.cpp b/seminar02_encapsulation/homework/code/0circle/circle.cpp new file mode 100644 index 0000000..4d123fe --- /dev/null +++ b/seminar02_encapsulation/homework/code/0circle/circle.cpp @@ -0,0 +1,38 @@ +#include +#include +#include "circle.h" +#include "point.h" + +Circle::Circle(const Point& acenter, float aradius) { + mCenter = acenter; + mRadius = aradius; +} +Circle::Circle() { + mCenter = Point {0, 0}; + mRadius = 1; +} +Circle::Circle(const Circle& circle) { + mCenter = circle.mCenter; + mRadius = circle.mRadius; +} +Point Circle::getCenter() const { return mCenter; } +float Circle::getRadius() const { return mRadius; } + +void Circle::setCenter(const Point& p) { + mCenter = p; +} +void Circle::setRadius(float radius) { + mRadius = radius > 0 ? radius : 0; +} +float Circle::getArea() const { + return mRadius * mRadius * M_PI; +} +float Circle::getDistance(const Point& p) { + return mCenter.distance(p) - mRadius; +} +bool Circle::isColliding(const Circle& c) const { + return (mCenter.distance(c.getCenter()) - (mRadius + c.getRadius()) > 0) ? false : true; +} +void Circle::move(const Point& p) { + mCenter = mCenter + p; +} diff --git a/seminar02_encapsulation/homework/code/0circle/circle.h b/seminar02_encapsulation/homework/code/0circle/circle.h new file mode 100644 index 0000000..d0bc7ee --- /dev/null +++ b/seminar02_encapsulation/homework/code/0circle/circle.h @@ -0,0 +1,23 @@ +#include "point.h" +class Circle +{ +private: + Point mCenter; + float mRadius; + +public: + Circle(const Point& acenter, float aradius); + Circle(); + Circle(const Circle& circle); + Point getCenter() const; + float getRadius() const; + + void setCenter(const Point& p); + void setRadius(float radius); + float getArea() const; + float getDistance(const Point& p); + bool isColliding(const Circle& c) const; + void move(const Point& p); +}; + + diff --git a/seminar02_encapsulation/homework/code/0circle/main.cpp b/seminar02_encapsulation/homework/code/0circle/main.cpp new file mode 100644 index 0000000..0535945 --- /dev/null +++ b/seminar02_encapsulation/homework/code/0circle/main.cpp @@ -0,0 +1,44 @@ +#include +#include "point.h" +#include "circle.h" + +using std::cout, std::endl; + + +int main() +{ + Point p = {7, -1}; + Point q = {-4, 2}; + cout << "Point p = " << p << endl; + cout << "Point q = " << q << endl; + cout << "p + q = " << p + q << endl; + + + Circle a {{4, 1}, 3}; + Circle b; + // b = a; + + cout << "Circle a: center: " << a.getCenter() << " radius: " << a.getRadius() << endl; + cout << "Circle b: center: " << b.getCenter() << " radius: " << b.getRadius() << endl; + + cout << "Area of a = " << a.getArea() << endl; + cout << "Distance from point p to circle a = " << a.getDistance(p) << endl; + + + + cout << "Collisions:" << endl; + if (a.isColliding(b)) + cout << "Yes, a is colliding b" << endl; + else + cout << "No, a isn't colliding b" << endl; + + + + cout << "Moving b by {1, 1}:" << endl; + b.move({1, 1}); + if (a.isColliding(b)) + cout << "Yes, a is colliding b" << endl; + else + cout << "No, a isn't colliding b" << endl; + +} diff --git a/seminar02_encapsulation/homework/code/0circle/point.cpp b/seminar02_encapsulation/homework/code/0circle/point.cpp new file mode 100644 index 0000000..634ac98 --- /dev/null +++ b/seminar02_encapsulation/homework/code/0circle/point.cpp @@ -0,0 +1,82 @@ +#include +#include + +#include "point.h" + + +Point::Point(float x, float y) +{ + mx = x; + my = y; +} + +Point::Point() +{ + mx = 0; + my = 0; +} + + +float Point::getX() const +{ + return mx; +} + +float Point::getY() const +{ + return my; +} + +void Point::setX(float x) +{ + mx = x; +} + +void Point::setY(float y) +{ + my = y; +} + +float Point::norm() const +{ + return std::sqrt(mx * mx + my * my); +} + +void Point::normalize() +{ + float pnorm = norm(); + mx /= pnorm; + my /= pnorm; +} + +float Point::distance(const Point& p) const +{ + return std::sqrt((p.mx - mx) * (p.mx - mx) + (p.my - my) * (p.my - my)); +} + +Point Point::operator+(const Point& right) const +{ + Point result = {mx + right.mx, my + right.my}; + return result; +} + +Point Point::operator*(float a) const +{ + Point result = {a * mx, a * my}; + return result; +} + + + + +Point operator*(float a, const Point& p) +{ + Point result = {a * p.mx, a * p.my}; + return result; +} + +std::ostream& operator<<(std::ostream& left, const Point& right) +{ + left << "(" << right.mx << ", " << right.my << ")"; + return left; +} \ No newline at end of file diff --git a/seminar02_encapsulation/homework/code/0circle/point.h b/seminar02_encapsulation/homework/code/0circle/point.h new file mode 100644 index 0000000..fb50aae --- /dev/null +++ b/seminar02_encapsulation/homework/code/0circle/point.h @@ -0,0 +1,37 @@ +#pragma once + +/* + Поля x и y сделаны приватными + Конкретно для этого класса их можно было сделать публичными + Так как пользователь всё-равно будет иметь доступ без ограничений к этим полям через геттеры и сеттеры + Но они сделаны приватными для образовательных целей +*/ + +class Point +{ +private: + float mx, my; + +public: + + Point(float x, float y); + Point(); + + float getX() const; + float getY() const; + void setX(float x); + void setY(float y); + + void normalize(); + float distance(const Point& p) const; + float norm() const; + Point operator+(const Point& right) const; + Point operator*(float a) const; + + + + friend Point operator*(float a, const Point& p); + friend std::ostream& operator<<(std::ostream& left, const Point& right); +}; + + diff --git a/seminar02_encapsulation/homework/code/1number/number.cpp b/seminar02_encapsulation/homework/code/1number/number.cpp new file mode 100644 index 0000000..55cfbcd --- /dev/null +++ b/seminar02_encapsulation/homework/code/1number/number.cpp @@ -0,0 +1,307 @@ +#include +#include +#include +#include +#include + +/* + Класс Number -- класс положительных больших чисел + + Большое число будет храниться в динамическом массиве data + Каждый элемент этого массива содержит разряд числа в 100-ричной системе счисления + (так как base = 100) + По сути, каждый элемент data хранит две цифры числа в десятичной записи + + Значение 100 для системы счисления выбрано как компромис между + эффективностью и удобством написания программы. + Если выбрать значения базы 10 - то программа будет не так эффективна по памяти + Если выбрать значения базы 256 (максимально эффективное использование памяти для типа char), + то алгоритм печати на экран сильно усложнится + В качестве альтернативы, можно было выбрать базу 1e9, + изменив при этом тип элементов c char на int + + capacity - размер массива data + size - сколько ячеек занимет число в массиве data + size <= capacity + + Для удобства разряды числа хранятся в обратном порядке + Например, число 12345678 соответствует массиву + data = {78, 56, 34, 12} + (это упрощает многие алгоритмы с такими числами) +*/ + + +class Number +{ +private: + static const int base = 100; + std::size_t size; + std::size_t capacity; + char* data; + +public: + + Number(int a) + { +#ifdef _DEBUG_CONSTRUCTOR + std::cout << "(Number constructor " << a << " -> "; +#endif + // Находим размер необходимой памяти под это число + int temp = a; + capacity = 0; + while (temp != 0) + { + temp /= base; + capacity += 1; + } + + // Отдельно обрабатываем случай, когда число равно 0 + if (capacity == 0) + capacity = 1; + + // Выделяем память и записывем число a в массив data + // Например, число 12345678 представится в виде массива [78, 56, 34, 12] + + data = new char[capacity]; + + for (int i = 0; i < capacity; ++i) + { + data[i] = a % base; + a /= base; + } + + // В данном случае размер будет равен вместимости + size = capacity; +#ifdef _DEBUG_CONSTRUCTOR + std::cout << *this << ")" << std::endl; +#endif + } + // Конструктор по умолчанию + Number() : Number(0) {} + // Конструктор копирования + Number(const Number& n) { + size = n.size; + capacity = n.capacity; + data = new char[capacity]; + for (int i = 0; i < size; i++) { + data[i] = n.data[i]; + } + } + Number(const char* str) { + int len = std::strlen(str); + size = (len + len % 2) / 2; + capacity = size; + data = new char[capacity]; + char buf[2]; + for (int i = 0; i < size; i++) { + buf[1] = str[len - 2 * i - 1]; + if (len - 2 * i - 1 > 0) { + buf[0] = str[len - 2 * i - 2]; + } + else { + buf[0] = '0'; + } + data[i] = std::stoi(buf); + } + } + ~Number() + { + delete [] data; + } + Number& operator=(const Number& right) { + capacity = right.capacity; + size = right.size; + data = new char[capacity]; + for (int i = 0; i < size; i++) { + data[i] = right.data[i]; + } + return *this; + } + + Number operator+(const Number& a) { + Number result; + int i; + char carry = 0; + int max_size = size > a.size ? size : a.size; + + result.capacity = max_size + 1; + result.data = new char[capacity]; + + for (i = 0; i < max_size; ++i) { + result.data[i] = (data[i] + a.data[i] + carry) % base; + carry = (data[i] + a.data[i] + carry) / base; + } + + if (carry) { + result.data[i] = carry; + result.size = max_size + 1; + } + else { + result.size = max_size; + } + return result; + } + void operator+=(const Number& a) { + *this = *this + a; + } + + bool isEven() const { + if (data[0] % 2) { + return false; + } + return true; + } + + Number operator*(const Number& right) const { +#ifdef _DEBUG_MUL + std::cout << "arg1=" << *this << "(capacity=" << capacity << ",size=" << size << ")" << " " << "arg2=" << right << "(capacity=" << right.capacity << ",size=" << right.size << ")"<< std::endl; +#endif + int i, j; + int temp; + Number result; + + result.capacity = capacity + right.capacity; + int *carry = (int*)std::calloc(result.capacity, sizeof(int)); + + result.data = (char*)calloc(result.capacity, sizeof(char)); +#ifdef _DEBUG_MUL + std::cout << "carry:[" << carry[0]; + for (int k = 1; k < result.capacity; ++k) { + std::cout << "," << carry[k]; + } + std::cout << "]" << std::endl; +#endif + for (i = 0; i < size; ++i) { + for (j = 0; j < right.size; ++j) { +#ifdef _DEBUG_MUL + std::cout << i + j << ":" << static_cast(result.data[i + j]) << " + " << static_cast(data[i]) << " * " << static_cast(right.data[j]) << " + " << carry[i+j] << std::endl; +#endif + temp = (result.data[i + j] + data[i] * right.data[j] + carry[i + j]); + result.data[i + j] = temp % base; + carry[i + j + 1] += temp / base; + carry[i + j] = 0; + + } + } +#ifdef _DEBUG_MUL + std::cout << "result before applying carry:" << result << std::endl; + std::cout << "carry:[" << carry[0]; + for (int k = 1; k < result.capacity; ++k) { + std::cout << "," << carry[k]; + } + std::cout << "]" << std::endl; +#endif + if (carry[i + j - 1]) { + result.data[i + j - 1] = carry[i + j - 1]; + result.size = i + j; + carry[i + j - 1] = 0; + } + else { + result.size = i + j - 1; + } + +#ifdef _DEBUG_MUL + std::cout << "before correcting capacity, result=" << result << std::endl; +#endif + // correcting capacity + /*char* temp_data = (char *)calloc(result.size, sizeof(char)); + for (i = 0; i < result.size; ++i) { + temp_data[i] = result.data[i]; + } + + free(result.data); + result.capacity = result.size; + result.data = (char*)calloc(result.size,sizeof(char)); + for (i = 0; i < result.size; ++i) { + result.data[i] = temp_data[i]; + } + + free(temp_data);*/ + free(carry); +#ifdef _DEBUG_MUL + std::cout << "return value=" << result << "(capacity=" << result.capacity << ",size=" << result.size << ")" << std::endl << "======" << std::endl; +#endif + return result; + } + void operator*=(const Number& a) { + *this = *this * a; + } + + friend std::ostream& operator<<(std::ostream& stream, const Number& right); + friend int main(); + friend Number factorial(int n); +}; + + + +std::ostream& operator<<(std::ostream& stream, const Number& right) +{ +#ifdef _DEBUG_COUT + stream << "["; + for (std::size_t i = 0; i < right.size; ++i) { + stream << static_cast(right.data[right.size - 2 - i]) << ","; + } + stream << "]"; +#else + // Печатаем самый большой разряд + stream << (int)right.data[right.size - 1]; + + // Печатаем остальные разряды с заполнением нулями до 2-х цифр + // setfill и setw это то же самое, что и в языке C спецификатор %02d + for (std::size_t i = 0; i < right.size - 1; ++i) + stream << std::setfill('0') << std::setw(2) << (int)right.data[right.size - 2 - i]; +#endif + return stream; +} + +Number fib(int n) { + Number a = 0; // F0 + Number b = 1; // F1 + for (int i = 1; i <=n; ++i) { + if (i % 2) { + a += b; + } + else { + b += a; + } + } + if (n % 2) { + return b; + } + return a; +} + +Number factorial(int n) { + Number result{1}; + for (int i = 2; i < n + 1; ++i) { + result = Number(i) * result; + } + return result; +} + +int main() +{ + Number x = Number("25852016738884976640000"); + Number y = Number("24"); + + //char s[3]; + //Number result = "1"; + //for (int i = 1; i < 26; ++i) { + //s = std::to_string(i); + //sprintf(s, "%d", i); + // result = (Number{i} * result); + //} + //x += y; + //Number z = x + y; + //std::cout << fib(1000) << std::endl; + //x = x * Number(24)*Number{25}; + //y = factorial(5); + //std::cout << x << " " << x.capacity << " " << x.size << std::endl; + //std::cout << y << " "<< y.capacity << " " << y.size << std::endl; + // 90405070506200618121707-18-13-05-18-08 + //std::cout << "===" << std::endl << Number(2) * Number(3) << " "<< Number(3) * Number(2) << std::endl; + //std::cout << "5! = " << Number(2) * Number(3) * Number(4) * Number(5) << std::endl; + std::cout << factorial(1000) << std::endl; + //std::cout << Number("620448401733239439360000") * Number(25) << std::endl; +} + diff --git a/seminar02_encapsulation/homework/homework_encapsulation.pdf b/seminar02_encapsulation/homework/homework_encapsulation.pdf new file mode 100644 index 0000000..52c019a Binary files /dev/null and b/seminar02_encapsulation/homework/homework_encapsulation.pdf differ diff --git a/seminar02_encapsulation/homework/homework_encapsulation.tex b/seminar02_encapsulation/homework/homework_encapsulation.tex new file mode 100644 index 0000000..c6e904f --- /dev/null +++ b/seminar02_encapsulation/homework/homework_encapsulation.tex @@ -0,0 +1,202 @@ +\documentclass{article} +\usepackage[utf8x]{inputenc} +\usepackage{ucs} +\usepackage{amsmath} +\usepackage{amsfonts} +\usepackage{marvosym} +\usepackage{wasysym} +\usepackage{upgreek} +\usepackage[english,russian]{babel} +\usepackage{graphicx} +\usepackage{float} +\usepackage{textcomp} +\usepackage{hyperref} +\usepackage{geometry} + \geometry{left=2cm} + \geometry{right=1.5cm} + \geometry{top=1cm} + \geometry{bottom=2cm} +\usepackage{tikz} +\usepackage{ccaption} +\usepackage{multicol} +\usepackage{fancyvrb} + +\usepackage{listings} +%\setlength{\columnsep}{1.5cm} +%\setlength{\columnseprule}{0.2pt} + +\usepackage{colortbl,graphicx,tikz} +\definecolor{X}{rgb}{.5,.5,.5} + +\date{} +\begin{document} +\pagenumbering{gobble} + +\lstset{ + language=C++, % choose the language of the code + basicstyle=\linespread{1.1}\ttfamily, + columns=fixed, + fontadjust=true, + basewidth=0.5em, + keywordstyle=\color{blue}\bfseries, + commentstyle=\color{gray}, + stringstyle=\ttfamily\color{orange!50!black}, + showstringspaces=false, + %numbers=false, % where to put the line-numbers + numbersep=5pt, + numberstyle=\tiny\color{black}, + numberfirstline=true, + stepnumber=1, % the step between two line-numbers. + numbersep=10pt, % how far the line-numbers are from the code + backgroundcolor=\color{white}, % choose the background color. You must add \usepackage{color} + showstringspaces=false, % underline spaces within strings + captionpos=b, % sets the caption-position to bottom + breaklines=true, % sets automatic line breaking + breakatwhitespace=true, % sets if automatic breaks should only happen at whitespace + xleftmargin=.2in, + extendedchars=\true, + keepspaces = true, +} +\lstset{literate=% + *{0}{{{\color{red!20!violet}0}}}1 + {1}{{{\color{red!20!violet}1}}}1 + {2}{{{\color{red!20!violet}2}}}1 + {3}{{{\color{red!20!violet}3}}}1 + {4}{{{\color{red!20!violet}4}}}1 + {5}{{{\color{red!20!violet}5}}}1 + {6}{{{\color{red!20!violet}6}}}1 + {7}{{{\color{red!20!violet}7}}}1 + {8}{{{\color{red!20!violet}8}}}1 + {9}{{{\color{red!20!violet}9}}}1 + {~} {$\sim$}{1} +} + +\title{Семинар \#2: Инкапсуляция. Домашнее задание.\vspace{-5ex}}\date{}\maketitle + +\section*{Класс Circle} +Допустим, что мы хотим создать программу, которая будет работать с окружностями (это может быть игра или, например, графический редактор). Для того, чтобы сделать код более понятным и удобным в использовании, мы решили создать класс окружности. Кроме того, мы решили использовать уже ранее написанный класс точки в 2D пространстве (файлы \texttt{point.h} и \texttt{point.cpp}). Создайте класс окружности, который будет включать следующие методы: +\begin{itemize} +\item Конструктор \texttt{Circle(const Point\& acenter, float aradius)}, который будет задавать поля \texttt{center} и \texttt{radius} соответстующими значениями. +\item Конструктор по умолчанию \texttt{Circle()} - задаются значения, соответствующие единичной окружности с центром в начале координат. +\item Конструктор копирования \texttt{Circle(const Circle\& circle)} +\item Сеттеры и геттеры, для полей \texttt{center} и \texttt{radius}. Поле \texttt{radius} нельзя задать отрицательным числом. При попытке задания его отрицательным числом оно должно устанавливаться в значение \texttt{0}. +\item Метод \texttt{float getArea() const}, который будет возвращать площадь поверхности круга. +\item Метод \texttt{float getDistance(const Point\& p) const}, который будет возвращать расстояние от точки \texttt{p}, до ближайшей точки окружности. +\item Метод \texttt{bool isColliding(const Circle\& c) const}, который будет возвращать \texttt{true}, если круг пересекается с кругом \texttt{c}. +\item Метод \texttt{void move(const Point\& p)}, который будет перемещать кружок на вектор \texttt{p}. +\end{itemize} +Весь начальный код содержится в папке \texttt{0circle}. При компиляции нужно указывать все \texttt{.cpp} файлы, которые вы хотите скомпилировать: +\begin{verbatim} +g++ main.cpp point.cpp +\end{verbatim} +\begin{itemize} +\item Создайте файлы \texttt{circle.h} и \texttt{circle.cpp} и перенесите реализацию класса окружности из файла \texttt{main.cpp} в эти файлы. +\end{itemize} + + +\newpage +\section*{Класс Number (большое число)} +Стандартные целочисленные типы данных, такие как \texttt{int} имеют фиксированный небольшой размер. Соответственно значения, которые можно хранить в переменных этих типов ограничены. Типичное максимальное значение \texttt{char} равно $2^7 - 1 = 127$, тип \texttt{int} обычно ограничен $2^{31}-1 = 2147483647$ и даже тип \texttt{unsigned long long} имеет ограничение в $2^{64}-1 = 1.8 * 10^{19}$. Хранить действительно большие числа в этих типах невозможно. В этом задании нужно сделать класс, с помощью которого будет удобно складывать и умножать большие целые положительные числа. Начальный код этого класса содержится в \texttt{1number/number.cpp}. Изучите этот код. + + +\begin{figure}[h!] + \centering + \includegraphics[scale=1]{../images/number1.png} + \caption{Представление числа 12345678 в памяти с помощью нашего класса Number} + \label{fig:nummber1} +\end{figure} + + +\subsection*{Задания:} +\begin{itemize} +\item \textbf{Конструктор по умолчанию:} Напишите конструктор по умолчанию \texttt{Number()}, который будет создавать число равное нулю. +\item \textbf{Конструктор копирования:} Напишите конструктор копирования \texttt{Number(const Number\& n)}. +\item \textbf{Конструктор из строки:} Напишите конструктор \texttt{Number(const char* str)}, который будет создавать большое число на основе строки. Предполагаем, что на вход конструктору всегда идёт корректная строка. Например, число из примера можно будет создать так: +\begin{lstlisting} +Number a = Number("12345678"); +\end{lstlisting} +\item \textbf{Присваивание:} Напишите оператор присваивания \texttt{Number\& operator=(const Number\& right)}. +\item \textbf{Сложение:} Напишите и протестируйте операторы сложения \texttt{operator+} и оператор присваивания сложения \texttt{operator+=}. Реализовывать оба этих оператора с нуля необязательно. Ведь, если написан один из этих операторов, то очень просто написать другой. +\item \textbf{Числа Фибоначчи:} Числа Фибоначчи задаются следующим образом: +\begin{align*} +F_0 &= 0\\ +F_1 &= 1\\ +F_n &= F_{n-1} + F_{n-2} +\end{align*} +Используйте класс \texttt{Number}, чтобы вычислить $F_{1000}$. Правильный ответ: +\begin{verbatim} +F(1000) = 43466557686937456435688527675040625802564660517371780402481729089536555417949051890 +40387984007925516929592259308032263477520968962323987332247116164299644090653318793829896964992 +8516003704476137795166849228875 +\end{verbatim} + + + + +\item \textbf{Четность:} Напишите метод \texttt{bool isEven() const}, который будет проверять является ли наше число чётным и, если это верно, возвращает \texttt{true}, в ином случае возвращает \texttt{false}. + +\item \textbf{Произведение:} Напишите метод \texttt{Number operator*(const Number\& right) const} - оператор умножения одного числа \texttt{Number} на другое. Протестируйте вашу функцию на различных примерах (умножение большого числа на большое, умножение большого числа на небольшое ($< 100$) или на ноль, умножение двух небольших чисел и т. д.).\\ +\item \textbf{Факториал:} Используйте написанный оператор для вычисления факториала от 1000. \\ +Правильный ответ: +\begin{verbatim} +1000! = 40238726007709377354370243392300398571937486421071463254379991042993851239862902059 +2044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759 +9188236277271887325197795059509952761208749754624970436014182780946464962910563938874378864 +8733711918104582578364784997701247663288983595573543251318532395846307555740911426241747434 +9347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379 +5345242215865932019280908782973084313928444032812315586110369768013573042161687476096758713 +4831202547858932076716913244842623613141250878020800026168315102734182797770478463586817016 +4365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186 +1168115536158365469840467089756029009505376164758477284218896796462449451607653534081989013 +8544248798495995331910172335555660213945039973628075013783761530712776192684903435262520001 +5888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838 +9714760885062768629671466746975629112340824392081601537808898939645182632436716167621791689 +0977991190375403127462228998800519544441428201218736174599264295658174662830295557029902432 +4153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690 +8979684825901254583271682264580665267699586526822728070757813918581788896522081643483448259 +9326604336766017699961283186078838615027946595513115655203609398818061213855860030143569452 +7224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136 +4932734975655139587205596542287497740114133469627154228458623773875382304838656889764619273 +8381490014076731044664025989949022222176590433990188601856652648506179970235619389701786004 +0811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614 +8396573822911231250241866493531439701374285319266498753372189406942814341185201580141233448 +2801505139969429015348307764456909907315243327828826986460278986432113908350621709500259738 +9863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826 +2809561214509948717012445164612603790293091208890869420285106401821543994571568059418727489 +9809425474217358240106367740459574178516082923013535808184009699637252423056085590370062427 +1243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000 +\end{verbatim} + +\item \textbf{Числа-градины:} Возьмём некоторое число $n$ и будем последовательно применять к нему следующую функцию: +\begin{equation*} +f(n) = + \begin{cases} + n / 2, &\textup{если n - четное}\\ + 3 n + 1, &\textup{если n - нечетное} + \end{cases} +\end{equation*} +В результате получится некоторая последовательность. Например, при $n = 7$ получится: +\begin{verbatim} +7 -> 22 -> 11 -> 34 -> 17 -> 52 -> 26 -> 13 -> 40 -> 20 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1 +\end{verbatim} +Последовательность доходит до 1. Вам нужно написать функцию, которая будет по начальному числу находить длину такой последовательности (\texttt{steps}) и максимальное число в этой последовательности(\texttt{max}). Например, для числа $7$, максимальное число в последовательности будет равно $52$, а длина последовательности -- $16$. Напишите программу, которая будет по начальному числу находить длину последовательности и максимальный элемент в ней. + +Тесты для проверки: +\begin{verbatim} +n = 7 steps = 16; max = 52 +n = 256 steps = 8; max = 256 +n = 1117065 steps = 527; max = 2974984576 +n = 4761963248413673697 steps = 2337; max = 9926927712374950744648 + +n = 90560792656972947582439785608972465789628974587264056284658721771 +steps = 1630; +max = 773658021643749360792171137214151494851244403993540980838080564520 +\end{verbatim} +Для решения этой задачи нужно написать оператор сравнения и метод деления на 2 (оператор целочисленного деления можно не писать). +\item \textbf{Раздельная компиляция:} Перенесите объявление класса \texttt{Number} в файл \texttt{number.h}, а определение методов в файл \texttt{number.cpp}. Раздельно скомпилируйте эту программу. +\end{itemize} + +\end{document} \ No newline at end of file diff --git a/seminar02_encapsulation/images/number1.png b/seminar02_encapsulation/images/number1.png new file mode 100644 index 0000000..38e5859 Binary files /dev/null and b/seminar02_encapsulation/images/number1.png differ diff --git a/seminar02_encapsulation/images/number1.svg b/seminar02_encapsulation/images/number1.svg new file mode 100644 index 0000000..f1e00e8 --- /dev/null +++ b/seminar02_encapsulation/images/number1.svg @@ -0,0 +1,263 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + 4 + + 5 + size + capacity + data + + + + + + + 78 + 56 + 43 + 12 + + Number + + diff --git a/seminar02_encapsulation/images/sepcompilation.png b/seminar02_encapsulation/images/sepcompilation.png new file mode 100644 index 0000000..2586af2 Binary files /dev/null and b/seminar02_encapsulation/images/sepcompilation.png differ diff --git a/seminar02_encapsulation/images/sepcompilation.svg b/seminar02_encapsulation/images/sepcompilation.svg new file mode 100644 index 0000000..8963c40 --- /dev/null +++ b/seminar02_encapsulation/images/sepcompilation.svg @@ -0,0 +1,338 @@ + + + + + + + + + + image/svg+xml + + + + + + + + struct Point { float x, y; float norm() const; void normalize(); Point operator+(const Point& r) const;}; + point.h + + #include <cmath>#include "point.h"float Point::norm() const { return sqrt(x*x + y*y);}void Point::normalize() { float pnorm = norm(); x /= pnorm; y /= pnorm;}Point Point::operator+(const Point& r) const{ Point result = {x + r.x, y + r.y}; return result;} + point.cpp + + #include <iostream>#include "point.h"int main() { Point p = {1, 2}; p.normalize(); std::cout << p.x << " " << p.y << std::endl;} + main.cpp + + diff --git a/seminar02_encapsulation/images/string_base.png b/seminar02_encapsulation/images/string_base.png new file mode 100644 index 0000000..f9a0186 Binary files /dev/null and b/seminar02_encapsulation/images/string_base.png differ diff --git a/seminar02_encapsulation/images/string_base.svg b/seminar02_encapsulation/images/string_base.svg new file mode 100644 index 0000000..dacd891 --- /dev/null +++ b/seminar02_encapsulation/images/string_base.svg @@ -0,0 +1,344 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + E + + l + + e + + p + + h + + a + + n + + t + + \0 + 8 + + String a + size + data + +