This commit is contained in:
nihonium 2022-09-14 19:05:27 +03:00
parent 46d1c64684
commit ab6732eded
Signed by: nihonium
GPG key ID: 0251623741027CFC
98 changed files with 10319 additions and 0 deletions

View file

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

View file

@ -0,0 +1,34 @@
/*
Объектно-ориентированное программирование (ООП) основано на представлении программы в виде совокупности взаимодействующих объектов
Основные принципы ООП: абстракция, инкапсуляция, наследование и полиморфизм
Абстракция: использование только тех характеристик объекта, которые с достаточной точностью представляют его в данной системе.
В каком-то смысле обычные структуры из языка C являются примером абстракции
struct book
{
char title[100];
float price;
int pages;
};
Для описание книги в коде мы используем лишь некоторое её характеристики, достаточные для нашей задачи
Инкапсуляция: связывание данных некоторого абстрактного объекта и функций для работы с ним
Тесно связано с инкапсуляцией такое понятие как сокрытие
Сокрытие: разделение данных и функций абстрактного объекта на открытые (видимые извне) и скрытые (видимые только внутри самого объекта)
Наследование и Полиморфизм будут пройдены позже в курсе
*/
int main(){}

View file

@ -0,0 +1,54 @@
/*
Хоть язык C и не является объектно-ориентированным, некоторые зачатки подходов ООП в нём тоже есть
Например, структуры языка C являются примером Абстракции
Для работы со структурами мы обычно писали функции так, что каждая из этих функций принимает на вход первым аргументом указатель на наш объект.
Такой подход НЕ является примером Инкапсуляции, так как структура и функции для работы с ней являются независимыми друг от друга.
При желании или по ошибке можно первым аргументом в эти функции передать вообще объект другого типа.
Эта программа написана на языке C, для компиляции используйте gcc:
gcc 00book.c
Функции в этом примере делают следующее:
make_discount сделать скидку на книгу, но цена на книгу не может упасть ниже 0.
print_book напечатать информацию о книге на экран
*/
#include <stdio.h>
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);
}

View file

@ -0,0 +1,44 @@
/*
Эта программа написана на языке C++, для компиляции используйте g++:
g++ 01book.cpp
В языке C++ появились ссылки, которые могут немного упростить код из предыдущего файла
Тем не менее, структура Book и функции для работы с ней всё ещё являются независимыми друг от друга.
То есть тут тоже нет Инкапсуляции.
*/
#include <iostream>
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);
}

View file

@ -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 <iostream>
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 из константного метода?
*/

View file

@ -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 <iostream>
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();
}

View file

@ -0,0 +1,49 @@
#include <iostream>
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();
}

View file

@ -0,0 +1,53 @@
/*
Ключевое слово this
Используя указатель this внутри структуры, можно узнать адрес объекта.
Например, если метод был вызван таким образом:
a.printThis();
то внутри метода this будет означать адрес объекта a
С помощью этого указателя можно доступаться до полей класса.
Например, title и this->title это одно и то же внутри методов структуры.
this можно использовать, если имя аргумента метода совпадает с одним из полей, как, например, в методе setPrice
Внутри метода setPrice поле price перекрывается аргументом price. Но можно всё-равно доступиться до поля price, используя указатель this
*/
#include <iostream>
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();
}

View file

@ -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 <iostream>
#include <cmath>
#include <string.h>
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) из библиотеки <cstdlib>
2) Конструкторы можно перегружать также, как и обычные функции и методы.
Добавьте новый конструктор, который не будет принимать никаких аргументов и будет создавать объект с полями равными
title: "Default" price: 0 pages: 0
Вызовите этот конструктор из main
Конструктор, который не принимает аргументов, называется конструктором по умолчанию.
3) Конструкторы можно перегружать также, как и обычные функции и методы.
Добавьте новый конструктор, который будет принимать объект типа Book (по константной ссылке)
и будет задавать поля текущего объекта, используя поля аргумента
Вызовите этот конструктор из main
Конструктор, который создаёт объект, используя объект такого-же типа, называется конструктором копирования.
*/

View file

@ -0,0 +1,69 @@
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <string.h>
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();
}

View file

@ -0,0 +1,65 @@
/*
Сокрытие данных - это разделение данных и функций абстрактного объекта на открытые (видимые извне) и скрытые (видимые только внутри самого объекта)
В языке C++ это реализуется с помощью модификаторов доступа public и private
Все поля и методы объявленные в секции public называются публичными и могут быть доступны извне структуры
Все поля и методы объявленные в секции private называются приватными и не могут быть доступны извне структуры
Приватные поля и методы могут быть доступны только в методах самого структуры (а также в друзьях, но об этом позже)
Назначение сокрытия данных заключается в том, чтобы объекты нельзя было 'поломать' извне
'Поломать' тут означает задать поля объекта бессмысленным образом
Например, в нашем примере мы бы могли поломать объект просто сделав поля price или pages отрицательными
a.pages = -100;
но благодаря тому, что поле pages является приватным, это сделать нельзя.
Учитывая проверку в конструкторе, получается, что поля pages и price в принципе никогда не смогут стать отрицательными.
Таким образом мы уберегли себя от возникновения ошибок при неправильном задании полей структуры.
*/
#include <iostream>
#include <cmath>
#include <string.h>
#include <cstdlib>
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;
}

View file

@ -0,0 +1,77 @@
/*
Синтаксис инициализации с помощью коструктора
Язык C++ имеет очень длинную историю и на её протяжении в язык добавлялись новые возможности
В итоге в языке часто можно сделать одно и то же разными методами.
В частности, вызвать конструктор можно 5-ю разными способами.
В этой программе строки для создания книг a, b, c, d, e делают одно и то же, несмотря, что имеют разный синтаксис
*/
#include <iostream>
#include <cmath>
#include <string.h>
#include <cstdlib>
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-ю разными способами
*/

View file

@ -0,0 +1,62 @@
#include <iostream>
#include <cmath>
#include <string.h>
#include <cstdlib>
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();
}

View file

