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