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