@ -0,0 +1,53 @@
/*
Классы. Ключевое слово class.
На самом деле классы мы уже прошли. Структуры с методоми из предыдущих файлов это и есть классы.
Для объявления класса может использоваться ключевое слово class.
Разница между этими ключевым словами минимальна
- при использовании struct все поля и методы по умолчанию публичны
- при использовании class все поля и методы по умолчанию приватны
Но, так как мы указываем private и public для всех членов, то разницы нет вообще.
*/
#include <iostream>
#include <cmath>
#include <string.h>
#include <cstdlib>
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();
}

View file

@ -0,0 +1,56 @@
/*
Некоторые замечания по оформлению
1) Как правило в классе сначала описываются публичные методы, а потом приватные
Так делают потому что если другой программист захочет воспользоваться вашим классом,
то его будет в первую очередь будет интересовать что ваш класс может делать
и уже потом его будет интересовать строение класса.
2) Приватные поля класса желательно называть так, чтобы их можно было отличить от обычных переменных
Это может сильно упростить понимание при написании/использовании больших програм и библиотек
В данном курсе мы будем называть приватные поля начиная с буквы m
Например, mTitle вместо title
*/
#include <iostream>
#include <cmath>
#include <string.h>
#include <cstdlib>
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();
}

View file

@ -0,0 +1,56 @@
/*
Это программа на языке C, для компиляции:
gcc 00point.c
./a.out
Опишем структуру точки в двумерном пространстве на языке C
Точка задаётся двумя координатами x и y
Так как эта структура имеет очень маленький размер (всего 8 байт), то в функции
её можно передавать по значению, а не по константному указателю.
*/
#include <stdio.h>
#include <math.h>
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,
которая будет принимать точку по указателю и делить координаты точки на норму точки
Эта функция не должна ничего возвращать
*/

View file

@ -0,0 +1,63 @@
/*
Это программа на языке C, для компиляции:
gcc 00point_solution.c
./a.out
*/
#include <stdio.h>
#include <math.h>
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);
}

View file

@ -0,0 +1,84 @@
/*
Это программа на языке C++, для компиляции:
g++ 01point.cpp
Та же самая точка, но на языке C++
В этом файле была видоизменена программа из предыдущего файла.
Были использованы перегруженные операторы для более удобного сложения и умножения точек.
Также была использована ссылка вместо указателя в функции pointNormalize.
*/
#include <iostream>
#include <iomanip>
#include <cmath>
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
*/

View file

@ -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 <iostream>
#include <iomanip>
#include <cmath>
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;
}

View file

@ -0,0 +1,48 @@
#include <iostream>
#include <iomanip>
#include <cmath>
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?
*/

View file

@ -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 <iostream>
#include <iomanip>
#include <cmath>
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;
}

View file

@ -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 из <algorithm>, которая принимает на вход три числа и
если первое число находится в промежутке между вторым и третьим, то clamp возвращает первое число
если первое число меньше, чем второе, то clamp возвращает второе число
если первое число больше, чем третье, то clamp возвращает третье число
Грубо говоря clamp ограничивает число в заданых пределах
*/
#include <iostream>
#include <cmath>
#include <algorithm>
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
*/

View file

@ -0,0 +1,45 @@
/*
Создадим свою строку
Один из самых главных недостатков языка C это работа со строками.
Строки в языке C это просто массивы элементов типа char
char str[100]; или char* p = malloc(100);
В языке C работать со строками очень неудобно по многим причинам:
- Нужно постоянно следить за тем, чтобы строка умещалась в памяти, которая под нею выделена.
- Строку можно выделить на Стеке, используя обычный массив, и тогда её вместимость нельзя будет увеличить,
а можно выделить в Куче, но тогда придётся самостоятельно выделять и освобождать память
и следить, чтобы не произошли утечки памяти.
- Строки нельзя просто копировать, сравнивать, складывать и т. д. Для этого нужно использовать
специальные функции типа strcpy и другие функции из библиотеки <string.h>.
Создадим же удобный класс строки
Такой чтобы можно было удобно создавать строки, приравнивать, складывать и сравнивать.
Не заботясь о выделении/удалении памяти, и о том, что строка помещается в выделенную память.
Чтобы можно было писать вот такой код:
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() {}

View file

@ -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 <iostream>
#include <cstdlib>
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;
*/

View file

@ -0,0 +1,104 @@
#include <iostream>
#include <cstdlib>
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;
}

View file

@ -0,0 +1,109 @@
/*
Делегирующий конструктор
В разных конструкторах может быть повторяющийся код.
Повторений кода иногда можно избежать если писать один конструктор на основе уже написанного.
Это можно сделать с помощью синтаксиса так называемого делегирующего конструктора
После объявления конструктора, но перед его телом можно написать двоеточие и вызвать другой конструктор
Например:
String() : String("")
{
cout << "Default Constructor" << endl;
}
Этот конструктор сначала вызовет конструктор String(const char* str) с аргументом "" (то есть пустой строкой)
а потом исполнит тело данного коструктора (в данном случае напечатает Default Constructor).
*/
#include <iostream>
#include <cstdlib>
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;
}
/*
Задача:
Попробуйте понять без запуска, что напечатает данная программа
*/

View file

@ -0,0 +1,113 @@
/*
В конструкторе мы выделили память с malloc, но нигде в программе её не освободили
Соответственно, в предыдущей программе у нас есть очень серьёзная ошибка - утечка памяти.
Где же нужно освобождать память?
Если память была выделена в конструкторе при создании объекта, то выделять её нужно при удалении объекта.
Для того, чтобы испольнить ваш код при удалении объекта существует специальный метод, который называется Деструктор.
Деструктор - это специальный метод, который вызывается тогда, когда объект уничтожается
Объекты, созданные на стеке удаляются при выходе из области видимости
Синтаксис деструктора такой:
~String()
{
...
}
*/
#include <iostream>
#include <cstdlib>
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";
}
*/

View file

@ -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 <iostream>
#include <cstdlib>
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
*/

View file

@ -0,0 +1,86 @@
#include <iostream>
#include <cstdlib>
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;
}

View file

@ -0,0 +1,86 @@
#include <iostream>
#include <cstdlib>
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;
}

View file

