Ссылка (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.