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
2022-09-14 19:05:27 +03:00

405 lines
No EOL
16 KiB
TeX
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

\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}