@ -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 <iostream>
#include <cstdlib>
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;
*/

View file

@ -0,0 +1,92 @@
#include <iostream>
#include <cstdlib>
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;
}

View file

@ -0,0 +1,132 @@
/*
Оператор присваивания сложения +=
Очень похож на оператор присваивания, разница только в том, каким станет левый операнд после применения этого оператора
К левому операнду в этом случае должна прибавиться копия правого оператора
Если a = "Cat" , b = "Dog" , то после применения a += b строка a будет равна "CatDog"
Прототип оператора присваивания сложения как метода класса String должен выглядеть так:
String& operator+=(const String& b)
*/
#include <iostream>
#include <cstdlib>
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
Подсказка: можно использовать уже написанные операторы, чтобы реализовать этот
*/

View file

@ -0,0 +1,110 @@
#include <iostream>
#include <cstdlib>
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;
}

View file

@ -0,0 +1,122 @@
/*
По решению предыдущего занятия понятно, что операторы + = и += взаимосвязаны.
Если реализованы операторы + и = можно, используя их, реализовать оператор +=
Если реализованы операторы = и += можно, используя их, реализовать оператор +
*/
#include <iostream>
#include <cstdlib>
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) Напишите оператор + , используя операторы = и +=
*/

View file

@ -0,0 +1,108 @@
#include <iostream>
#include <cstdlib>
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;
}

View file

@ -0,0 +1,120 @@
/*
Операторы сравнения == != > >= < <=
К строкам можно применять операторы сравнения.
Очевидно, когда строки равны: должны быть равны размеры строк (mSize) и все символы от 0 до mSize
При этом вместимость (mCapacity) у равных строк может отличаться
Как сравнивать строки на больше/меньше? В этом случае сравниваем лексикографически, то есть, по алфавиту.
То слово, которое находилось бы в орфографическом словаре позже и будет большим.
Например, "Cat" > "Camel" так как первые 2 буквы совпадают, а третья буква у слова Cat идёт дальше по алфавиту
Более точное сравнение такое: мы сравниваем посимвольно до первого несовпадающего символа
Если мы нашли первые несовпадающий символ и не дошли до конца в обоих строках, то та строка будет больше,
у которой этот символ больше.
*/
#include <iostream>
#include <iomanip>
#include <cstdlib>
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
Подсказка: можно использовать уже написанные сравнения
Например, если вы написали оператор > , то очень просто написать оператор <= , используя оператор >
*/

View file

@ -0,0 +1,140 @@
#include <iostream>
#include <iomanip>
#include <cstdlib>
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;
}

View file

@ -0,0 +1,122 @@
/*
Операторы индексации []
Чтобы получить доступ к одному символу у строки в стиле C можно использовать оператор индексации (квадратные скобочки).
Хотелось бы иметь такую же возможность и для нашей строки.
Для этого можно перегрузить оператор индексации:
char& operator[](size_t i)
Этот оператор вызавется при взятии символа по индексу, например, если a это строка типа String,
a[i] будет восприниматься компилятором как a.operator[](i)
Оператор индексации должен возвращать ссылку на символ, чтобы можно было менять соответствующий символ
a[i] = 'A';
*/
#include <iostream>
#include <iomanip>
#include <cstdlib>
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) из библиотеки <cstdlib>.
*/

View file

@ -0,0 +1,102 @@
#include <iostream>
#include <iomanip>
#include <cstdlib>
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;
}

View file

@ -0,0 +1,145 @@
/*
Напишем ещё 2 очень полезных метода:
1) reserve - увеличивает вместимость строки, на вход методу передаётся новая вместмость
если новая вместимость меньше старой, то ничего не происходит (вместимость не уменьшается)
размер и содержимое строки не меняется
2) resize - изменяет размер строки, на вход методу передаётся новый размер
если новый размер меньше старого, то строка усекается
при необходимости увеличивает вместимость
Используя эти два метода можно немного упростить код для операторов сложения и присваивания.
Эти методы могут быть полезны и для программиста, который будет работать с нашей строкой, поэтому сделаем их публичными.
*/
#include <iostream>
#include <iomanip>
#include <cstdlib>
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;
}

View file

@ -0,0 +1,134 @@
#include <iostream>
#include <iomanip>
#include <cstdlib>
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;
}

View file

@ -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 <iostream>
#include <iomanip>
#include <cstdlib>
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) Напишите перегруженный оператор << для считывания строки с экрана.
*/

View file

@ -0,0 +1,158 @@
#include <iostream>
#include <iomanip>
#include <cstdlib>
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;
}

View file

@ -0,0 +1,262 @@
/*
Собираем все методы вместе. Получилась строка, которой можно удобно пользоваться и не задумываться о выделении памяти.
*/
#include <iostream>
#include <cstdlib>
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".
*/

View file

@ -0,0 +1,233 @@
#include <iostream>
#include <cstdlib>
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;
}
}

View file

@ -0,0 +1,142 @@
/*
Рассмотрим при каких условиях происходит вызов того или иного метода.
Для этого будем использовать класс String, в котором была добавлена печать на экран для конструкторов,
деструктора и оператора присваивания.
Например, конструктор по умолчанию будет печатать Default Constructor и т. д.
*/
#include <iostream>
#include <cstdlib>
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) Проверьте ваши догадки скомпилировав и запустив программу
*/

View file

@ -0,0 +1,121 @@
/*
Рассмотрим при каких условиях происходит вызов того или иного метода.
Для этого будем использовать класс String, в котором была добавлена печать на экран для конструкторов,
деструктора и оператора присваивания.
Например, конструктор по умолчанию будет печатать Default Constructor и т. д.
*/
#include <iostream>
#include <cstdlib>
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) Проверьте ваши догадки, скомпилировав и запустив программу
*/

View file

@ -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=)
*/

View file

@ -0,0 +1,135 @@
/*
Посмотрим какие особые методы вызываются при передаче объекта в функцию.
*/
#include <iostream>
#include <cstdlib>
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;
}
Какие методы будут вызваны теперь?
*/

View file

@ -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.
Получается, что обычно передавать объекты в функции более эффективно по ссылке, а не по значению.
*/

