Ссылка (reference) представляет способ манипулировать каким-либо объектом. Фактически ссылка - это альтернативное имя для объекта. Для определения ссылки применяется знак амперсанда &:
int number {5};
int &refNumber {number};
В данном случае определена ссылка refNumber, которая ссылается на объект number. При этом в определении ссылки используется тот же тип, который представляет объект, на который ссылка ссылается, то есть в данном случае int.
При этом нельзя просто определить ссылку:
int &refNumber;
Она обязательно должна указывать на какой-нибудь объект.
Также нельзя присвоить ссылке литеральное значение, например, число:
int &refNumber = 10;
После установления ссылки мы можем через нее манипулировать самим объектом, на который она ссылается:
#include <iostream>
int main()
{
int number {5};
int &refNumber {number};
std::cout << refNumber << std::endl; // 5
refNumber = 20;
std::cout << number << std::endl; // 20
}
Изменения по ссылке неизбежно скажутся и на том объекте, на который ссылается ссылка.
Можно определять не только ссылки на переменные, но и ссылки на константы. Но при этом ссылка сама должна быть константной:
const int number{5};
const int &refNumber{number};
std::cout << refNumber << std::endl; // 5
//refNumber = 20; изменять значение по ссылке нельзя
Инициализировать неконстантную ссылку константным объектом мы не можем:
const int number {5};
int &refNumber {number}; // ошибка
Также константная ссылка может указывать и на обычную переменную, только значение по такой ссылке мы не сможем изменить:
int number {5};
const int &refNumber {number};
std::cout << refNumber << std::endl; // 5
//refNumber = 20; изменять значение по ссылке на константу нельзя
// но мы можем изменить саму переменную
number = 20;
std::cout << refNumber << std::endl; // 20
В данном случае несмотря на то, что мы не можем напрямую изменить значение по константной ссылке, тем не менее мы можем изменить сам объект, что приведет естественно к изменению константной ссылки.
В большинстве случае ссылки находят свое применение в функциях, когда надо передать значения по ссылке, что будет рассмотрено в последующих статьях. Однако есть и другие сценарии использования ссылок.
Например, в цикл for, который перебирает последовательность в стиле "for-each", мы не можем изменить значения перебираемых элементов. Например:
#include <iostream>
int main()
{
int numbers[] {1, 2, 3, 4, 5};
// меняем число на его квадрат
for (auto n : numbers)
{
n = n * n;
}
// смотрим результат
for (auto n : numbers)
{
std::cout << n << "\t";
}
std::cout << std::endl;
}
Здесь два цикла. В первом цикле при переборе массива помещаем каждый элемент массива в переменную n и изменяем ее значение на квадрат числа. Однако это приведет только к изменению этой переменной n, но никак не элементов перебираемого массива numbers. Элементы массива сохранят свои значения, что нам и покажет второй цикл, который выводит элементы на консоль:
1 2 3 4 5
Теперь используем ссылки:
#include <iostream>
int main()
{
int numbers[] {1, 2, 3, 4, 5};
// теперь n - ссылка на элемент массива
for (auto& n : numbers)
{
n = n * n;
}
// смотрим результат
for (auto n : numbers)
{
std::cout << n << "\t";
}
std::cout << std::endl;
}
Теперь в первом цикле переменная n представляет ссылку на элемент массива. Использование ссылки позволяет оптимизировать работу с циклом, поскольку теперь значение элемента массива не копируется в переменную n. И через ссылку можно изменить значение соответствующего элемента:
1 4 9 16 25
Иногда, наоборот, не нужно или даже нежелательно изменять элементы коллекции. В этом случае мы можем сделать ссылку константной:
#include <iostream>
int main()
{
int numbers[] {1, 2, 3, 4, 5};
// n - константная ссылка
for (const auto& n : numbers)
{
std::cout << n << "\t";
}
std::cout << std::endl;
}
Хотя здесь мы не можем изменять значение элемента, но также с помощью ссылок оптимизируем перебор массива, так как элементы массива не копируются в переменную n.