Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по С++ глава 2.doc
Скачиваний:
15
Добавлен:
05.11.2018
Размер:
249.86 Кб
Скачать

2.3.6. Ссылки

Переменная, объявленная как ссылка, служит псевдонимом другой переменной. Ссылку объявляют, используя оператор ссылки &.

int Count = 0;

int &RefCount * Count;

В этом примере переменная Re f Count объявлена как ссылка на данные типа int и инициализирована так, что ссылается на переменную Count типа int. Это определение заставляет RefCount стать псевдонимом переменной Count, т. е. и Re f Count, и Count будут ссылаться на одно и то же место в памяти. Две переменные всегда имеют одинаковые значения, а присваивание, сделанное для одной переменной, изменяет значение другой. Например:

int Count =» 0;

int &RefCount = Count; // здесь и Count, и RefCount равны О

Ref Count = 1; // здесь и Count, и RefCount равны 1

++Count; // здесь и Count, и RefCount равны 2

Примечание

Пробел непосредственно перед или после оператора & несущественен. Следовательно, можно объявить RefCount как int& RefCount, int & RefCount, int &RefCount или даже int&RefCount (хотя последняя форма трудно читаема).

Если переменная определяется как ссылка, она должна быть инициализирована переменной объявленного типа. Как только эта инициализация выполнена, ссылаться на другие переменные уже нельзя. Кроме того, нельзя инициализировать ссылочную переменную значениями констант (например, 5 или 'а'), если переменная не объявлена как ссылка на тип const (описан далее в разделе «Константы»).

int &RInt = 5; // ОШИБКА

Примечание

Инициализация нессылочной переменной при ее определении заключается в добавлении оператора присваивания, назначающего переменной новое значение. Для ссылочной переменной оператор присваивания - имеет несколько другое значение. Инициализация ссылки задает переменную, для которой ссылка будет псевдонимом. Другими словами, инициализация указывает, какую ячейку памяти представляет ссылочная переменная. Следующий за ним оператор присваивания изменяет значение этой области памяти.

Ссылочная переменная существенно отличается от указателя. Указатель ссылается на область памяти, содержащую адрес целевой ячейки, а ссылочная переменная – непосредственно на целевую ячейку памяти (в точности совпадая с переменной, используемой для ее инициализации). На рис. 2.1 показаны различия между переменной, ссылкой и указателем на нее. Допустим, что были заданы следующие объявления:

int Count = 0;

int &RefCount = Count;

int *PtrCount = &Count;

Обратите внимание: указатель PtrCount в приведенном коде может быть объявлен и инициализирован.

int *PtrCount = &RefCount; // что эквивалентно:

// int *PtrCount = &Count;

Переменные Count и RefCount ссылаются на одну и ту же ячейку памяти, поэтому после выполнения операции взятия адреса & они будут иметь одинаковые значения. Помните, что ссылочная переменная всегда ведет себя точно так же, как и переменная, используемая для ее инициализации.

2.3.7. Ссылки и функции

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

void FuncA (int &Parm);

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

Рис. 2.1. Переменная, ссылка и указатель на нее

void FuncA (int &Parm) //ссылочный параметр

{

++Parm;

}

void FuncB (int Farm) //нессылочный параметр

{

++Parm;

}

void main()

{

int N = 0;

FuncA (N); // N передается по ссылке

// здесь N равно 1

FuncB (N); // передается через значение

// здесь N все еще равно 1

}

В этом примере функции FuncA и FuncB увеличивают значение параметра Parm. Однако только функция FuncA, которая получает параметр как ссылку, изменяет значение переменной N, передаваемой из вызывающей функции. Нельзя передать константу (например, 5) как параметр, если последний не объявлен как ссылка на тип const (см, раздел «Константы и функции»).

void FuncA (int &RInt); void main ()

{

FuncA (5); // СШИБКА: ссылка не может инициализироваться значением константы

// ...

}

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

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

Оператором &, можно объявить функцию, возвращающую ссылку. Например:

int & Get Index();

Это объявление означает, что при вызове функции GetIndex будет создан временный псевдоним для некоторой целочисленной переменной (определяемой оператором return внутри функции). Следовательно, вызов функции GetIndex может находиться в любом месте выражения, содержащего данную целочисленную переменную, например:

int N;

N = Getlndex(); // копирует значение переменной типа int

Getlndex() = 5; //-присваивает 5 переменной типа int

++Getlndex(); // увеличивает значение переменной типа int

Следующий код показывает, как можно реализовать функцию GetIndex, и иллюстрирует эффект вызова функции.

int Index = 0; int &Getlndex()

{

return Index;

}

void main ()

{

int N; // здесь Index равно 0

N - GetIndex {);

// здесь Index равно 0 и N равно О

GetIndex () - 5;

// здесь Index равно 5

++GetIndex ();

// здесь Index равно б

}

Функция, возвращаемое значение которой является ссылкой, должна возвращать переменную соответствующего типа и может возвращать константу, – например 5, только в случае, если она объявлена как возвращающая ссылку на тип const (см. «Константы и функции»). Возвращаемая переменная используется для инициализации временной ссылочной переменной, создаваемой при вызове функции. Другими словами, полагая, что Index и GetIndex определены так, как показано выше, оператор

++GetIndex (); // вызов функции приводит к созданию временной

// ссылочной переменной, являющейся псевдонимом переменной Index

эквивалентен коду

int &Temp = Index; // объявление и инициализация действительной

// ссылки на переменную

++Теmр;

В этих примерах значение переменной index увеличивается на 1.

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

int &BadIdeal()

{

int i;

return i;

}

int &BadIdea2 {int Farm)

{

return Parm;

}

Этот код не вызывает ошибок компилятора, однако результат использования ссылки одной из этих функций непредсказуем. Функция, возвращающая ссылку, может "безопасно" вернуть только глобальную переменную, переменную типа static или переменную, память для которой выделяется динамически (см. параграф «Операторы new и delete»).

С другой стороны, функция, возвращающая нессылочный тип, может выполнить «безопасный» возврат автоматической переменной или параметра, потому что при вызове функции создается отдельная копия содержимого переменной, а не просто генерируется ссылка на нее. Именно по этой причине функция, возвращающая значение, не являющееся ссылкой, менее эффективна, чем функция, возвраща­ющая ссылку, особенно если она возвращает большой объект данных.

Примечание

В следующих главах приведены примеры передачи и возврата ссылок из функций.