405 lines
16 KiB
TeX
405 lines
16 KiB
TeX
|
\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}
|