initial commit
This commit is contained in:
commit
2369f801af
76 changed files with 4273 additions and 0 deletions
60
seminar01_overload/classroom_tasks/code/01ref/00ref.cpp
Normal file
60
seminar01_overload/classroom_tasks/code/01ref/00ref.cpp
Normal 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 в квадрат
|
||||
|
||||
*/
|
||||
39
seminar01_overload/classroom_tasks/code/01ref/01ref.cpp
Normal file
39
seminar01_overload/classroom_tasks/code/01ref/01ref.cpp
Normal 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 в конце этой программы
|
||||
|
||||
|
||||
*/
|
||||
43
seminar01_overload/classroom_tasks/code/01ref/02ref.cpp
Normal file
43
seminar01_overload/classroom_tasks/code/01ref/02ref.cpp
Normal 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
|
||||
Напечатайте этот массив
|
||||
|
||||
*/
|
||||
|
|
@ -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-х случаях?
|
||||
*/
|
||||
|
|
@ -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) Запустите программу, проверьте ваши догадки и объясните результат
|
||||
|
||||
*/
|
||||
|
|
@ -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 в конце данной программы
|
||||
|
||||
*/
|
||||
|
|
@ -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;
|
||||
|
||||
*/
|
||||
69
seminar01_overload/classroom_tasks/code/01ref/07func_ref.cpp
Normal file
69
seminar01_overload/classroom_tasks/code/01ref/07func_ref.cpp
Normal 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)
|
||||
|
||||
|
||||
*/
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
*/
|
||||
|
|
@ -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
|
||||
|
||||
*/
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
*/
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
*/
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Что напечатает данная программа?
|
||||
*/
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#include <iostream>
|
||||
using std::cout, std::endl;
|
||||
|
||||
|
||||
/*
|
||||
Задача:
|
||||
|
||||
Напишите функцию multiplyBy2, которая принимает число по ссылке и увеличивает его в 2 раза
|
||||
Вызовите эту функцию из функции main
|
||||
|
||||
*/
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
#include <iostream>
|
||||
using std::cout, std::endl;
|
||||
|
||||
|
||||
/*
|
||||
Задача:
|
||||
|
||||
Напишите функцию sumAndSave, которая должна принимать 3 аргумента
|
||||
Первые два аргумента по значению
|
||||
Третий аргумент по ссылке
|
||||
|
||||
Функция должна складывать первые 2 аргумента и сохранять результат по третей ссылке
|
||||
|
||||
Вызовите эту функцию из функции main
|
||||
|
||||
*/
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
Reference in a new issue