seminar2
This commit is contained in:
parent
46d1c64684
commit
ab6732eded
98 changed files with 10319 additions and 0 deletions
34
seminar02_encapsulation/classroom_tasks/code/0book/00oop.cpp
Normal file
34
seminar02_encapsulation/classroom_tasks/code/0book/00oop.cpp
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Объектно-ориентированное программирование (ООП) основано на представлении программы в виде совокупности взаимодействующих объектов
|
||||
|
||||
Основные принципы ООП: абстракция, инкапсуляция, наследование и полиморфизм
|
||||
|
||||
|
||||
Абстракция: использование только тех характеристик объекта, которые с достаточной точностью представляют его в данной системе.
|
||||
|
||||
В каком-то смысле обычные структуры из языка C являются примером абстракции
|
||||
|
||||
struct book
|
||||
{
|
||||
char title[100];
|
||||
float price;
|
||||
int pages;
|
||||
};
|
||||
|
||||
Для описание книги в коде мы используем лишь некоторое её характеристики, достаточные для нашей задачи
|
||||
|
||||
|
||||
|
||||
Инкапсуляция: связывание данных некоторого абстрактного объекта и функций для работы с ним
|
||||
Тесно связано с инкапсуляцией такое понятие как сокрытие
|
||||
|
||||
Сокрытие: разделение данных и функций абстрактного объекта на открытые (видимые извне) и скрытые (видимые только внутри самого объекта)
|
||||
|
||||
|
||||
|
||||
Наследование и Полиморфизм будут пройдены позже в курсе
|
||||
*/
|
||||
|
||||
|
||||
|
||||
int main(){}
|
||||
54
seminar02_encapsulation/classroom_tasks/code/0book/01book.c
Normal file
54
seminar02_encapsulation/classroom_tasks/code/0book/01book.c
Normal 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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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 из константного метода?
|
||||
|
||||
*/
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
Конструктор, который создаёт объект, используя объект такого-же типа, называется конструктором копирования.
|
||||
*/
|
||||
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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-ю разными способами
|
||||
|
||||
*/
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
56
seminar02_encapsulation/classroom_tasks/code/0book/10m.cpp
Normal file
56
seminar02_encapsulation/classroom_tasks/code/0book/10m.cpp
Normal 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();
|
||||
}
|
||||
|
|
@ -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,
|
||||
которая будет принимать точку по указателю и делить координаты точки на норму точки
|
||||
Эта функция не должна ничего возвращать
|
||||
*/
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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?
|
||||
*/
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
*/
|
||||
|
|
@ -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() {}
|
||||
|
||||
|
|
@ -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;
|
||||
*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Задача:
|
||||
|
||||
Попробуйте понять без запуска, что напечатает данная программа
|
||||
*/
|
||||
|
|
@ -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";
|
||||
}
|
||||
*/
|
||||
|
|
@ -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
|
||||
*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
*/
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
Подсказка: можно использовать уже написанные операторы, чтобы реализовать этот
|
||||
*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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) Напишите оператор + , используя операторы = и +=
|
||||
|
||||
*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
Подсказка: можно использовать уже написанные сравнения
|
||||
Например, если вы написали оператор > , то очень просто написать оператор <= , используя оператор >
|
||||
|
||||
*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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>.
|
||||
|
||||
*/
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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) Напишите перегруженный оператор << для считывания строки с экрана.
|
||||
|
||||
*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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".
|
||||
*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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) Проверьте ваши догадки скомпилировав и запустив программу
|
||||
|
||||
*/
|
||||
|
|
@ -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) Проверьте ваши догадки, скомпилировав и запустив программу
|
||||
*/
|
||||
|
|
@ -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=)
|
||||
|
||||
*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
Какие методы будут вызваны теперь?
|
||||
|
||||
*/
|
||||
|
|
@ -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.
|
||||
|
||||
|
||||
Получается, что обычно передавать объекты в функции более эффективно по ссылке, а не по значению.
|
||||
|
||||
*/
|
||||
|
|
@ -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 чтобы она меняла первую букву переданной строки.
|
||||
*/
|
||||
|
|
@ -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';
|
||||
}
|
||||
|
||||
*/
|
||||
|
|
@ -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.
|
||||
|
||||
*/
|
||||
|
|
@ -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.
|
||||
|
||||
*/
|
||||
|
|
@ -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};
|
||||
}
|
||||
|
|
@ -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.
|
||||
*/
|
||||
|
|
@ -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.
|
||||
*/
|
||||
|
|
@ -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 такого конструктора нет и автоматически он не был создан. Поэтому эта строка приведёт к ошибке.
|
||||
|
||||
|
||||
|
||||
Но конструктор копирования, оператор присваивания и деструктор автоматически генерироваться будут.
|
||||
|
||||
|
||||
Задача:
|
||||
|
||||
Исправьте ошибку, написав конструктор по умолчанию самостоятельно.
|
||||
*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
Можно ли туда передать что-нибудь?
|
||||
|
||||
*/
|
||||
|
|
@ -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')
|
||||
*/
|
||||
|
|
@ -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));
|
||||
*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
}
|
||||
Reference in a new issue