Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Липпман.doc
Скачиваний:
8
Добавлен:
14.08.2019
Размер:
7.54 Mб
Скачать

7.3.1. Параметры-ссылки

Использование ссылок в качестве параметров модифицирует стандартный механизм передачи по значению. При такой передаче функция манипулирует локальными копиями аргументов. Используя параметры-ссылки, она получает l-значения своих аргументов и может изменять их.

В каких случаях применение параметров-ссылок оправданно? Во-первых, тогда, когда без использования ссылок пришлось бы менять типы параметров на указатели (см. приведенную выше функцию swap()). Во-вторых, при необходимости вернуть из функции несколько значений. В-третьих, для передачи большого объекта типа класса. Рассмотрим два последних случая подробнее.

Как пример функции, использующей параметр-ссылку для возврата дополнительного значения, возьмем look_up(), которая будет искать заданную величину в векторе целых чисел. В случае успеха look_up() вернет итератор, указывающий на найденный элемент, иначе – на элемент, расположенный за конечным. Если величина содержится в векторе несколько раз, итератор будет указывать на первое вхождение. Кроме того, дополнительный параметр-ссылка occurs возвращает количество найденных элементов.

#include <vector>

// параметр-ссылка 'occurs'

// содержит второе возвращаемое значение

vector<int>::const_iterator look_up(

const vector<int> &vec,

int value, // искомое значение

int &occurs ) // количество вхождений

{

// res_iter инициализируется значением

// следующего за конечным элемента

vector<int>::const_iterator res_iter = vec.end();

occurs = 0;

for ( vector<int>::const_iterator iter = vec.begin();

iter != vec.end();

++iter )

if ( *iter == value )

{

if ( res_iter == vec.end() )

res_iter = iter;

++occurs;

}

return res_iter;


}

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

class Huge { public: double stuff[1000]; };

extern int calc( const Huge & );

int main() {

Huge table[ 1000 ];

// ... инициализация table

int sum = 0;

for ( int ix=0; ix < 1000; ++ix )

// calc() ссылается на элемент массива

// типа Huge

sum += calc( tab1e[ix] );

// ...


}

Может возникнуть желание использовать параметр-ссылку, чтобы избежать создания копии большого объекта, но в то же время не дать вызываемой функции возможности изменять значение аргумента. Если параметр-ссылка не должен модифицироваться внутри функции, то стоит объявить его как ссылку на константу. В такой ситуации компилятор способен распознать и пресечь попытку непреднамеренного изменения значения аргумента.

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

class X;

extern int foo_bar( X& );

int foo( const X& xx ) {

// ошибка: константа передается

// функции с параметром неконстантного типа

return foo_bar( xx );


}

Для того чтобы программа компилировалась, мы должны изменить тип параметра foo_bar(). Подойдет любой из следующих двух вариантов:

extern int foo_bar( const X& );


extern int foo_bar( X ); // передача по значению

Вместо этого можно передать копию xx, которую позволено менять:

int foo( const X &xx ) {

// ...

X x2 = xx; // создать копию значения

// foo_bar() может поменять x2,

// xx останется нетронутым

return foo_bar( x2 ); // правильно


}

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

void ptrswap( int *&vl, int *&v2 ) {

int *trnp = v2;

v2 = vl;

vl = tmp;


}

Объявление

int *&v1;

должно читаться справа налево: v1 является ссылкой на указатель на объект типа int. Модифицируем функцию main(), которая вызывала rswap(), для проверки работы ptrswap():

#include <iostream>

void ptrswap( int *&vl, int *&v2 );

int main() {

int i = 10;

int j = 20;

int *pi = &i;

int *pj = &j;

cout << "Перед ptrswap():\tpi: "

<< *pi << "\tpj: " << *pj << endl;

ptrswap( pi, pj );

cout << "После ptrswap():\tpi: "

<< *pi << "\tpj: " << pj << endl;

return 0;


}

Вот результат работы программы:

Перед ptrswap(): pi: 10 pj: 20

После ptrswap(): pi: 20 pj: 10