Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих (Стенли Липпман) 3-е хххх.pdf
Скачиваний:
86
Добавлен:
30.05.2015
Размер:
5.92 Mб
Скачать

С++ для начинающих

317

В объявлении функции описывается ее интерфейс. Он содержит все данные о том, какую информацию должна получать функция (список параметров) и какую информацию она возвращает. Для пользователей важны только эти данные, поскольку лишь они фигурируют в точке вызова. Интерфейс помещается в заголовочный файл, как мы поступили с функциями min(), abs() и gcd().

При выполнении наша программа main.C, получив от пользователя значения:

Введите первое значение: 15 Введите второе значение: 123

выдаст следующий результат:

mm: 15

НОД: 3

7.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();

Но можно вернуть указатель на первый элемент массива:

С++ для начинающих

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 ); // правильно

С++ для начинающих

319

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

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 );

Единственное разумное поведение компилятора сообщение об ошибке, поскольку попытка выполнить такую программу чревата весьма серьезными последствиями. С++ действительно не пропустит подобные вызовы. Текст сообщения будет выглядеть примерно так:

// gcd( "hello", "world" )

error: invalid argument types ( const char *, const char * ) -- expecting ( int, int )

ошибка: неверные типы аргументов ( const char *, const char * ) -- ожидается ( int, int )

// gcd( 24312 )

error: missing value for second argument

ошибка: пропущено значение второго аргумента

А если вызвать эту функцию с аргументами типа double? Должен ли этот вызов расцениваться как ошибочный?

gcd( 3.14, 6.29 );

С++ для начинающих

320

Как было сказано в разделе 4.14, значение типа double может быть преобразовано в int. Следовательно, считать такой вызов ошибочным было бы слишком сурово. Вместо этого аргументы неявно преобразуются в int (отбрасыванием дробной части) и таким образом требования, налагаемые на типы параметров, выполняются. Поскольку при подобном преобразовании возможна потеря точности, хороший компилятор выдаст предупреждение. Вызов превращается в

gcd( 3, 6 );

что дает в результате 3.

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

Пропуск аргумента при вызове или передача аргумента неуказанного типа часто служили источником ошибок в языке С. Теперь такие погрешности обнаруживаются на этапе компиляции.

Упражнение 7.1

(a)set( int *, int );

(b)void func();

(c)string error( int );

Какие из следующих прототипов функций содержат ошибки? Объясните.

(d) arr[10] sum( int *, int );

Упражнение 7.2

Напишите прототипы для следующих функций:

Функция с именем compare, имеющая два параметра типа ссылки на класс matrix и возвращающая значение типа bool.

Функция с именем extract без параметров, возвращающая контейнер set для хранения значений типа int. (Контейнерный тип set описывался в разделе 6.13.)

Упражнение 7.3

double calc( double );

int count( const string &, char ); void sum( vector<int> &, int );

Имеются объявления функций: vector<int> vec( 10 );

Какие из следующих вызовов содержат ошибки и почему?