Представления о заголовках функций и прототипах

В листинге 9.6 показана функция swap(), использующая в качестве аргументов указатели, а в листинге 9.7 — та же функция, но с использованием ссылок. Использовать функцию, которая принимает в качестве параметров ссылки, легче, да и в программе такая функция проще читается, но как вызывающей функции узнать о том, каким способом переданы параметры — по ссылке или по значению? Будучи клиентом (или пользователем) функции swap(), программист должен быть уверен в том, что функция swap() на самом деле изменит параметры.

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

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

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

Когда Колонел Джон Роблинг (Colonel John Roebling) проектировал свой Бруклинский мост (Brooklyn Bridge), он интересовался деталями процесса литья и изготовления проводов. Он глубоко вникал в подробности механических и химических процессов, которые требовалось обеспечить для создания необходимых материалов. Но в наши дни инженеры более эффективно используют свое рабочее время, доверяя информации о строительных материалах и не интересуясь подробностями их изготовления.

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

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

Возвращение нескольких значений

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

И вновь одинакового результата можно достичь, используя как ссылки, так и указатели. В листинге 9.8 показана функция, которая возвращает три значения: два в виде параметров-указателей и одно в виде возвращаемого значения функции.

Листинг 9.8. Возвращение значений с помощью указателей

1: // Листинг 9.8.

2: // Возвращение нескольких значений из функции с помощью указателей

3:

4: #include <iostream.h>

5: int

6: short Factor(int n, int* pSquared, int* pCubed);

7:

8: int main()

9: {

10: int number, squared, cubed;

11: short error;

12:

13: cout << "Enter a number (0 - 20): ";

14: cin >> number;

15:

16: error = Factor(number, &squared, &cubed);

17:

18: if (!error)

19: {

20: cout << "number: " << number << "\n";

21: cout << "square: " << squared << "\n";

22: cout << "cubed: " << cubed << "\n";

23: }

24: else

25: cout << "Error encountered!!\n";

26: return 0;

27: }

28:

29: short Factor(int n, int *pSquared, int *pCubed)

30: {

31: short Value = 0;

32: if (n > 20)

33: Value = 1;

34: else

35: {

36: *pSquared = n*n;

37: *pCubed = n*ri*n;

38: Value = 0;

39: }

40: return Value;

41: }

Результат:

Enter a number (0-20): 3

number: 3

square: 9

cubed: 27

Анализ: В строке 10 переменные number, squared и cubed определяются с использованием типа int. Переменной number присваивается значение, введенное пользователем. Это значение, а также адреса переменных squared и cubed передаются функции Factor() в виде параметров.

В функции Factor() анализируется первый параметр, который передается как значение. Если он больше 20 (максимальное значение, которое может обработать эта функция), то возвращаемое значение Value устанавливается равным единице, что служит признаком ошибки. Обратите внимание на то, что возвращаемое значение из функции Factor() может принимать либо значение 1, либо 0, являющееся признаком того, что все прошло нормально, а также заметьте, что функция возвращает это значение лишь в строке 40.

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

В строках 36 и 37 посредством указателей переменным в функции main() присваиваются возвращаемые значения. В строке 38 переменной Value присваивается значение возврата, означающее успешное завершение работы функции. В строке 40 это значение Value возвращается вызывающей функции.

Эту программу можно слегка усовершенствовать, дополнив ее следующим объявлением:

enum ERROR_VALUE { SUCCESS, FAILURE} ;

Затем вместо возврата значений 0 или 1 эта программа сможет возвращать SUCCESS ИЛИ FAILURE.

Наши рекомендации