С.Б. Липпман, Ж. Лажойе - Язык программирования С++ Вводный курс (1114944), страница 52
Текст из файла (страница 52)
Мы расскажемтакже о различных видах значений, которые может вернуть функция. Будутпредставлены четыре специальных случая применения функций: встроенные(inline), рекурсивные, написанные на других языках и объявленные директивамисвязывания, а также функция main(). В завершение главы мы разберем болеесложное понятие – указатель на функцию.7.1. ВведениеФункцию можно рассматривать как операцию, определенную пользователем. В общемслучае она задается своим именем.
Операнды функции, или формальные параметры,задаются в списке параметров, через запятую. Такой список заключается в круглыескобки. Результатом функции может быть значение, которое называют возвращаемым.Об отсутствии возвращаемого значения сообщают ключевым словом void. Действия,которые производит функция, составляют ее тело; оно заключено в фигурные скобки.Тип возвращаемого значения, ее имя, список параметров и тело составляют определениефункции. Вот несколько примеров:С++ для начинающихinline int abs( int obj ){// возвращает абсолютное значение iobjreturn( iobj < 0 ? -iobj : iobj );}inline int min( int p1, int p2 ){// возвращает меньшую из двух величинreturn( pi < p2 ? pi :p2 );}int gcd( int vl, int v2 ){// возвращает наибольший общий делительwhile ( v2 ){int temp = v2;v2 = vl % v2;vl = temp;}return vl;}Выполнение функции происходит тогда, когда в тексте программы встречается операторвызова.
Если функция принимает параметры, при ее вызове должны быть указаныфактические параметры, аргументы. Их перечисляют внутри скобок, через запятую. Вследующем примере main() дважды вызывает abs() и по одному разу min() и gcd().#include <iostream>int main(){// прочитать значения из стандартного вводаcout << "Введите первое значение: ";int i;cin >> i;if ( !cin ) {cerr << "!? Ошибка ввода - аварийный выход!\n";return -1;}cout << "Введите второе значение: ";int j;cin >> j;if ( !cin ) {cerr << "!? Ошибка ввода - аварийный выход!\n";return -2;}cout << "\nmin: " << min( i, j ) << endl;i = abs( i );j = abs( j );cout << "НОД: " << gcd( i, j ) << endl;return 0;Функция main() определяется в файле main.C.}315С++ для начинающихВызов функции может обрабатываться двумя разными способами. Если она объявленавстроенной (inline), то компилятор подставляет в точку вызова ее тело.
Во всех остальныхслучаях происходит нормальный вызов, который приводит к передаче управления ей, аактивный в этот момент процесс на время приостанавливается. По завершении работывыполнение программы продолжается с точки, непосредственно следующей за точкойвызова. Работа функции завершается выполнением последней инструкции ее тела илиспециальной инструкции return.Функция должна быть объявлена до момента ее вызова, попытка использоватьнеобъявленное имя приводит к ошибке компиляции. Определение функции можетслужить ее объявлением, но ему разрешено появиться в программе только один раз.Поэтому обычно его помещают в отдельный исходный файл.
Иногда в одном файленаходятся определения нескольких функций, логически связанных друг с другом. Чтобыиспользовать их в другом исходном файле, необходим механизм, позволяющий объявитьее, не определяя.Объявление функции состоит из типа возвращаемого значения, имени и спискапараметров. Вместе эти три элемента составляют прототип. Объявление можетпоявиться в файле несколько раз.В нашем примере файл main.C не содержит определений abs(), min() и gcd(), поэтомувызов любой из них приводит к ошибке компиляции.
Чтобы компиляция была успешной,int abs( int );int min( int, int );их необязательно определять, достаточно только объявить:int gcd( int, int );(В таком объявлении можно не указывать имя параметра, ограничиваясь названиемтипа.)Объявления (а равно определения встроенных функций17) лучше всего помещать взаголовочные файлы, которые могут включаться всюду, где необходимо вызватьфункцию. Таким образом, все файлы используют одно общее объявление. Если егонеобходимо модифицировать, изменения будут локализованы.
Вот так выглядит// определение функции находится в файле gcd.Сint gcd( int, int );inline int abs(int i) {return( i<0 ? -i : i );}inline int min(int vl.int v2) {return( vl<v2 ? vl : v2 );заголовочный файл для нашего примера. Назовем его localMath.h:}17 Таким образом, как мы видим, определения встроенных функций могутвстретиться в программе несколько раз! – Прим. ред.316С++ для начинающихВ объявлении функции описывается ее интерфейс. Он содержит все данные о том, какуюинформацию должна получать функция (список параметров) и какую информацию онавозвращает. Для пользователей важны только эти данные, поскольку лишь онифигурируют в точке вызова.
Интерфейс помещается в заголовочный файл, как мыпоступили с функциями min(), abs() и gcd().При выполнении наша программа main.C, получив от пользователя значения:Введите первое значение: 15Введите второе значение: 123выдаст следующий результат:mm: 15НОД: 37.2. Прототип функцииПрототип функции описывает ее интерфейс и состоит из типа возвращаемого функциейзначения, имени и списка параметров. В данном разделе мы детально рассмотрим этихарактеристики.7.2.1.
Тип возвращаемого функцией значенияТип возвращаемого функцией значения бывает встроенным, как int или double,составным, как int& или double*, или определенным пользователем – перечислениемили классом. Можно также использовать специальное ключевое слово void, которое#include <string>#include <vector> class Date { /* определение */ };bool look_up( int *, int );double calc( double );int count( const string &, char );Date& calendar( const char );говорит о том, что функция не возвращает никакого значения:void sum( vector<int>&, int );Однако функция или встроенный массив не могут быть типом возвращаемого значения.// массив не может быть типом возвращаемого значенияСледующий пример ошибочен:int[10] foo_bar();Но можно вернуть указатель на первый элемент массива:317С++ для начинающих318// правильно: указатель на первый элемент массиваint *foo_bar();(Размер массива должен быть известен вызывающей программе.)// правильно: возвращается список символовФункция может возвращать типы классов, в частности контейнеры.
Например:list<char> foo_bar();(Этот подход не очень эффективен. Обсуждение типа возвращаемого значения см. вразделе 7.4.)Тип возвращаемого функцией значения должен быть явно указан. Приведенный ниже// ошибка: пропущен тип возвращаемого значениякод вызывает ошибку компиляции:const is_equa1( vector<int> vl, vector<int> v2 );В предыдущих версиях С++ в подобных случаях считалось, что функция возвращаетзначение типа int. Стандарт С++ отменил это соглашение.
Правильное объявление// правильно: тип возвращаемого значения указанis_equal() выглядит так:const bool is_equa1( vector<int> vl, vector<int> v2 );7.2.2. Список параметров функцииСписок параметров не может быть опущен. Функция, которая не требует параметров,должна иметь пустой список либо список, состоящий из одного ключевого слова void.int fork();Например, следующие объявления эквивалентны:int fork( void );Такой список состоит из названий типов, разделенных запятыми. После имени типаможет находиться имя параметра, хотя это и необязательно. В списке параметров неразрешается использовать сокращенную запись, соотнося одно имя типа с несколькимиint manip( int vl, v2 );// ошибкапараметрами:int manip( int vl, int v2 ); // правильноС++ для начинающихИмена параметров не могут повторяться.
Имена, фигурирующие в определении функции,можно и даже нужно использовать в ее теле. В объявлении же функции они необязательны и служат средством документирования ее интерфейса. Например:void print( int *array, int size );Имена параметров в объявлении и в определении одной и той же функции не обязанысовпадать. Однако употребление разных имен может запутать пользователя.С++ допускает сосуществование двух или более функций, имеющих одно и то же имя, норазные списки параметров. Такие функции называются перегруженными.
О спискепараметров в этом случае говорят как о сигнатуре функции, поскольку именно ониспользуется различения разных версий одноименных функций. Имя и сигнатураоднозначно идентифицируют версию. (Перегруженные функции подробно обсуждаются вглаве 9.)7.2.3. Проверка типов формальных параметровФункция gcd() объявлена следующим образом:int gcd( int, int );Объявление говорит о том, что имеется два параметра типа int. Список формальныхпараметров предоставляет компилятору информацию, с помощью которой тот можетпроверить типы передаваемых функции фактических аргументов.Что будет, если попытаться вызвать функцию gcd() с аргументами типа char*?gcd( "hello", "world" );А если передать этой функции не два аргумента, а только один? Или больше двух? Чтослучится, если потеряется запятая между числами 24 и 312?gcd( 24312 );Единственное разумное поведение компилятора – сообщение об ошибке, посколькупопытка выполнить такую программу чревата весьма серьезными последствиями.