Значения параметров, используемые по умолчанию
Для каждого параметра, объявляемого в прототипе и определении функции, должно быть передано соответствующее значение в вызове функции. Передаваемое значение должно иметь объявленный тип. Следовательно, если некоторая функция объявлена как
long myFunction(int);
то она действительно должна принимать целочисленное значение. Если тип объявленного параметра не совпадет с типом передаваемого аргумента, компилятор сообщит об ошибке.
Из этого правила существует одно исключение, которое вступает в силу, если в прототипе функции для параметра объявляется стандартное значение. Это значение, которое используется в том случае, если при вызове функции для этого параметра не установлено никакого значения. Несколько изменим предыдущее объявление:
long myFunction (int x = 50);
Этот прототип нужно понимать следующим образом. Функция myFunction возвращает значение типа long и принимает параметр типа int. Но если при вызове этой функции аргумент предоставлен не будет, используйте вместо него число 50. А поскольку в прототипах функций имена параметров не обязательны, то последний вариант объявления можно переписать по-другому:
long myFunction (int = 50);
Определение функции не изменяется при объявлении значения параметра, задаваемого по умолчанию. Поэтому заголовок определения этой функции будет выглядеть по-прежнему:
long myFunction (int x)
Если при вызове этой функции аргумент не устанавливается, то компилятор присвоит переменной x значение 50. Имя параметра, для которого в прототипе устанавливается значение по умолчанию, может не совпадать с именем параметра, указываемого в заголовке функции: значение, заданное по умолчанию, присваивается по позиции, а не по имени.
Установку значений по умолчанию можно назначить любым или всем параметрам функции. Но одно ограничение все же действует: если какой-то параметр не имеет стандартного значения, то ни один из предыдущих по отношению к нему параметров также не может иметь стандартного значения. Предположим, прототип функции имеет вид
long myFunction (int Param1, int Param2, int Param3);
тогда параметру Param2 можно назначить стандартное значение только в том случае, если назначено стандартное значение и параметру Param3. Параметру Param1 можно назначить стандартное значение только в том случае, если назначены стандартные значения как параметру Param2, так и параметру Param3. Использование значений, задаваемых параметрам функций по умолчанию, показано в листинге 5.7.
Листинг 5.7. Использование значений, заданных по умолчанию для параметров функций
1: // Листинг 5.7. Использование стандартных
2: // значений параметров
3:
4: #include <iostream.h>
5:
6: int VolumeCube(int length, int width = 25, int height = 1);
7:
8: int main()
9: {
10: int length = 100;
11: int width = 50;
12: int height = 2;
13: int volume;
14:
15: volume = VolumeCube(length, width, height);
16: cout << "First volume equals: " << volume << "\n";
17:
18: volume = VolumeCube(length, width);
19: cout << "Second time volume equals: " << volume << "\n";
20:
21: volume = VolumeCube(length);
22: cout << "Third time volume equals: " << volume << "\n";
23: return 0;
24: }
25:
26: VolumeCube(int length, int width, int height)
27: {
28:
29: return (length * width * height);
30: }
Результат:
First volume equals: 10000
Second time volume equals: 5000
Third time volume equals: 2500
Анализ: В прототипе функции VolumeCube() B строке 6 объявляется, что функция принимает три параметра, причем последние два имеют значения, устанавливаемые по умолчанию.
Эта функция вычисляет объем параллелепипеда на основании переданных размеров. Если значение ширины не передано, то ширина устанавливается равной 25, а высота — 1. Если значение ширины передано, а значение высоты нет, то по умолчанию устанавливается только значение высоты. Но нельзя передать в функцию значение высоты без передачи значения ширины.
В строках 10—12 инициализируются переменные, предназначенные для хранения размеров параллелепипеда по длине, ширине и высоте. Эти значения передаются функции VolumeCube() в строке 15. После вычисления объема параллелепипеда результат выводится в строке 16.
В строке 18 функция VolumeCube() вызывается снова, но без передачи значения для высоты. В этом случае для вычисления объема параллелепипеда используется значение высоты, заданное по умолчанию, и полученный результат выводится в строке 19.
При третьем вызове функции VolumeCube() (строка 21) не передается ни значение ширины, ни значение высоты. Поэтому вместо них используются значения, заданные по умолчанию, и полученный результат выводится в строке 22.
Рекомендуется: Помните, что параметры функции действуют внутри нее, подобно локальным переменным.
Не рекомендуется: Не устанавливайте значение по умолчанию для первого параметра, если для второго параметра используемого по умолчанию значения не предусмотрено. Не забывайте, что аргументы, переданные в функцию как значения, не могут повлиять на переменные, используемые при вызове функции. Не забывайте, что изменения, внесенные в глобальную переменную в одной функции, изменяют значение этой переменной для всех функций.
Перегрузка функций
В языке C++ предусмотрена возможность создания нескольких функций с одинаковым именем. Это называется перегрузкой функций. Перегруженные функции должны отличаться друг от друга списками параметров: либо типом одного или нескольких параметров, либо различным количеством параметров, либо и тем и другим одновременно. Рассмотрим следующий пример:
int myFunction (int, int); int myFunction (long, long); int myFunction (long);
Функция myFunction() перегружена с тремя разными списками параметров. Первая и вторая версии отличаются типами параметров, а третья — их количеством.
Типы возвращаемых значений перегруженных функций могут быть одинаковыми или разными. Следует иметь в виду, что при создании двух функций с одинаковым именем и одинаковым списком параметров, но с различными типами возвращаемых значений, будет сгенерирована ошибка компиляции.
Перегрузка функций также называется полиморфизмом функций. Поли (гр. poly) означает много, морфе (гр. morphe) — форма, т.е. полиморфическая функция — это функция, отличающаяся многообразием форм.
Под полиморфизмом функции понимают существование в программе нескольких перегруженных версий функции, имеющих разные назначения. Изменяя количество или тип параметров, можно присвоить двум или нескольким функциям одно и то же имя. При этом никакой путаницы при вызове функций не будет, поскольку нужная функция определяется по совпадению используемых параметров. Это позволяет создать функцию, которая сможет, например, усреднять целочисленные значения, значения типа double или значения других типов без необходимости создавать отдельные имена для каждой функции — AverageInts(), AverageDoubles() и т.д.
Предположим, вы пишете функцию, которая удваивает любое передаваемое ей значение. При этом вы бы хотели иметь возможность передавать ей значения типа int, long, float или double. Без перегрузки функций вам бы пришлось создавать четыре разные функции:
int DoubleInt(int); long DoubleLong(long); float DoubleFloat(float); double DoubleDouble(double);
С помощью перегрузки функций можно использовать следующие объявления:
int Double(int); long Double(long); float Double(float); double Double(double);
Благодаря использованию перегруженных функций не нужно беспокоиться о вызове в программе нужной функции, отвечающей типу передаваемых переменных. При вызове перегруженной функции компилятор автоматически определит, какой именно вариант функции следует использовать. Перегрузка функции показана в листинге 5.8.
Листинг 5.8. Полиморфизм функций
1: // Листинг 5.8. Пример
2: // полиморфизма функций
3:
4: #include <iostream.h>
5:
6: int Double(int);
7: long Double(long);
8: float Double(float);
9: double Double(double);
10:
11: int main()
12: {
13: int myInt = 6500;
14: long myLong = 65000;
15: float myFloat = 6.5F;
16: double myDouble = 6.5e20;
17:
18: int doubledInt;
19: long doubledLong;
20: float doubledFloat;
21: double doubledDouble;
22:
23: cout << "myInt: " << myInt << "\n";
24: cout << "myLong: " << myLong << "\n";
25: cout << "myFloat: " << myFloat << "\n";
26: cout << "myDouble: " << myDouble << "\n";
27:
28: doubledInt = Double(myInt);
29: doubledLong = Double(myLong);
30: doubledFloat = Double(myFloat);
31: doubledDouble = Double(myDouble);
32:
33: cout << "doubledInt: " << doubledInt << "\n";
34: cout << "doubledLong: " << doubledLong << "\n";
35: cout << "doubledFloat: " << doubledFloat << "\n";
36: cout << "doubledDouble: " << doubledDouble << "\n";
37:
38: return 0;
39: }
40:
41: int Double(int original)
42: {
43: cout << "In Double(int)\n";
44: return 2 * original;
45: }
46:
47: long Double(long original)
48: {
49: cout << "In Double(long)\n";
50: return 2 * original;
51: }
52:
53: float Double(float original)
54: {
55: cout << "In Double(float)\n";
56: return 2 * original;
57: }
58:
59: double Double(double original)
60: {
61: cout << "In Double(double)\n";
62: return 2 * original;
63: }
Результат:
myInt: 6500
myLong: 65000
myFloat: 6.5
myDouble: 6.5e+20
In Double(int)
In Double(long)
In Double(float)
In Double(double)
DoubledInt: 13000
DoubledLong 130000
DoubledFLoat: 13
DoubledDouble: 1.3e+21
Анализ: Функция Double() перегружается для приема параметров четырех типов: int, long, float и double. Прототипы функций занимают строки 6—9, а определения — строки 41-63.
В теле основной программы объявляется восемь локальных переменных. В строках 13-16 инициализируются первые четыре переменные, а в строках 28-31 остальным четырем переменным присваиваются результаты передачи значений первых четырех переменных функции Double(). Обратите внимание, что по виду вызова эти функции ничем не отличаются друг от друга. Но удивительное дело: вы передаете аргумент — и вызывается нужная функция!
Дело в том, что компилятор определяет тип переданного аргумента, на основании которого выбирает соответствующий вариант функции Double(). А результаты работы этой программы подтверждают ожидаемую очередность вызова вариантов этой перегруженной функции.