This repository has been archived on 2023-05-13. You can view files and clone it, but cannot push or open issues or pull requests.
mipt_cpp/seminar02_encapsulation/classroom_tasks/classroom_tasks_encapsulation.tex

405 lines
16 KiB
TeX
Raw Normal View History

2022-09-14 19:05:27 +03:00
\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 <cmath>
#include <iostream>
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 <cmath>
#include <iostream>
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 <cstdlib>
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 <cmath>
#include <iostream>
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 <cmath>
#include <iostream>
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}