initial commit

This commit is contained in:
nihonium 2022-09-01 16:37:41 +03:00
commit 2369f801af
Signed by: nihonium
GPG key ID: 0251623741027CFC
76 changed files with 4273 additions and 0 deletions

View file

@ -0,0 +1,60 @@
#include <iostream>
using std::cout, std::endl;
/*
В C++ вводится понятие нового типа под названием Ссылка
Ссылку можно рассматривать как новое имя для объекта.
Ссылку также можно рассматривать как удобный указатель, который автоматически разыменовывается
(На самом деле под капотом ссылка и является указателем)
Ссылка объявляется с помощью значка & после имени типа.
Не стоит путать & используемый при объявлении ссылки с & используемым для нахождения адреса переменной.
Это разные &
Пусть есть переменная a
int a = 10;
Давайте создадим указатель и ссылку на эту переменную и увеличим её на 1 с помощью указателя/ссылки
Используем указатель: Используем ссылку:
int* p = &a; int& r = a;
*p += 1; r += 1;
Ссылкой пользоваться удобно, так как:
1) При создании ссылки нам не нужно передавать ей адрес
Просто передаём ей саму переменную, а ссылка уже сама находит её адрес
2) Не нужно её разыменовывать, она всегда разыменовывается сама
*/
int main()
{
int a = 10;
int& r = a;
r += 1;
cout << a << endl;
}
/*
Задачи:
1) Используйте ссылку r, чтобы увеличить a в 2 раза
Проверьте, как изменилась a, напечатав её
2) Используйте ссылку r, чтобы присвоить a число 100
Проверьте, как изменилась a, напечатав её
3) Создайте переменную b типа float, равную 1.5
Создайте ссылку на b и используйте эту ссылку, чтобы возвести b в квадрат
*/

View file

@ -0,0 +1,39 @@
#include <iostream>
using std::cout, std::endl;
/*
Пусть у нас есть некоторый объект, например
int a = 10;
После того как мы создали ссылку на этот объект
int& r = a;
Все (почти) операции применяемые к ссылке r применяются на самом деле к объекту a
Как будто у одного объекта теперь два имени a и r
Поэтому можно сказать, что ссылка это новое имя для объекта
При этом изменить саму ссылку (например, чтобы она начала указывать на другое имя) нельзя
*/
int main()
{
int a = 10;
int& r = a;
r += 5; // Прибавим к a число 5
r *= 2; // Умножим a на 2
cout << r << endl; // Напечатаем a
cout << sizeof(r) << endl; // Напечатаем размер a
cout << &r << endl; // Напечатаем адрес a
}
/*
Задачи:
1) Чему будет равно значение a в конце этой программы
*/

View file

@ -0,0 +1,43 @@
#include <iostream>
using std::cout, std::endl;
/*
Пусть у нас есть некоторый объект, например
int a = 10;
После того как мы создали ссылку на этот объект
int& r = a;
Все (почти) операции применяемые к ссылке r применяются на самом деле к объекту a
Как будто у одного объекта теперь два имени a и r
Поэтому можно сказать, что ссылка это новое имя для объекта
При этом изменить саму ссылку (например, чтобы она начала указывать на другое имя) нельзя
*/
int main()
{
int a[5] = {10, 20, 30, 40, 50};
int& b = a[1];
b += 1;
for (int i = 0; i < 5; ++i)
{
cout << a[i] << " ";
}
cout << endl;
}
/*
Задачи:
1) Что будет содержать массив a в конце данной программы?
2) Создайте ссылку, которая будет указывать на последний элемент массива a
Используйте эту ссылку, чтобы умножить последний элемент массива на 2
Напечатайте этот массив
*/

View file

