Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Книга C++.doc
Скачиваний:
24
Добавлен:
10.11.2019
Размер:
2.48 Mб
Скачать

Указатели - аргументы функций.

Работая с аргументами функций в С++, Вы, наверное, обратили внимание на следующий факт: передавая аргументы в функцию по значению, сами переменные, после отработки функции, свое значение не изменяли. Это можно объяснить следующим образом: функции в качестве своих аргументов получают значения параметров, поэтому прямой возможности, находясь в вызванной функции, изменить переменную функции, откуда произошел вызов, нет. Однако, большинство практических приложений, как раз требуют от программиста решения следующей задачи: передать в функцию переменные; произвести изменения значений переменных; после отработки функции, переменные должны остаться с измененными значениями. Есть ли решение данной проблемы?.. Ответ может быть только один - да и, как один из вариантов, с помощью указателей.

Для того, чтобы разобраться с механизмом передачи аргументов в функцию, с последующим изменением их значений внутри вызванной функции, рассмотрим функцию my_swap, которая меняет местами значения своих параметров. Другими словами, если объявлены две переменные типа int a, b; причем, a=7 и b=10, то после вызова функции my_swap(&a, &b)  результат будет следующим: а=10 и b=7.

Чтобы получить желаемый эффект, надо вызываемой функции передать указатели на те значения, которые должны быть изменены: my_swap(&a, &b); Так как оператор & получает адрес переменной, то &a вернет нам адрес переменной а. Таким образом, используя оператор & для получения адреса переменных (значение которых должно будет измениться в функции), мы сможем передать в функцию адреса наших переменных.

Графически это можно представить следующим образом:

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

Рассмотрим реализацию функции, которая будет изменять значения своих параметров:

//реализация функции, которая будет изменять значения своих параметров

void my_swap(int *px, int *py)

{

int temp;

//производим стандартную перестановку через дополнительную переменную temp

temp=*px; //переменной temp писвоили 7

*px=*py; //переменная а приняла значение 10

*py=temp; //переменная b приняла значение 7

}

Теперь, вызов функции из main() будет выглядеть следующим образом:

my_swap(&a, &b);

Разобрав механизм вызова функции с передачей параметров по ссылке, рассмотрим практический пример использования, опять таки, нашей функции:

/*Иллюстрация использования механизма вызова функции с

передачей параметров по ссылке*/

#include <iostream.h>

//реализация функции, которя будет изменять значения своих параметров

void my_swap(int *px, int *py)

{

int temp;

//производим стандартную перестановку через дополнительную переменную temp

temp=*px; //переменной temp писвоили 7

*px=*py; //переменная а приняла значение 10

*py=temp; //переменная b приняла значение 7

}

void main()

{

int a,b;

//объявили две переменные типа int

cout<<"Enter a = ";

//вывели на экран предложение ввести значение переменной а

cin>>a;

cout<<"Enter b = ";

//вывели на экран предложение ввести значение переменной b

cin>>b;

my_swap(&a, &b);

//ВЫЗОВ ФУНКЦИИ С ПЕРЕДАЧЕЙ ПАРАМЕТРОВ ПО ССЫЛКЕ

cout<<"New value of a = "<<a<<endl;

//выведем на экран результат работы нашей программы

cout<<"New value of b = "<<b<<endl;

}

Давайте, подведем итоги:

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

  1. Объявите параметры-указатели в заголовке функции.

  2. Используйте разыменованный указатель в теле функции.

  3. Передавайте адреса в качестве аргументов при вызове функции.

Ссылки

В языке С++ ссылка определяется как другое имя уже существующего объекта. Например, в жизни, каждый из нас имеет, как правило, несколько имен и разные люди зовут нас по-разному (Саша, Алескандр, Шурик, Сан Саныч). Многих Великих мира сего мы вообще знаем только под их псевдонимами, например: Леонида Утесова или Софи Лорен :)

Так и ссылка есть другое имя для объекта. Например:

int L = 999;

int& RL = L;

Здесь RL является ссылкой. Теперь к L можно обращаться как по имени L, так и по имени RL. Т.е. по сути ссылка является синонимом, псевдонимом переменной L.

Основные достоинтсва ссылок проявляются при работе с функциями, но ссылки могут использоваться и самостоятельно.

Итак, давайте запишем в общем виде, как определяется ссылка:

тип& имя_ссылки = инициализирующее_выражение;

Для определения ссылки используется оператор ссылки & (символ амперсанд). Его можно найти над цифрой 7 на основной клавиатуре. При определении ссылки необходимо обязательно задать инициализирующее_выражение, псевдонимом которого и будет ссылка. Например:

float& ref; // ошибка

int Count = 0;

int &RefCount = Count; // правильно

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

int Count = 0;

int &RefCount = Count;

// здесь и Count и RefCount равны 0

RefCount = 1;

// здесь и Count и RefCount равны 1

++Count;

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

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

Если переменная определяется как ссылка, она должна быть инициализирована переменной объявленного типа. Как только эта инициализация выполнена, ссылаться на другие переменные уже нельзя. Т.е. после определения ссылка всегда "смотрит" на тот участок памяти (на тот объект), с которым она связана инициализацией. Другими словами, после определения ссылка не может быть никаким способом "оторвана" от объекта инициализации и связана с другим объектом. Ни одна из операций не действует на ссылку, а относится к тому объекту, с которым она связана. Таким образом, ссылка полностью аналогична исходному имени объекта.

int Count1 = 1, Count2 = 2;

int &refCount = Count1; // refCount = 1

refCount = Count2; // refCount = 2 и Count1 = 2;

Count2++; // refCount = 2, Count1 = 2, а Count2 = 3

cout << sizeof(refCount);

Обратите внимание на оператор cout << sizeof(refCount); Результатом выполнения операции sizeof к ссылке является не ее размер, а размер именуемого ею объекта, в данном случае переменной Count1. Аналогично, применив к ссылке операцию взятия адреса &, определим не адрес ссылки, а адрес того объекта, которым инициализирована ссылка. В общем, какие бы операции мы не рассматривали, вывод один - каждая операция над ссылкой является операцией над тем объектом, с которым она связана.

Т.к. ссылка есть "другое имя уже существующего объекта", то инициализирующим выражением может быть только Lvalue-выражение, т.е. выражение, которое может стоять в левой части оператора присваивания. Другими словами в качестве инициализирующего выражения должно выступать имя некоторого объекта, имеющего место в памяти.

float val = 1.1f;

float &ref_val = val; // правильно

float &ref_pi = 3.14f; // ошибка

Ссылочная переменная существенно отличается от указателя. Указатель ссылается на область памяти, содержащую адрес ячейки, в которой хранится значение некоторой переменной (т.е. косвенная адресация). Поэтому, чтобы обратиться к значению с помощью указателя нужно использовать операцию разыменования. А ссылочная переменная ссылается прямо на ячейку со значением (поскольку в точности совпадает с переменной, используемой для инициализации). Поэтому обратиться к данной ячейке памяти можно просто по имени ссылки.

int L = 100;

int &rL = L; // ссылка на L

int *pL= &L; // указатель на L

cout << L << endl; // выводим значение L

cout << rL << endl; /* выводим значания L,

обращаясь к ней по имени rL */

cout << *pL << endl; /* выводим значение L,

используя операцию разыменования указателя pL */

Существуют некоторые ограничения использования операции ссылка &, в частности запомните три "нельзя":

  1. Нельзя выполнять ссылку на ссылку.

  2. int a;

int& &ra = a; // ошибка

  1. Нельзя создавать массивы ссылок.

  2. int a;

int& ra[10]; // ошибка

  1. Нельзя создавать указатель на ссылку.

  2. int a;

int& *pra = &a; // ошибка