14.3  Передача ссылки в качестве параметра функции

Несомненно, наиболее важное применение ссылки – это передача ее в качестве параметра функции. Чтобы проиллюстрировать, что такое параметр-ссылка и как он работает, опишем программу, в которой параметром является указатель (а не ссылка):

#include <iostream>

using namespace std;

void f(int *n)

{

*n = 100; //занесение числа 100 в аргумент, на который указывает указатель n

}

int main (void)

{

int i = 0;

f(&i); //Операция взятия адреса

cout<<"Value of i: "<<i<<endl;

return 0;

}

Здесь функция f() загружает целое значение 100 по адресу, который обозначается указателем n. В данной программе функция f() вызывается из функции main() с адресом переменной i. Таким образом, после выполнения функции f() переменная i будет содержать число 100.

В этой программе показано, как использовать указатель для реализации механизма передачи параметра посредством вызова по ссылке (call by reference). В программах языка С такой механизм является единственным способом добиться вызова функции по ссылке. Однако в языке C++ с помощью параметра-ссылки можно полностью автоматизировать весь процесс. Чтобы узнать, как это сделать, изменим предыдущую программу. В ее новой версии используется параметр-ссылка:

#include <iostream>

using namespace std;

// В функции f() используется параметр-ссылка (знак * не требуется)

void f(int &n)

{

n = 100; // Занесение числа 100 в аргумент, используемый при вызове функции f()

}

int main()

{

int i = 0;

f(i);

cout<<"Value of i: "<<i<<endl;

return 0;

}

Для объявления параметра-ссылки перед именем переменной ставится знак амперсанда (&). Таким образом, переменная n объявляется параметром функции f(). Теперь, поскольку переменная n является ссылкой, больше не нужно и даже неверно указывать оператор *. Вместо него всякий раз, когда переменная n упоминается внутри функции f(), она автоматически трактуется как указатель на аргумент, используемый при вызове функции f(). Это значит, что инструкция n=100;, фактически помещает число 100 в переменную, используемую при вызове функции f(), каковой, в данном случае, является переменная i.  Далее, при вызове функции f() перед аргументом не нужно ставить знак &. Вместо этого, поскольку функция f() объявлена как получающая параметр-ссылку, ей автоматически передается адрес аргумента.

При использовании параметра-ссылки компилятор автоматически передает функции адрес переменной, указанной в качестве аргумента. Нет необходимости, и не допускается, получать адрес аргумента с помощью знака & (операции взятия адреса). Более того, внутри функции компилятор автоматически использует переменную, на которую указывает параметр-ссылка. Нет необходимости (и опять не допускается) ставить знак *. Таким образом, параметр-ссылка полностью автоматизирует механизм передачи параметра посредством вызова функции по ссылке.

Адрес, на который указывает ссылка, изменить нельзя. Например, если в предыдущей программе инструкция n++; находилась бы внутри функции f(), ссылка n по-прежнему указывала бы на переменную i в функции main(). Вместо инкрементирования адреса, на который указывает ссылка n, эта инструкция инкрементирует значение переменной (в данном случае это переменная i).

Параметры-ссылки имеют несколько преимуществ по сравнению с аналогичными альтернативными параметрами-указателями. Во-первых, нет необходимости получать и передавать в функцию адрес аргумента. При использовании параметра-ссылки адрес передается автоматически. Во-вторых, параметры-ссылки предлагают более понятный и элегантный интерфейс, чем неуклюжий механизм указателей. В-третьих, при передаче объекта функции через ссылку копия объекта не создается. Это исключает потери времени при копировании объекта.

Классическим примером передачи аргументов по ссылке является функция, меняющая местами значения двух своих аргументов. В данном примере в функции swap() ссылки используются для того, чтобы поменять местами два ее целых аргумента:

#include <iostream>

using namespace std;

//Функция обмена

void swap(int &x, int &y)

{

int t;

t=x;

x=y;

y=t;

}

int main()

{

int i,j;

i=10;

j=19;

cout<< "To swap: "<<"i= "<<i<<", "<<"j= "<<j<<endl;

swap(i, j);

cout<<"After swap: "<<"i= "<<i<<", "<<"j= "<<j<<endl;

return 0;

}

В  следующей программе с помощью  функции  round() округляется значение типа double. Округляемое значение передаётся по ссылке:

#include <iostream>

#include <cmath>

using namespace std;

void round(double &num)

{

double frac; double val;

frac = modf(num, &val); // Разложение num на целую и дробную части

if(frac < 0.5)

{

num = val;

}

else

{

num = val +1.0;

}

}

int main()

{

double i = 100.4;

cout<<i<<", round: ";

round(i);

cout<<i<<endl;

i = 10.9;

cout<<i<<", round: ";

round(i);

cout<<i<<endl;

return 0;

}

В функции round() для разложения числа на целую и дробную части указана редко используемая функция modf(). Возвращаемым значением этой функции является дробная часть, целая часть помещается в переменную, на которую указывает второй параметр функции modf().