@ -0,0 +1,49 @@
#include <iostream>
using std::cout, std::endl;
/*
Несмотря на то, что ссылки и указатели во многом похожи, у них есть и много больших отличий.
(помимо разной инициализации и того, что ссылку не нужно постоянно разыменовывать)
Различие 1)
Указатель можно создать без инициализации вот так:
int* p;
В этом случае в p будет храниться произвольный адрес.
Разыменовывать такой указатель, не задав его значение адресом какого-либо объекта,
очень опасно, это может привести к сложновыявляемым ошибкам.
Ссылку нельзя создать без инициализации, то есть так нельзя:
int& r;
При создании ссылки нужно указать на что она будет указывать
Различие 2)
Указатель можно приравнять нулевому значению
В C++ вводится специальное нулевое значение для указателя nullptr
Вместо NULL, который был просто равен числу 0. В C++ лучше использовать nullptr
Разыменование нулевого указателя также приведёт к ошибке.
Ссылку нельзя присвоить никакому нулевому значению
*/
int main()
{
}
/*
Задачи:
1) Попробуйте создать:
a) Указатель без инициализации
б) Ссылку без инициализации
в) Указатель, равнуй нулевому значению nullptr
г) Ссылку, равную нулю
Скомпилируется ли программа в этих 4-х случаях?
*/

View file

@ -0,0 +1,52 @@
#include <iostream>
using std::cout, std::endl;
/*
Несмотря на то, что ссылки и указатели во многом похожи, у них есть и много больших отличий.
(помимо разной инициализации и того, что ссылку не нужно постоянно разыменовывать)
Различие 3) Указатель можно переприсвоить. Если указатель сначала указывал в одно место,
например, на переменную a, то можно просто написать
p = &b;
и указатель станет указывать на переменную b.
Со ссылками такое не пройдёт, они всегда указывают на тот объект, который был указан при создании ссылки
При попытке изменить это и написать что-то вроде
r = b;
ссылка автоматически разыменуется и присваивание произойдёт к тому, на что указывала ссылка
*/
int main()
{
int a = 10;
int b = 20;
int* p = &a;
*p += 1;
p = &b;
*p += 1;
cout << a << " " << b << endl;
int& r = a;
r += 1;
r = b;
r += 1;
cout << a << " " << b << endl;
}
/*
Задачи:
1) Попробуйте понять, что напечатает программа без её запуска
2) Запустите программу, проверьте ваши догадки и объясните результат
*/

View file

@ -0,0 +1,44 @@
#include <iostream>
using std::cout, std::endl;
/*
Несмотря на то, что ссылки и указатели во многом похожи, у них есть и много больших отличий.
(помимо разной инициализации и того, что ссылку не нужно постоянно разыменовывать)
Различие 4)
Арифметика указателей.
К указателю можно прибавлять/отнимать целые числа. Можно вычесть 2 указателя.
Можно применить [] к указателю. При всём этом, желательно, чтобы указатель указывал на элемент массива.
Неаккуратное использование арифметики указателей может привести к ошибкам.
Например, можно прибавить к указателю не то число и выйти за пределы массива.
Ничего такого со ссылками сделать нельзя.
При попытке прибавить к ссылке число, оно прибавится к той переменной, на которую указывает ссылка.
Так как ссылка автоматически разыменуется.
*/
int main()
{
int a[5] = {10, 20, 30, 40, 50};
int* p = &a[0];
p += 1; // Увеличиваем указатель
*p += 1; // Увеличиваем то, на что указывает указатель
int& r = a[0];
r += 1; // Увеличиваем то, на что указывает ссылка (она автоматически разыменовывается)
}
/*
Задачи:
1) Чему будет равен массив a в конце данной программы
*/

View file

@ -0,0 +1,46 @@
#include <iostream>
using std::cout, std::endl;
/*
Несмотря на то, что ссылки и указатели во многом похожи, у них есть и много больших отличий.
Различие 5)
Ссылки это не совсем обычный объект, некоторые операции с ними запрещены:
Ссылку нельзя сделать элементом массива
Нельзя получить адрес ссылки (если применим & то вернётся адрес того объекта на который указывет ссылка)
Нельзя создать укатель на ссылку
Нельзя создать ссылку на ссылку
*/
int main()
{
int x = 1;
int y = 2;
int z = 2;
int& a[3] = {x, y, z}; // Ошибка, создать массив из ссылок не получится
}
/*
Задачи:
1) Можно ли инициализировать ссылку на int простым числом вот так:
int& r = 5;
2) Ссылку на ссылку создать нельзя, но код ниже почему-то работает.
Объясните почему этот код работает
int a = 10;
int& r1 = a;
int& r2 = r1;
r2 += 1;
*/

