Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
lekcii_1_10 / Лекция 08.docx
Скачиваний:
50
Добавлен:
21.05.2015
Размер:
139.15 Кб
Скачать

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

В этой главе мы продолжим изучение функций, а именно рассмотрим три самые важные темы, связанные с функциями C++: ссылки, перегрузка функций и использование аргументов по умолчанию. Эти три средства в значительной степени расширяют возможности функций.

- Ссылка — это неявный указатель.

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

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

Два способа передачи аргументов

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

Первый называется вызовом по значению (call-by-value). В этом случае значение аргумента копируется в формальный параметр подпрограммы. Следовательно, изменения, внесенные в параметры подпрограммы, не влияют на аргументы, используемые при ее вызове.

- При вызове по ссылке функции передается адрес аргумента.

Второй способ передачи аргумента подпрограмме называется вызовом по ссылке (call-by-reference). В этом случае в параметр копируется адрес аргумента (а не его значение). В пределах вызываемой подпрограммы этот адрес используется для доступа к реальному аргументу, заданному при ее вызове. Это значит, что изменения, внесенные в параметр, окажут воздействие на аргумент, используемый при вызове подпрограммы.

Как в C++ реализована передача аргументов

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

Рассмотрим следующую функцию.

#include <iostream>

using namespace std;

int sqr_it(int x);

int main()

{

 int t=10;

 cout << sqr_it(t) << ' ' << t;

 return 0;

}

int sqr_it(int x)

{

 x = x*x;

 return x;

}

В этом примере значение аргумента, передаваемого функции sqr_it(), 10, копируется в параметр х. При выполнении присваивания х = х*х изменяется лишь локальная переменная х. Переменная t, используемая при вызове функции sqr_it(), по-прежнему будет иметь значение 10, и на нее никак не повлияют операции, выполняемые в этой функции. Следовательно, после запуска рассматриваемой программы на экране будет выведен такой результат: 100 10.

Использование указателя для обеспечения вызова по ссылке

Несмотря на то что в качестве С++-соглашения о передаче параметров по умолчанию действует вызов по значению, существует возможность "вручную" заменить его вызовом по ссылке. В этом случае функции будет передаваться адрес аргумента (т.е. указатель на аргумент). Это позволит внутреннему коду функции изменить значение аргумента, которое хранится вне функции.

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

Чтобы понять, как передача указателя позволяет вручную обеспечить вызов по ссылке, рассмотрим следующую версию функции swap(). (Она меняет значения двух переменных, на которые указывают ее аргументы.)

void swap(int *х, int *у)

{

 int temp;

 temp = *x; // Временно сохраняем значение, расположенное по адресу х.

 *х = *у; // Помещаем значение, хранимое по адресу у, в адрес х.

 *у = temp; // Помещаем значение, которое раньше хранилось по адресу х, в адрес у.

}

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

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

#include <iostream>

using namespace std;

// Объявляем функцию swap(), которая использует указатели.

void swap(int *х, int *у);

Int main()

{

 int i, j;

 i = 10;

 j = 20;

 cout << "Исходные значения переменных i и j: ";

 cout << i << ' ' << j << '\n';

 swap(&j, &i); // Вызываем swap() с адресами переменных i и j.

 cout << "Значения переменных i и j после обмена: ";

 cout << i << ' ' << j << '\n';

 return 0;

}

// Обмен аргументами.

void swap(int *x, int *y)

{

 int temp;

 temp = *x; // Временно сохраняем значение, расположенное по адресу х.

 *х = *у; // Помещаем значение, хранимое по адресу у, в адрес х.

 *у = temp; // Помещаем значение, которое раньше хранилось по адресу х, в адрес у.

}

Результаты выполнения этой программы таковы.

Исходные значения переменных i и j: 10 20

Значения переменных i и j после обмена: 20 10

В этом примере переменной i было присвоено начальное значение 10, а переменной j20. Затем была вызвана функция swap() с адресами переменных i и j. Для получения адресов здесь используется унарный оператор Следовательно, функции swap() при вызове были переданы адреса переменных i и j, а не их значения. После выполнения функции swap() переменные i и j обменялись своими значениями.

Ссылочные параметры

Ссылочный параметр автоматически получает адрес соответствующего аргумента.

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

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

Чтобы лучше понять механизм действия ссылочных параметров, рассмотрим для начала простой пример. В следующей программе функция f() принимает один ссылочный параметр типа int.

// Использование ссылочного параметра.

#include <iostream>

using namespace std;

void f(int &i);

Int main()

{

 int val = 1;

 cout << "Старое значение переменной val: " << val << '\n';

 f(val); // Передаем адрес переменной val функции f().

 cout << "Новое значение переменной val: " << val << '\n';

 return 0;

}

void f(int &i)

{

 i = 10; // Модификация аргумента, заданного при вызове.

}

При выполнении этой программы получаем такой результат.

Старое значение переменной val: 1

Новое значение переменной val: 10

Обратите особое внимание на определение функции f().

void f (int &i)

{

 i = 10; // Модификация аргумента, заданного при вызове.

}

Итак, рассмотрим объявление параметра i. Его имени предшествует символ который "превращает" переменную i в ссылочный параметр. (Это объявление также используется в прототипе функции.) Инструкция

i = 10;

(в данном случае она одна составляет тело функции) не присваивает переменной i значение 10. В действительности значение 10 присваивается переменной, на которую ссылается переменная i (в нашей программе ею является переменная val). Обратите внимание на то, что в этой инструкции не используется оператор который необходим при работе с указателями. Применяя ссылочный параметр, вы тем самым уведомляете С++-компилятор о передаче адреса (т.е. указателя), и компилятор автоматически разыменовывает его за вас. Более того, если бы вы попытались "помочь" компилятору, использовав оператор то сразу же получили бы сообщение об ошибке (и вправду "ни одно доброе дело не остается безнаказанным").

Поскольку переменная i была объявлена как ссылочный параметр, компилятор автоматически передает функции f() адрес любого аргумента, с которым вызывается эта функция. Таким образом, в функции main() инструкция

f(val); // Передаем адрес переменной val функции f().

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

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

#include <iostream>

using namespace std;

// Объявляем функцию swap() с использованием ссылочных параметров.

void swap(int &х, int &у);

Соседние файлы в папке lekcii_1_10