View file

@ -0,0 +1,131 @@
/*
Особые методы при передачах в функцию
Посмотрим какие особые методы вызываются при передаче объекта в функцию
*/
#include <iostream>
#include <cstdlib>
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 чтобы она меняла первую букву переданной строки.
*/

View file

@ -0,0 +1,136 @@
#include <iostream>
#include <cstdlib>
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';
}
*/

View file

@ -0,0 +1,149 @@
/*
Различный синтаксис вызова конструкторов
*/
#include <iostream>
#include <cstdlib>
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.
*/

View file

@ -0,0 +1,124 @@
#include <iostream>
#include <cstdlib>
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.
*/

View file

@ -0,0 +1,116 @@
#include <iostream>
#include <cstdlib>
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};
}

View file

@ -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 <iostream>
#include <cstdlib>
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.
*/

View file

@ -0,0 +1,191 @@
/*
Особые методы
Проверим, что компилятор автоматически генерирует конструктор копирования и оператор присваивания.
*/
#include <iostream>
#include <cstdlib>
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.
*/

View file

@ -0,0 +1,148 @@
/*
Замечание по поводу автоматической генерации конструктора по умолчанию
*/
#include <iostream>
#include <cstdlib>
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 такого конструктора нет и автоматически он не был создан. Поэтому эта строка приведёт к ошибке.
Но конструктор копирования, оператор присваивания и деструктор автоматически генерироваться будут.
Задача:
Исправьте ошибку, написав конструктор по умолчанию самостоятельно.
*/

View file

@ -0,0 +1,164 @@
/*
Удалённые методы
*/
#include <iostream>
#include <cstdlib>
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;
}
Можно ли туда передать что-нибудь?
*/

View file

@ -0,0 +1,188 @@
/*
Неявное приведение типа с помощью конструктора от одного параметра.
*/
#include <iostream>
#include <cstdlib>
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')
*/

View file

@ -0,0 +1,147 @@
/*
Ключевое слово explicit.
*/
#include <iostream>
#include <cstdlib>
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));
*/

View file

@ -0,0 +1,62 @@
/*
Раздельная компиляция.
В этой части мы вынесем весь код, связанный с нашим классом Point в отдельные файлы.
А также скомпилируем отдельно код, относящийся к нашему классу Point.
Это будет проделано в следующий примерах, а пока тут просто лежит код
класса Point из предыдущих частей.
*/
#include <iostream>
#include <iomanip>
#include <cmath>
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;
}

View file

@ -0,0 +1,81 @@
/*
Вынос определений методов вне класса.
Методы внутри класса можно только объявить, а определить их можно вне класса.
Например, метод norm объявлен внутри класса:
float norm() const;
а определён вне класса:
float Point::norm() const
{
return std::sqrt(x * x + y * y);
}
Чтобы компилятор понимал к какому классу относится тот или иной метод,
к его имени вне класса нужно добавить название класса и два двоеточия.
Поэтому метод norm вне класса Point называется как Point::norm.
*/
#include <iostream>
#include <iomanip>
#include <cmath>
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;
}

View file

@ -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 <iostream>.
Чтобы посмотреть в каких папках компилятор ищет файлы стандартной библиотеки можно скомпилировать программу так:
g++ -v main.cpp
Он напечатает множество информации о компиляции, в числе прочего пути где происходит поиск при исполнении #include.
Вы могли заметить, что стандартные библиотеки в директиве #include пишутся в треугольных скобочках, а нашу
библиотеку point.hpp мы написали в двойных кавычках. В чём разница между этими вариантами?
На самом деле разница лишь в том, в каких папках компилятор ищет данные файлы.
Если мы пишем библиотеку в треугольных скобках, то компилятор ищет её в папках стандартной библиотеки.
Если мы пишем библиотеку в кавычках, то компилятор ищет её в папках текущей директории.
Защита от двойного включения
Используя директивы #include, очень легко включить один и тот же файл 2 и более раз.
Например, в этом примере файл iostream включается 2 раза: один раз непосредственно в файле main.cpp, а второй раз
он включится после того, как включиться файл point.hpp. Внутри файла point.hpp тоже есть #include <iostream>.
Если не предпринимать никаких действий, то произойдёт ошибка, так как одинаковые функции и классы
будут повторяться в программе несколько раз.
Чтобы защититься от этой проблемы нужно в начале заголовочного файла написать директиву
#pragma once
Таким образом компилятор будет понимать, что вы не хотите включать файл более одного раза и включит только одну копию файла.
Эта директива была использовани и в файле point.hpp.
*/
#include <iostream>
#include "point.hpp"
using std::cout, std::endl;
int main()
{
Point a = {7.2, 3.1};
cout << a.norm() << endl;
}

View file

@ -0,0 +1,39 @@
#pragma once
#include <iostream>
#include <iomanip>
#include <cmath>
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;
}

View file

@ -0,0 +1,17 @@
/*
Всё что относится к классу Point мы перенесли в отдельный файл point.hpp.
А также мы разделили объявления и определения методов в файле point.hpp.
*/
#include <iostream>
#include <iomanip>
#include <cmath>
#include "point.hpp"
using std::cout, std::endl;
int main()
{
Point a = {7.2, 3.1};
cout << a.norm() << endl;
}

View file

@ -0,0 +1,45 @@
#pragma once
#include <iostream>
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;
}

View file

@ -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 <iostream>
#include "point.hpp"
using std::cout, std::endl;
int main()
{
Point a = {7.2, 3.1};
cout << a.norm() << endl;
}

View file

@ -0,0 +1,35 @@
#include <iostream>
#include <iomanip>
#include <cmath>
#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;
}

View file

@ -0,0 +1,15 @@
#pragma once
#include <iostream>
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);

View file