View file

@ -0,0 +1,69 @@
#include <iostream>
using std::cout, std::endl;
/*
В 90% случаях ссылки используются для того чтобы передать что либо в функцию
Часто нам хочется передать переменную в функцию и изменить её там (внутри функции)
Это можно делать и с помощью указателей, но с помощью ссылок это делать гораздо удобней
Рассмотрим два эквивалентных участка кода:
Передаём по указателю: Передаём по ссылке:
void sqr(int* p) void sqr(int& r)
{ {
*p = *p * *p; r = r * r;
} }
int main() int main()
{ {
int a = 5; int a = 5;
sqr(&a); sqr(a);
cout << a << endl; cout << a << endl;
} }
Обратите внимание на 2 вещи:
1) Ссылку не нужно разыменовывать внутри функции, это происходит автоматически
2) При передаче в функцию, не нужно передавать адрес переменной
Нужно передать саму переменную, компилятор сам вычислит её адрес
При этом копирования объекта a в функцию не происходит,
ссылки работают также быстро как и указатели
*/
void sqr(int& r)
{
r = r * r;
}
int main()
{
int a = 5;
sqr(a);
cout << a << endl;
}
/*
Задачи:
1) Напишите функцию void inc(int& x), которая должна принимать объект типа int
и увеличивать его на 1
Вызовите эту функцию из main и протестируйте её работу
2) Напишите функцию void normalize(float& x, float& y), которая должна принимать
2 объекта типа float и нормализировать их. То есть делить их на некоторое число,
так чтобы было x*x + y*y == 1
Для этого x и y нужно разделить на sqrt(x*x + y*y)
3) Можно ли передать в функцию sqr не переменную, а число?
То есть, можно ли написать так:
sqr(5)
*/

View file

@ -0,0 +1,50 @@
#include <iostream>
#include <cmath>
using std::cout, std::endl;
void inc(int& x)
{
x += 1;
}
void normalize(float& x, float& y)
{
float norm = std::sqrt(x * x + y * y);
x /= norm;
y /= norm;
}
int main()
{
int a = 5;
inc(a);
cout << a << endl;
float x = 9, y = 6;
normalize(x, y);
cout << x << " " << y << endl;
}
/*
Задачи:
1) Напишите функцию void inc(int& x), которая должна принимать объект типа int
и увеличивать его на 1
Вызовите эту функцию из main и протестируйте её работу
2) Напишите функцию void normalize(float& x, float& y), которая должна принимать
2 объекта типа float и нормализировать их. То есть делить их на некоторое число,
так чтобы было x*x + y*y == 1
Для этого x и y нужно разделить на sqrt(x*x + y*y)
3) Можно ли передать в функцию sqr не переменную, а число?
То есть, можно ли написать так:
sqr(5)
*/

View file

@ -0,0 +1,81 @@
#include <iostream>
using std::cout, std::endl;
/*
Чаще всего по ссылке в функцию передаются объекты структур и классов
Даже если мы не хотим менять объект внутри функции, мы всё-равно можем
захотеть передать его по ссылке, так как передача по ссылке не копирует объект,
следовательно это гораздо более эффективно
В этом случае передаём по константной ссылке (по аналогии с константным указателем)
*/
struct Book
{
char title[100];
int pages;
float price;
};
void increasePrice(Book& b, float value)
{
b.price += value;
}
void printBook(Book& b)
{
cout << b.title << ", pages = " << b.pages << ", price = " << b.price << endl;
}
int main()
{
Book b = {"War and Peace", 1200, 900};
printBook(b);
increasePrice(b, 100);
printBook(b);
}
/*
// Тот же самый код с использованием указателей выглядел бы так:
void increasePrice(Book* b, float value)
{
b->price += value;
}
void printBook(const Book* b)
{
cout << b->title << ", pages = " << b->pages << ", price = " << b->price << endl;
}
int main()
{
Book b = {"War and Peace", 1200, 900};
printBook(&b);
increasePrice(b, 100);
printBook(&b);
}
*/
/*
Задачи:
1) Напишите функцию addPage, которая бы принимала структуру Book по ссылке
и увеличивала количество страниц на 1
Протестируйте эту функцию в main
2) Напишите функцию changeFirstLetter, которая бы принимала структуру Book по ссылке
и изменяла первую букву в названии на букву A
*/

View file

@ -0,0 +1,41 @@
#include <iostream>
using std::cout, std::endl;
struct Book
{
char title[100];
int pages;
float price;
};
void addPage(Book& b)
{
b.pages++;
}
void changeFirstLetter(Book& b)
{
b.title[0] = 'A';
}
void printBook(const Book& b)
{
cout << b.title << ", pages = " << b.pages << ", price = " << b.price << endl;
}
int main()
{
Book b = {"War and Peace", 1200, 900};
printBook(b);
addPage(b);
printBook(b);
changeFirstLetter(b);
printBook(b);
}

View file

@ -0,0 +1,73 @@
#include <iostream>
using std::cout, std::endl;
/*
Ссылки, как и указатели используются для передачи объектов в функции
Рассмотрим три функции
incByValue - принимаем объект по значению
В этом случае объект копируется и функция работает с копией
incByPointer - принимаем объект по адресу
В этом случае внутри функции создаётся указатель, и в этот указатель
мы передаём адрес нашего объекта
incByReference - принимаем объект по ссылке
В этом случае происходит всё примерно то же самое, что и в случае incByPointer
Только с гораздо более приятным синтаксисом
*/
void incByValue(int a)
{
a += 1;
}
void incByPointer(int* p)
{
*p += 1;
}
void incByReference(int& a)
{
a += 1;
}
int main()
{
int a = 10;
cout << "1) Initial a = " << a << endl
incByValue(a);
cout << "2) After incByValue a = " << a << endl;
incByPointer(&a);
cout << "3) After incByPointer a = " << a << endl
incByReference(a);
cout << "4) After incByReference a = " << a << endl;
}
/*
Задание:
1) Напишите функции:
cubeByPointer(int* p) - принимет число по указателю и возводит это число в куб
cubeByReference(int& a) - принимет число по ссылке и возводит это число в куб
Протестируйте эти функции в main
2) Написать функции:
swapByPointer(int* pa, int* pb) - принимает 2 числа по указателю и обменивает их значения
swap(int& pa, int& pb) - принимает 2 числа по ссылке и обменивает их значения
Протестируйте эти функции в main
*/

View file

@ -0,0 +1,54 @@
#include <iostream>
using std::cout, std::endl;
void cubeByPointer(int* p)
{
*p = *p * *p * *p;
}
void cubeByReference(int& a)
{
a = a * a * a;
}
void swapByPointer(int* pa, int* pb)
{
int temp = *pa;
*pa = *pb;
*pb = temp;
}
void swapByReference(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int a = 5;
cubeByPointer(&a);
cout << a << endl;
a = 5;
cubeByReference(a);
cout << a << endl;
int x = 10, y = 20;
swapByPointer(&x, &y);
cout << x << " " << y << endl;
x = 10;
y = 20;
swapByReference(x, y);
cout << x << " " << y << endl;
}

View file

