C++ Программирование в среде С++ Builder 5

         

Ссылки


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

Ссылки как псевдонимы переменных

Переменная-ссылка объявляется со знаком “&”, предшествующим ее имени; инициализирующим выражением должно быть имя переменной. Рассмотрите такой пример:

///////////////////////////////////////////////////////////////////

// Alias.cpp: Ссылка как псевдоним.

//

#include <stdio.h>

int main(void)

{

int iVar = 7777;

// Инициализация целой переменной.

int *iPtr = siVar;

// Инициализация указателя адресом

// iVar.



int &iRef = iVar;

// Инициализация ссылки как

// псевдонима iVar.

int *irPtr = &iRef;

// Инициализация указателя "адресом"

// ссылки.

printf("iVar = %d\n", iVar) ;

printf("*iPtr = %d\n", *iPtr);

printff'iRef = %d\n", iRef);

printf("*irPtr = %d\n", *irPtr) ;

printff'iPtr = %p, irPtr = %p\n", iPtr, irPtr);

return 0;

}

Первые четыре оператора printf выводят одно и то же число 7777. последний оператор печатает значения двух указателей, первый из которых инициализирован адресом переменной, а второй адресом ссылки. Эти адреса также оказываются одинаковыми. Как видите, после инициализации ссылки ее имя используется в точности как имя ассоциированной с ней переменной, т. е. как псевдоним.

Еще раз подчеркнем, что ссылку после инициализации нельзя изменить; все обращения к ссылке будут относиться на самом деле к переменной, именем которой она была инициализирована.

Ссылки в качестве параметров функции

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


Параметры функции мало чем отличаются от ее локальных автоматических переменных. Разница только в том, что они располагаются на стеке выше адреса в регистре ЕВР, а локальные переменные — ниже. Передача параметров при вызове функции эквивалентна созданию локальных переменных и инициализации их значениями соответствующих аргументов.

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

Тем самым параметры-ссылки делают возможной настоящую “передачу по ссылке” без использования явных указателей и адресов. Вот сравнение различных видов передачи параметров:

//////////////////////////////////////////////////

// RefPar.cpp: Передача параметров по ссылке.

//

#include <stdio.h>

void SwapVal(int x, int y)

// Передача значений.

{

int temp;

temp = x;

x = y;

у = temp;

}

void SwapPtr(int *x, int *y)

// Передача указателей.

{

int temp;

temp = *x;

*x = *y;

*y = temp;

}

void SwapRef(int &x, int &y)

// Передача ссылок.

{

int temp;

temp = x;

x = y;

y = temp;

}

int main(void)

(

int i = 11, i = 22;

printf("Initial values: i=%d,j=%d.\n", i,j ) ;

SwapVal(i, j);

printf ("After SwapVal():i = %d,j =%d.\n",i,j);

SwapPtr (&i, &j) ;

printf("After SwapPtr():i =%d,j=%d.\n",i,j);

SwapRef(i, j) ;

printf("After SwapRef():i =%d,j=%d.\n",i,j);

return 0;

Программа напечатает:

Initial values: i = 11, j = 22.

After SwapValO i = 11, j = 22.

After SwapPtr():i = 22, j = 11.

After SwapRef () i = 11, j =22.

Как и должно быть, функция SwapVal () не влияет на значения передаваемых ей переменных, поскольку она обменивает лишь локальные их копии. Функция SwapPtr () , принимающая указатели, обменивает значия исходных переменных, но требует явного указания адресов при вызове и явного их разыменования в теле переменной. Наконец, SwapRef () , с параметрами-ссылками, также обменивает значения переданных ей переменных. Причем она отличается от функции, принимающей параметры-значения, только заголовком. Тело SwapRef () и ее вызов выглядят совершенно аналогично функции SwapVal () .



Помимо случаев, когда функция должна возвращать значения в параметрах, ссылки могут быть полезны при передаче больших структур, поскольку функции тогда передается не сама структура, а ее адрес.



Передавая параметры по ссылке, нужно всегда помнить о возможных побочных эффектах. Если только функция не должна явно изменять значение своего аргумента, лучше объявить параметр как ссылку на константу (например, const int SiVal). Тогда при попытке присвоить параметру значение в теле функции компилятор выдаст сообщение об ошибке.



Ссылка в качестве возвращаемого значения



Возвращаемое функцией значение также может быть объявлено ссылкой. Это позволяет использовать функцию в левой части присваивания. Рассмотрите такой пример:

////////////////////////////////////////////////////////

// массива.

//

#include <stdio.h>

#include <assert.h>

const int arrSize = 8;

int &refItem(int indx)

// Возвращает ссылку на элемент

//

iArray[indx] {

static int iArray[arrSize];

//

// Проверка диапазона:

//

assert(indx >= 0 && indx< arrSize);

return iArray[indx];

}

int main(void) {

int i;

for (i=0; .KarrSise; i++)

refltem(i) = 1 << i;

// Присваивает значение элементу

// iArray[i].

for (i=O; i<arrSize; i++)

printf("iArray[%02d] = %4d\n'\ i, refltem(i));

refltem(i) = 0; // Попытка выхода за пределы

// массива.

return 0;

}

В первом из операторов for функция refltem() вызывается в левой части присваивания. Во втором for она возвращает значение, которое передается функции printf (). Обратите внимание, что, во-первых, массив iArray[] объявлен как статический локальный в refltem(), благодаря чему непосредственное обращение к нему вне этой функции невозможно. Во-вторых, refltem() попутно проверяет допустимость переданного ей индекса. Ссылка позволяет на элементарном уровне организовать механизм, который мы в следующей главе назовем сокрытием или абстрагированием данных.


Содержание раздела