@ -0,0 +1,30 @@
/*
Пространства имён
Имя Point достаточно распространённое и есть очень большая вероятность того, что при подключении нескольких библиотек
в некоторых из них тоже будет класс с именем Point. Конечно, если ничего не предпринять, это приведёт к ошибке.
Чтобы избежать ошибок, связанных с конфликтами имён, положим весь код нашего класса Point в пространство имён mipt.
Для этого обернём наш код в файлах point.hpp и point.cpp
namespace mipt
{
...
}
Теперь наш класс Point будет лежать в пространстве имён mipt.
Для его использования вне пространства имён mipt нужно писать mipt::Point (ну либо использовать using mipt::Point;)
*/
#include <iostream>
#include "point.hpp"
using std::cout, std::endl;
int main()
{
mipt::Point a = {7.2, 3.1};
cout << a.norm() << endl;
}

View file

@ -0,0 +1,40 @@
#include <iostream>
#include <iomanip>
#include <cmath>
#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;
}
}

View file

@ -0,0 +1,20 @@
#pragma once
#include <iostream>
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);
}

View file

@ -0,0 +1,265 @@
/*
В данной программе содержится класс String, написанный нами в одной из предыдущих частей.
Задачи:
1) Вынесите класс String в отдельный заголовочный файл string.hpp и скомпилируйте программу.
2) Вынесите объявления методов класса String в заголовочный файл string.hpp, а определения
методов в компилируемый файл string.cpp. Скомпилируйте программу.
3) Вынесите объявления методов класса String в заголовочный файл string.hpp, а определения
методов в компилируемый файл string.cpp. А также поместите весь код класса String в пространство имён mipt.
Скомпилируйте программу.
*/
#include <iostream>
#include <cstdlib>
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;
}

View file

@ -0,0 +1,37 @@
#include <iostream>
#include <cstdlib>
#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;
}

View file

@ -0,0 +1,214 @@
#include <iostream>
#include <cstdlib>
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;
}

View file

@ -0,0 +1,39 @@
/*
Компиляция:
g++ main.cpp string.cpp
*/
#include <iostream>
#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;
}

View file

@ -0,0 +1,204 @@
#include <iostream>
#include <cstdlib>
#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;
}

View file

@ -0,0 +1,51 @@
#pragma once
#include <iostream>
#include <cstdlib>
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);

View file

@ -0,0 +1,32 @@
#include <iostream>
#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;
}

View file

@ -0,0 +1,207 @@
#include <iostream>
#include <cstdlib>
#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;
}
}

View file

@ -0,0 +1,54 @@
#pragma once
#include <iostream>
#include <cstdlib>
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);
}

View file

@ -0,0 +1,38 @@
#include <iostream>
#include <math.h>
#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;
}

View file

@ -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);
};

View file

@ -0,0 +1,44 @@
#include <iostream>
#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;
}

View file

@ -0,0 +1,82 @@
#include <iostream>
#include <cmath>
#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;
}

View file

@ -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);
};

View file