@ -0,0 +1,97 @@
#include <iostream>
#include <cmath>
using std::cout, std::endl;
/*
Константные ссылки можно создать, используя ключевое слово const
int a = 10;
const int& r = a;
Это означает, что a нельзя будет изменить по этой ссылке
То есть поменять a, используя ссылку r будет нельзя:
r += 1; // Ошибка!
a += 1; // OK
Важным неочевидным отличием константных ссылок от обычных ссылок является то, что обычные ссылки
можно инициализировать только объектами, которые уже храняться в памяти (например, переменными).
int& r1 = a; // OK
int& r2 = 5; // Ошибка
Константные ссылки можно инициализировать чем угодно (нужно только чтобы тип совпадал)
const int& cr1 = a; // OK
const int& cr2 = 5; // OK
*/
int main()
{
int a = 10;
const int& r = a;
int& r1 = 20; // Это не будет компилироваться
const int& r2 = 20 // Тут всё ОК
}
/*
Задание:
1) Можно ли инициализировать ссылку таким образом?
float& r = std::sqrt(2);
Можно ли инициализировать константную ссылку таким образом?
const float& r = std::sqrt(2);
2) Пусть есть функция:
void printAgeV(int x)
{
cout << "My age is " << x << " years" << endl;
}
Можно ли вызвать её так?
int a = 10;
printAgeV(a)
Можно ли вызвать её так?
printAgeV(20)
3) Пусть есть функция:
void printAgeR(int& x)
{
cout << "My age is " << x << " years" << endl;
}
Можно ли вызвать её так?
int a = 10;
printAgeR(a)
Можно ли вызвать её так?
printAgeR(20)
4) Пусть есть функция:
void printAgeCR(const int& x)
{
cout << "My age is " << x << " years" << endl;
}
Можно ли вызвать её так?
int a = 10;
printAgeCR(a)
Можно ли вызвать её так?
printAgeCR(20)
*/

View file

@ -0,0 +1,63 @@
#include <iostream>
#include <cmath>
using std::cout, std::endl;
/*
Константные ссылки нужны прежде всего, чтобы передавать большие объекты в функции, внутри которых они не должны меняться
Рассмотрим структуру Book, чей размер более 100 байт
1) При передаче такой структуры в функцию по значению, как это происходит в функции printBookV,
вся структура будет копироваться внутрь функции и это очень медленно. Так делать не стоит.
2) При передаче такой структуры в функцию по обычной ссылке, как это происходит в функции printBookR, структура не копируется
На самом деле, под капотом внутрь функции копируется адрес структуры.
Адрес намного меньше самой структуры, поэтому это копирование работает намного быстрее.
Но возникает проблема с тем, что структура внутри такой функции может поменяться.
В реальной ситуации, если функций много и они большие, уследить за тем меняется ли аргументы внутри функций становится проблематично.
3) При передаче такой структуры в функцию по константной ссылке, как это происходит в функции printBookCR, структура не копируется.
Плюс к этому мы можем быть уверены, что внутри функции наша структура не поменяется и это сильно упрощает понимание программы.
*/
struct Book
{
char title[100];
int pages;
float price;
};
void printBookV(Book b)
{
cout << b.title << ", pages = " << b.pages << ", price = " << b.price << endl;
}
void printBookR(Book& b)
{
cout << b.title << ", pages = " << b.pages << ", price = " << b.price << endl;
}
void printBookCR(const Book& b)
{
cout << b.title << ", pages = " << b.pages << ", price = " << b.price << endl;
}
int main()
{
Book b = {"War and Peace", 1200, 900};
printBookV(b);
printBookR(b);
printBookCR(b);
}

View file

@ -0,0 +1,50 @@
#include <iostream>
using std::cout, std::endl;
/*
Ссылки можно и возвращать из функции
Например, функция get возвращает ссылку на глобальную переменную x
*/
int x = 10;
int& get()
{
return x;
}
int main()
{
cout << x << endl;
cout << get() << endl;
get() += 1;
cout << x << endl;
}
/*
С указателями аналогичный код выглядел бы так:
int x = 10;
int* get()
{
return &x;
}
int main()
{
cout << x << endl;
cout << *get() << endl;
*get() += 1;
cout << x << endl;
}
*/

View file

@ -0,0 +1,51 @@
#include <iostream>
using std::cout, std::endl;
/*
Ссылки можно и возвращать из функции
Но при этом нужно следить за тем, чтобы функция не вернула ссылку на локальную переменную, как это происходит в данном примере.
После завершения функции, переменная x удалится, так как она была определена внутри функции.
В результате, внутри функции main мы попробуем доступиться к области памяти, в которой раньше лежала переменная x.
Это приведёт к ошибке
*/
int& get()
{
int x = 10;
return x;
}
int main()
{
cout << get() << endl;
get() += 1;
cout << get() << endl;
}
/*
Аналогичная ошибка может произойти и при работе с обычными указателями:
int* get()
{
int x = 10;
return &x;
}
int main()
{
cout << *get() << endl;
*get() += 1;
cout << *get() << endl;
}
*/

View file

@ -0,0 +1,39 @@
#include <iostream>
using std::cout, std::endl;
/*
Ссылки можно и возвращать из функции
Например, функция increase принимает ссылку, увеличивет то, на что указывает эта ссылка на 1
и возвращает эту ссылку
При этом никакого копирование самой переменной a в функцию и из функции не происходит
*/
int& increase(int& r)
{
r += 1;
return r;
}
int main()
{
int a = 10;
cout << "1) a = " << a << endl;
increase(a);
cout << "2) a = " << a << endl;
increase(a) += 7;
cout << "3) a = " << a << endl;
increase(increase(increase(a)));
cout << "4) a = " << a << endl;
}
/*
Что напечатает данная программа?
*/

View file

@ -0,0 +1,17 @@
#include <iostream>
using std::cout, std::endl;
/*
Задача:
Напишите функцию multiplyBy2, которая принимает число по ссылке и увеличивает его в 2 раза
Вызовите эту функцию из функции main
*/
int main()
{
}

View file

@ -0,0 +1,16 @@
#include <iostream>
using std::cout, std::endl;
void multiplyBy2(int& a)
{
a *= 2;
}
int main()
{
int x = 10;
multiplyBy2(x);
cout << x << endl;
}

View file

@ -0,0 +1,22 @@
#include <iostream>
using std::cout, std::endl;
/*
Задача:
Напишите функцию sumAndSave, которая должна принимать 3 аргумента
Первые два аргумента по значению
Третий аргумент по ссылке
Функция должна складывать первые 2 аргумента и сохранять результат по третей ссылке
Вызовите эту функцию из функции main
*/
int main()
{
}

View file

@ -0,0 +1,36 @@
#include <iostream>
using std::cout, std::endl;
/*
Задача:
Напишите функцию sumAndSave, которая должна принимать 3 аргумента
Первые два аргумента по значению
Третий аргумент по ссылке
Функция должна складывать первые 2 аргумента и сохранять результат по третей ссылке
Вызовите эту функцию из функции main
*/
void sumAndSave(int a, int b, int& c)
{
c = a + b;
}
int main()
{
int x = 10, y = 20;
int z;
sumAndSave(x, y, z);
cout << z << endl;
sumAndSave(70, 80, z);
cout << z << endl;
}

View file

@ -0,0 +1,28 @@
#include <iostream>
using std::cout, std::endl;
/*
Задача:
Напишите функцию
void calculateLetters(char str[], int& numLetters)
Которая будет принимать на вход строку и считать количество строчных букв в этой строке
Строчные буквы - это символы от 'a' и до 'z'
Например, вызов calculateLetters("ab54AB,gd1:e", x) должен сохранить число 5 в переменную x
*/
int main()
{
int x;
calculateLetters("ab54AB,gd1:e", x);
cout << x << endl;
}

View file

@ -0,0 +1,23 @@
#include <iostream>
using std::cout, std::endl;
void calculateLetters(char str[], int& numLetters)
{
numLetters = 0;
for (int i = 0; str[i] != '\0'; ++i)
{
if (str[i] >= 'a' && str[i] <= 'z')
numLetters += 1;
}
}
int main()
{
int x;
calculateLetters("ab54AB,gd1:e", x);
cout << x << endl;
}