Ссылка (reference) по существу является неявным указателем. Она используется как другое имя объекта.
Если в языке C требовалось предоставить функции возможность изменять значения своих переменных-аргументов, приходилось явным образом объявлять параметры как указатели. Тогда функция могла обрабатывать значения переданных переменных, используя оператор *. Например, следующая программа реализует функцию swap(), обменивающую между собой значения двух аргументов целого типа:
#include <iostream.h>
void swap(int *a, int *b)
{
int t;
t = *a; *a = *b; *b = t;
}
int main()
{
int x=99, y=88;
cout << x << " " << у << "\n";
swap(&x, &y); // обмен значений
cout << x << " " << у << "\n";
return 0;
}
При вызове функции swap() перед подставляемыми в нее переменными должен стоять оператор взятия адреса &. Именно так реализуется передача по адресу в С. Хотя C++ позволяет подобный синтаксис, он поддерживает и более ясный, более прозрачный метод передачи по ссылке, используя параметр типа ссылки (reference parameter). В таком случае при объявлении функции перед параметром ставится знак амперсанта &. В качестве примера рассмотрим функцию:
void f(int &i)
{
i = rand(); // изменяет переданный аргумент
}
Обратим внимание, что инструкция
i = rand();
не использует оператор *. При объявлении параметра-ссылки компилятор C++ знает, что это неявный указатель, и обрабатывает его соответствующим образом. Всякий раз при вызове функции f() ей автоматически передается адрес аргументов. Например, в следующем фрагменте кода
int i;
f(i); // i получает случайное значение
printf("%d", i);
адрес переменной i, а не значение этой переменной, передается функции f(). Благодаря этому функция f() может модифицировать значение переменной i. Обратим внимание, что при вызове функции f() нет необходимости ставить оператор взятия адреса & перед переменной i. Компилятор автоматически передает функции адрес переменной i.
Для того чтобы проиллюстрировать использование параметров ссылочного типа, мы перепишем функцию swap() с использованием ссылок. Обратим внимание, каким образом функция swap() объявляется и вызывается:
#include <iostream.h>
void swap(int &a, int &b)
{
int t;
t = a; a = b; b = t; }
int main()
{
int x=99, у=88;
cout << х << " " << у << "\n";
swap(x, у); // обмен значений
cout << х << " " << у << "\n";
return 0;
}
Еще раз отметим, что определив а и b как параметры ссылочного типа, мы не нуждаемся в использовании оператора взятия адреса & при вызове функции swap(), а также не используем оператор * внутри функции swap(). На применение переменных ссылочного типа имеется ряд ограничений:
■ Нельзя получить адрес переменной ссылочного типа.
■ Нельзя создать массив ссылок.
■ Нельзя создать указатель на ссылку.
■ Ссылки на битовые поля не допускаются.
Как объяснялось в прошлой главе, когда объект передается функции как аргумент, создается копия объекта. Более того, при создании этой копии конструктор объекта не вызывается. Вместо этого делается точная копия объекта. Однако по окончании выполнения функции вызывается деструктор копии объекта. Если по каким-либо причинам нежелательно, чтобы создавалась копия объекта или чтобы вызывался деструктор, то следует передать объект в функцию по ссылке. При передаче по ссылке копия объекта не создается. Это также означает, что объект-параметр не будет уничтожаться при выходе из функции и соответственно не будет вызван деструктор. В качестве примера рассмотрим следующую программу:
#include <iostream.h>
class C
{
int id;
public:
int i;
C(int i) { cout << "Constructing " << i << "\n"; id = i; }
~C() { cout << "Destructing " << id << "\n"; }
void neg(C &p) { p.i = -p.i;}
};
int main()
{
C A(1);
A.i = 10;
A.neg(A);
cout << A.i << "\n";
return 0;
}
Программа выдает следующие результаты:
Constructing 1
-10
Destructing 1
Как можно видеть, деструктор объекта A вызывался только один раз.
При передаче объекта в функцию по ссылке действие над объектом внутри функции модифицирует этот объект. И эти изменения сохраняют силу после завершения работы функции.