@ -0,0 +1,307 @@
#include <iostream>
#include <cstring>
#include <iomanip>
#include <vector>
#include <algorithm>
/*
Класс 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<int>(result.data[i + j]) << " + " << static_cast<int>(data[i]) << " * " << static_cast<int>(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<int>(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;
}

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -0,0 +1,263 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="number1.svg"
inkscape:export-filename="C:\Users\vova\workspace\latex\cs_mipt_faki\term2\seminar02_encapsulation\images\number1.png"
inkscape:export-xdpi="300"
inkscape:export-ydpi="300">
<defs
id="defs2">
<marker
inkscape:stockid="TriangleOutM"
orient="auto"
refY="0.0"
refX="0.0"
id="TriangleOutM"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path1145"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
transform="scale(0.4)" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
inkscape:cx="317.04261"
inkscape:cy="872.15364"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="2560"
inkscape:window-height="1377"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="opacity:1;fill:#d7f4d7;fill-opacity:1;stroke:#000000;stroke-width:0.30000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect815"
width="23.450636"
height="8.9508085"
x="62.878162"
y="28.701445" />
<rect
style="opacity:1;fill:#d7f4d7;fill-opacity:1;stroke:#000000;stroke-width:0.30000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect815-2"
width="23.450638"
height="8.9508085"
x="62.878166"
y="37.652252" />
<rect
style="opacity:1;fill:#cccccc;fill-opacity:1;stroke:#000000;stroke-width:0.30000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect815-0"
width="23.450638"
height="8.9508085"
x="62.878166"
y="46.603043" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="73.134239"
y="35.031639"
id="text840"><tspan
sodipodi:role="line"
id="tspan838"
x="73.134239"
y="35.031639"
style="stroke-width:0.26458332">4</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="50.580757"
y="111.18085"
id="text844"><tspan
sodipodi:role="line"
id="tspan842"
x="50.580757"
y="115.4662"
style="stroke-width:0.26458332"></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="73.19175"
y="43.75705"
id="text840-0"><tspan
sodipodi:role="line"
id="tspan838-0"
x="73.19175"
y="43.75705"
style="stroke-width:0.26458332">5</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="51.218136"
y="34.563919"
id="text872"><tspan
sodipodi:role="line"
id="tspan870"
x="51.218136"
y="34.563919"
style="stroke-width:0.26458332">size</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="40.424309"
y="43.743973"
id="text872-4"><tspan
sodipodi:role="line"
id="tspan870-4"
x="40.424309"
y="43.743973"
style="stroke-width:0.26458332">capacity</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="51.084499"
y="53.3396"
id="text894"><tspan
sodipodi:role="line"
id="tspan892"
x="51.084499"
y="53.3396"
style="stroke-width:0.26458332">data</tspan></text>
<circle
style="opacity:1;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.35941789;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path896"
cx="74.336952"
cy="50.934158"
r="1.8411777" />
<rect
style="opacity:1;fill:#d7f4d7;fill-opacity:1;stroke:#000000;stroke-width:0.20522621;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect815-5"
width="11.451464"
height="8.5778608"
x="101.42937"
y="46.831276" />
<rect
style="opacity:1;fill:#d7f4d7;fill-opacity:1;stroke:#000000;stroke-width:0.20522621;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect815-5-8"
width="11.451464"
height="8.5778608"
x="112.88083"
y="46.831276" />
<rect
style="opacity:1;fill:#d7f4d7;fill-opacity:1;stroke:#000000;stroke-width:0.20522621;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect815-5-1"
width="11.451464"
height="8.5778608"
x="124.33229"
y="46.831276" />
<rect
style="opacity:1;fill:#d7f4d7;fill-opacity:1;stroke:#000000;stroke-width:0.20522621;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect815-5-5"
width="11.451464"
height="8.5778608"
x="135.78375"
y="46.831276" />
<rect
style="opacity:1;fill:#ebffeb;fill-opacity:1;stroke:#000000;stroke-width:0.20522621;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect815-5-82"
width="11.451464"
height="8.5778608"
x="147.23523"
y="46.831276" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="104.47309"
y="52.654404"
id="text840-07"><tspan
sodipodi:role="line"
id="tspan838-4"
x="104.47309"
y="52.654404"
style="stroke-width:0.26458332">78</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="115.86423"
y="52.529114"
id="text840-07-9"><tspan
sodipodi:role="line"
id="tspan838-4-7"
x="115.86423"
y="52.529114"
style="stroke-width:0.26458332">56</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="127.49047"
y="52.54583"
id="text840-07-4"><tspan
sodipodi:role="line"
id="tspan838-4-8"
x="127.49047"
y="52.54583"
style="stroke-width:0.26458332">43</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="139.01648"
y="52.495712"
id="text840-07-8"><tspan
sodipodi:role="line"
id="tspan838-4-9"
x="139.01648"
y="52.495712"
style="stroke-width:0.26458332">12</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#TriangleOutM)"
d="m 74.537403,51.067801 c 4.677216,-0.36749 21.181108,-0.8018 24.288118,-0.80179"
id="path998"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="63.742908"
y="27.441465"
id="text1806"><tspan
sodipodi:role="line"
id="tspan1804"
x="63.742908"
y="27.441465"
style="stroke-width:0.26458332">Number</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

View file

@ -0,0 +1,338 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="sepcompilation.svg"
inkscape:export-filename="C:\Users\vova\workspace\latex\cs_mipt_faki\term2\seminar02_encapsulation\images\sepcompilation.png"
inkscape:export-xdpi="300"
inkscape:export-ydpi="300">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.98994949"
inkscape:cx="243.20281"
inkscape:cy="570.05869"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="2560"
inkscape:window-height="1377"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="opacity:1;fill:#e7eef4;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect815"
width="138.83838"
height="58.966839"
x="23.432297"
y="13.06549" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="28.503822"
y="24.373625"
id="text823"><tspan
sodipodi:role="line"
id="tspan821"
x="28.503822"
y="24.373625"
style="stroke-width:0.26458332"><tspan
style="fill:#0000ff"
id="tspan845">struct</tspan> Point {</tspan><tspan
sodipodi:role="line"
x="28.503822"
y="30.547235"
style="stroke-width:0.26458332"
id="tspan825"> <tspan
style="fill:#0000ff"
id="tspan837">float</tspan> x, y;</tspan><tspan
sodipodi:role="line"
x="28.503822"
y="36.720848"
style="stroke-width:0.26458332"
id="tspan827"></tspan><tspan
sodipodi:role="line"
x="28.503822"
y="42.894459"
style="stroke-width:0.26458332"
id="tspan829"> <tspan
style="fill:#0000ff"
id="tspan841">float</tspan> norm() const;</tspan><tspan
sodipodi:role="line"
x="28.503822"
y="49.068069"
style="stroke-width:0.26458332"
id="tspan831"> <tspan
style="fill:#0000ff"
id="tspan843">void</tspan> normalize();</tspan><tspan
sodipodi:role="line"
x="28.503822"
y="55.24168"
style="stroke-width:0.26458332"
id="tspan833"> Point <tspan
style="fill:#0000ff"
id="tspan847">operator</tspan>+(<tspan
style="fill:#0000ff"
id="tspan851">const</tspan> Point&amp; r) <tspan
style="fill:#0000ff"
id="tspan849">const</tspan>;</tspan><tspan
sodipodi:role="line"
x="28.503822"
y="61.415291"
style="stroke-width:0.26458332"
id="tspan835">};</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="25.79151"
y="11.021605"
id="text855"><tspan
sodipodi:role="line"
id="tspan853"
x="25.79151"
y="11.021605"
style="stroke-width:0.26458332">point.h</tspan></text>
<rect
style="opacity:1;fill:#e7eef4;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect815-8"
width="139.52013"
height="114.80299"
x="23.665709"
y="86.590546" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="26.57818"
y="89.27375"
id="text823-4"><tspan
sodipodi:role="line"
x="26.57818"
y="89.27375"
style="fill:#0000ff;stroke-width:0.26458332"
id="tspan835-0"> </tspan><tspan
sodipodi:role="line"
x="26.57818"
y="95.447365"
style="stroke-width:0.26458332"
id="tspan1159"><tspan
style="fill:#0000ff"
id="tspan1161">#include</tspan> &lt;cmath&gt;</tspan><tspan
sodipodi:role="line"
x="26.57818"
y="101.62097"
style="stroke-width:0.26458332"
id="tspan1163"><tspan
style="fill:#0000ff"
id="tspan1171">#include</tspan> &quot;point.h&quot;</tspan><tspan
sodipodi:role="line"
x="26.57818"
y="107.79458"
style="stroke-width:0.26458332"
id="tspan997" /><tspan
sodipodi:role="line"
x="26.57818"
y="113.96819"
style="stroke-width:0.26458332"
id="tspan995"><tspan
style="fill:#0000ff"
id="tspan1001">float</tspan> Point::norm() <tspan
style="fill:#0000ff"
id="tspan1003">const</tspan> {</tspan><tspan
sodipodi:role="line"
x="26.57818"
y="120.14181"
style="stroke-width:0.26458332"
id="tspan969"> <tspan
style="fill:#0000ff"
id="tspan1005">return</tspan> sqrt(x*x + y*y);</tspan><tspan
sodipodi:role="line"
x="26.57818"
y="126.31542"
style="stroke-width:0.26458332"
id="tspan971">}</tspan><tspan
sodipodi:role="line"
x="26.57818"
y="132.48901"
style="stroke-width:0.26458332"
id="tspan973" /><tspan
sodipodi:role="line"
x="26.57818"
y="138.66263"
style="stroke-width:0.26458332"
id="tspan975"><tspan
style="fill:#0000ff"
id="tspan1007">void</tspan> Point::normalize() {</tspan><tspan
sodipodi:role="line"
x="26.57818"
y="144.83624"
style="stroke-width:0.26458332"
id="tspan977"> <tspan
style="fill:#0000ff"
id="tspan1009">float</tspan> pnorm = norm();</tspan><tspan
sodipodi:role="line"
x="26.57818"
y="151.00984"
style="stroke-width:0.26458332"
id="tspan979"> x /= pnorm;</tspan><tspan
sodipodi:role="line"
x="26.57818"
y="157.18346"
style="stroke-width:0.26458332"
id="tspan981"> y /= pnorm;</tspan><tspan
sodipodi:role="line"
x="26.57818"
y="163.35707"
style="stroke-width:0.26458332"
id="tspan983">}</tspan><tspan
sodipodi:role="line"
x="26.57818"
y="169.53069"
style="stroke-width:0.26458332"
id="tspan985" /><tspan
sodipodi:role="line"
x="26.57818"
y="175.7043"
style="stroke-width:0.26458332"
id="tspan987">Point Point::<tspan
style="fill:#0000ff"
id="tspan1011">operator</tspan>+(<tspan
style="fill:#0000ff"
id="tspan1013">const</tspan> Point&amp; r) <tspan
style="fill:#0000ff"
id="tspan1015">const</tspan>{</tspan><tspan
sodipodi:role="line"
x="26.57818"
y="181.8779"
style="stroke-width:0.26458332"
id="tspan989"> Point result = {x + r.x, y + r.y};</tspan><tspan
sodipodi:role="line"
x="26.57818"
y="188.05151"
style="stroke-width:0.26458332"
id="tspan991"> <tspan
style="fill:#0000ff"
id="tspan1017">return</tspan> result;</tspan><tspan
sodipodi:role="line"
x="26.57818"
y="194.22513"
style="stroke-width:0.26458332"
id="tspan993">}</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="25.736752"
y="81.801659"
id="text855-9"><tspan
sodipodi:role="line"
id="tspan853-3"
x="25.736752"
y="81.801659"
style="stroke-width:0.26458332">point.cpp</tspan></text>
<rect
style="opacity:1;fill:#e7eef4;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect815-6"
width="139.8732"
height="58.965"
x="23.31591"
y="215.80164" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="28.386515"
y="227.10884"
id="text823-9"><tspan
sodipodi:role="line"
x="28.386515"
y="227.10884"
style="stroke-width:0.26458332"
id="tspan835-8"><tspan
style="fill:#0000ff"
id="tspan1149">#include</tspan> &lt;iostream&gt;</tspan><tspan
sodipodi:role="line"
x="28.386515"
y="233.28246"
style="stroke-width:0.26458332"
id="tspan1143"><tspan
style="fill:#0000ff"
id="tspan1151">#include</tspan> &quot;point.h&quot;</tspan><tspan
sodipodi:role="line"
x="28.386515"
y="239.45607"
style="stroke-width:0.26458332"
id="tspan1145" /><tspan
sodipodi:role="line"
x="28.386515"
y="245.62967"
style="stroke-width:0.26458332"
id="tspan1141"><tspan
style="fill:#0000ff"
id="tspan1153">int</tspan> main() {</tspan><tspan
sodipodi:role="line"
x="28.386515"
y="251.80327"
style="stroke-width:0.26458332"
id="tspan1131"> Point p = {1, 2};</tspan><tspan
sodipodi:role="line"
x="28.386515"
y="257.97687"
style="stroke-width:0.26458332"
id="tspan1133"> p.normalize();</tspan><tspan
sodipodi:role="line"
x="28.386515"
y="264.15048"
style="stroke-width:0.26458332"
id="tspan1137"> std::cout &lt;&lt; p.x &lt;&lt; &quot; &quot; &lt;&lt; p.y &lt;&lt; std::endl;</tspan><tspan
sodipodi:role="line"
x="28.386515"
y="270.3241"
style="stroke-width:0.26458332"
id="tspan1139">}</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="25.674202"
y="213.75682"
id="text855-5"><tspan
sodipodi:role="line"
id="tspan853-9"
x="25.674202"
y="213.75682"
style="stroke-width:0.26458332">main.cpp</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View file

@ -0,0 +1,344 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="string_base.svg"
inkscape:export-filename="C:\Users\vova\workspace\latex\cs_mipt_faki\term2\seminar02_encapsulation\images\string_base.png"
inkscape:export-xdpi="300"
inkscape:export-ydpi="300">
<defs
id="defs2">
<marker
inkscape:stockid="TriangleOutM"
orient="auto"
refY="0.0"
refX="0.0"
id="marker5033"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5031"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
transform="scale(0.4)" />
</marker>
<marker
inkscape:stockid="TriangleOutL"
orient="auto"
refY="0.0"
refX="0.0"
id="TriangleOutL"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4853"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
transform="scale(0.8)" />
</marker>
<marker
inkscape:stockid="TriangleOutM"
orient="auto"
refY="0.0"
refX="0.0"
id="TriangleOutM"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4856"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
transform="scale(0.4)" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.98994949"
inkscape:cx="315.82303"
inkscape:cy="843.84077"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
showguides="false"
inkscape:window-width="2560"
inkscape:window-height="1377"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="opacity:1;fill:#d7f4d7;fill-opacity:1;stroke:#000000;stroke-width:0.52916664;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4520"
width="30.468725"
height="9.6217022"
x="39.030174"
y="38.982063" />
<rect
style="opacity:1;fill:#e6e6e6;fill-opacity:1;stroke:#000000;stroke-width:0.52916664;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4520-4"
width="30.468725"
height="9.6217022"
x="39.030174"
y="48.603767" />
<circle
style="opacity:1;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.44303662;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4543"
cx="54.455605"
cy="53.397713"
r="2.573324" />
<flowRoot
xml:space="preserve"
id="flowRoot4545"
style="fill:black;fill-opacity:1;stroke:none;font-family:Consolas;font-style:normal;font-weight:normal;font-size:18.66666667px;line-height:1.25;letter-spacing:0px;word-spacing:0px;-inkscape-font-specification:'Consolas, Normal';font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal"><flowRegion
id="flowRegion4547"><rect
id="rect4549"
width="38.385796"
height="35.860416"
x="342.94678"
y="370.46112" /></flowRegion><flowPara
id="flowPara4551"></flowPara></flowRoot> <rect
style="opacity:1;fill:#fff6d5;fill-opacity:1;stroke:#000000;stroke-width:0.52916664;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4553"
width="11.626225"
height="9.0871639"
x="91.456924"
y="44.917343" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="95.248917"
y="51.714432"
id="text4566"><tspan
sodipodi:role="line"
id="tspan4564"
x="95.248917"
y="51.714432"
style="font-size:7.05555534px;stroke-width:0.26458332">E</tspan></text>
<rect
style="opacity:1;fill:#fff6d5;fill-opacity:1;stroke:#000000;stroke-width:0.52916664;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4553-2"
width="11.626225"
height="9.0871639"
x="103.08315"
y="44.917343" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="106.99326"
y="51.714439"
id="text4566-3"><tspan
sodipodi:role="line"
id="tspan4564-3"
x="106.99326"
y="51.714439"
style="font-size:7.05555534px;stroke-width:0.26458332">l</tspan></text>
<rect
style="opacity:1;fill:#fff6d5;fill-opacity:1;stroke:#000000;stroke-width:0.52916664;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4553-2-8"
width="11.626225"
height="9.0871639"
x="114.70938"
y="44.917343" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="118.61949"
y="51.714439"
id="text4566-3-5"><tspan
sodipodi:role="line"
id="tspan4564-3-6"
x="118.61949"
y="51.714439"
style="font-size:7.05555534px;stroke-width:0.26458332">e</tspan></text>
<rect
style="opacity:1;fill:#fff6d5;fill-opacity:1;stroke:#000000;stroke-width:0.52916664;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4553-2-4"
width="11.626225"
height="9.0871639"
x="126.33562"
y="44.917343" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="130.24574"
y="51.714439"
id="text4566-3-6"><tspan
sodipodi:role="line"
id="tspan4564-3-7"
x="130.24574"
y="51.714439"
style="font-size:7.05555534px;stroke-width:0.26458332">p</tspan></text>
<rect
style="opacity:1;fill:#fff6d5;fill-opacity:1;stroke:#000000;stroke-width:0.52916664;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4553-2-8-4"
width="11.626225"
height="9.0871639"
x="137.96185"
y="44.917343" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="141.87195"
y="51.714439"
id="text4566-3-5-0"><tspan
sodipodi:role="line"
id="tspan4564-3-6-4"
x="141.87195"
y="51.714439"
style="font-size:7.05555534px;stroke-width:0.26458332">h</tspan></text>
<rect
style="opacity:1;fill:#fff6d5;fill-opacity:1;stroke:#000000;stroke-width:0.52916664;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4553-2-6"
width="11.626225"
height="9.0871639"
x="149.58807"
y="44.917343" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="153.49818"
y="51.714447"
id="text4566-3-8"><tspan
sodipodi:role="line"
id="tspan4564-3-0"
x="153.49818"
y="51.714447"
style="font-size:7.05555534px;stroke-width:0.26458332">a</tspan></text>
<rect
style="opacity:1;fill:#fff6d5;fill-opacity:1;stroke:#000000;stroke-width:0.52916664;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4553-2-8-5"
width="11.626225"
height="9.0871639"
x="161.21431"
y="44.917343" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="165.12442"
y="51.714447"
id="text4566-3-5-9"><tspan
sodipodi:role="line"
id="tspan4564-3-6-2"
x="165.12442"
y="51.714447"
style="font-size:7.05555534px;stroke-width:0.26458332">n</tspan></text>
<rect
style="opacity:1;fill:#fff6d5;fill-opacity:1;stroke:#000000;stroke-width:0.52916664;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4553-2-61"
width="11.626225"
height="9.0871639"
x="172.84053"
y="44.917343" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="176.75064"
y="51.714432"
id="text4566-3-7"><tspan
sodipodi:role="line"
id="tspan4564-3-9"
x="176.75064"
y="51.714432"
style="font-size:7.05555534px;stroke-width:0.26458332">t</tspan></text>
<rect
style="opacity:1;fill:#fff6d5;fill-opacity:1;stroke:#000000;stroke-width:0.52916664;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4553-2-8-57"
width="11.626225"
height="9.0871639"
x="184.46677"
y="44.917343" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="186.10507"
y="51.714432"
id="text4566-3-5-96"><tspan
sodipodi:role="line"
id="tspan4564-3-6-9"
x="186.10507"
y="51.714432"
style="font-size:7.05555534px;stroke-width:0.26458332">\0</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="52.359035"
y="45.97747"
id="text4566-0"><tspan
sodipodi:role="line"
id="tspan4564-37"
x="52.359035"
y="45.97747"
style="font-size:7.05555534px;stroke-width:0.26458332">8</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker5033)"
d="M 54.1309,53.481438 C 68.229368,51.744187 78.051525,49.806481 86.938236,49.539213"
id="path4709"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="39.555893"
y="36.946739"
id="text5223"><tspan
sodipodi:role="line"
id="tspan5221"
x="39.555893"
y="36.946739"
style="font-size:4.23333311px;stroke-width:0.26458332">String a</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="28.549953"
y="45.468987"
id="text5223-0"><tspan
sodipodi:role="line"
id="tspan5221-1"
x="28.549953"
y="45.468987"
style="font-size:4.23333311px;stroke-width:0.26458332">size</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888855px;line-height:1.25;font-family:Consolas;-inkscape-font-specification:'Consolas, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="28.480877"
y="55.038445"
id="text5223-0-0"><tspan
sodipodi:role="line"
id="tspan5221-1-1"
x="28.480877"
y="55.038445"
style="font-size:4.23333311px;stroke-width:0.26458332">data</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB