предотвращает случайную модификацию аргумента в функции
print
. Кроме объявления аргумента, все остальное без изменений. Правда, теперь все операции будут производиться не над копией, а над самим аргументом, полученным по ссылке. Такие аргументы называются ссылками, потому что они ссылаются на объекты, определенные вне функции. Вызов функции
Константная ссылка обладает полезным свойством: она не позволяет случайно изменить объект, на который ссылается. Например, если мы сделаем глупую ошибку и попытаемся присвоить элементу вектора, полученного извне функции
print
, какое-то значение, то компилятор сразу выдаст сообщение об этом.
void print(const vector<double>& v) // передача по константной ссылке
{
// ...
v[i] = 7; // ошибка: v — константа (т.е. не может изменяться)
// ...
}
Передача аргументов по константной ссылке — очень полезный и распространенный механизм. Вернемся к функции
my_find
(см. раздел 8.5.1), выполняющей поиск строки в векторе строк. Передача по значению здесь была бы слишком неэффективной.
int my_find(vector<string> vs, string s); // передача по значению:
// копия
Если вектор содержит тысячи строк, то поиск занял бы заметный объем времени даже на быстром компьютере. Итак, мы можем улучшить функцию
my_find
, передавая ее аргументы по константной ссылке.
// передача по ссылке: без копирования, доступ только для чтения
int my_find(const vector<string>& vs, const string& s);
8.5.5. Передача параметров по ссылке
А что делать, если мы хотим, чтобы функция модифицировала свои аргументы? Иногда это очень нужно. Например, мы можем написать функцию
init
, которая должна присваивать начальные значения элементам вектора.
void init(vector<double>& v) // передача по ссылке
изменяла вектор, являющийся ее аргументом. Иначе говоря, мы хотим не копировать его (т.е. передавать по значению), не объявлять с помощью константной ссылки (т.е. передавать по константной ссылке), а просто передать обычную ссылку на вектор.
Рассмотрим ссылки более подробно. Ссылка — это конструкция, позволяющая пользователю объявлять новое имя объекта. Например,
int&
— это ссылка на переменную типа
int
. Это позволяет нам написать следующий код:
int i = 7;
int& r = i; // r — ссылка на переменную i
r = 9; // переменная i становится равной 9
i = 10;
cout << r << ' ' << i << '\n'; // вывод: 10 10
Иначе говоря, любая операция над переменной
r
на самом деле означает операцию над переменной
i
. Ссылки позволяют уменьшить размер выражений. Рассмотрим следующий пример:
vector< vector<double> > v; // вектор векторов чисел типа double
Допустим, нам необходимо сослаться на некоторый элемент
v[f(x)][g(y)]
несколько раз. Очевидно, что выражение
v[f(x)][g(y)]
выглядит слишком громоздко и повторять его несколько раз неудобно. Если бы оно было просто значением, то мы могли бы написать следующий код:
double val = v[f(x)][g(y)]; // val — значение элемента v[f(x)][g(y)]
В таком случае можно было бы повторно использовать переменную
val
. А что, если нам нужно и читать элемент
v[f(x)][g(y)]
, и присваивать ему значения
v[f(x)][g(y)]
? В этом случае может пригодиться ссылка.
double& var = v[f(x)][g(y)]; // var — ссылка на элемент v[f(x)][g(y)]
Теперь можем как считывать, так и изменять элемент
v[f(x)][g(y)]
с помощью ссылки
var
. Рассмотрим пример.
var = var/2+sqrt(var);
Это ключевое свойство ссылок — оно может служить “аббревиатурой” объекта и использоваться как удобный аргумент. Рассмотрим пример.
// передача по ссылке (функция ссылается на полученную переменную)