Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Cpp_Страуструп.doc
Скачиваний:
17
Добавлен:
03.05.2015
Размер:
3.2 Mб
Скачать

4.6 Функции

Самый распространенный способ задания в С++ каких-то действий - это

вызов функции, которая выполняет такие действия. Определение функции

есть описание того, как их выполнить. Неописанные функции

вызывать нельзя.

4.6.1 Описания функций

Описание функции содержит ее имя, тип возвращаемого значения

(если оно есть) и число и типы параметров, которые должны

задаваться при вызове функции. Например:

extern double sqrt(double);

extern elem* next_elem();

extern char* strcpy(char* to, const char* from);

extern void exit(int);

Семантика передачи параметров тождественна семантике

инициализации: проверяются типы фактических параметров и, если

нужно, происходят неявные преобразования типов. Так, если

учесть приведенные описания, то в следующем определении:

double sr2 = sqrt(2);

содержится правильный вызов функции sqrt() со значением с плавающей

точкой 2.0. Контроль и преобразование типа фактического параметра

имеет в С++ огромное значение.

В описании функции можно указывать имена параметров. Это

облегчает чтение программы, но транслятор эти имена просто

игнорирует.

4.6.2 Определения функций

Каждая вызываемая в программе функция должна быть где-то в ней

определена, причем только один раз. Определение функции - это ее

описание, в котором содержится тело функции. Например:

extern void swap(int*, int*); // описание

void swap(int* p, int* q) // определение

{

int t = *p;

*p = *q;

*q = *t;

}

Не так редки случаи, когда в определении функции не используются

некоторые параметры:

void search(table* t, const char* key, const char*)

{

// третий параметр не используется

// ...

}

Как видно из этого примера, параметр не используется, если

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

программы или если рассчитывают на ее дальнейшее расширение. В

обоих случаях резервирование места в определении функции для

неиспользуемого параметра гарантирует, что другие функции,

содержащие вызов данной, не придется менять.

Уже говорилось, что функцию можно определить как подстановку

(inline). Например:

inline fac(int i) { return i<2 ? 1 : n*fac(n-1); }

Спецификация inline служит подсказкой транслятору, что вызов

функции fac можно реализовать подстановкой ее тела, а не с помощью

обычного механизма вызова функций ($$R.7.1.2). Хороший оптимизирующий

транслятор вместо генерации вызова fac(6) может просто использовать

константу 720. Из-за наличия взаиморекурсивных вызовов функций-подстановок,

а также функций-подстановок, рекурсивность которых зависит от входных

данных, нельзя утверждать, что каждый вызов функции-подстановки

действительно реализуется подстановкой ее тела. Степень оптимизации,

проводимой транслятором, нельзя формализовать, поэтому одни

трансляторы создадут команды 6*5*4*3*2*1, другие - 6*fac(5), а

некоторые ограничатся неоптимизированным вызовом fac(6).

Чтобы реализация вызова подстановкой стала возможна даже

для не слишком развитых систем программирования, нужно, чтобы не

только определение, но и описание функции-подстановки находилось

в текущей области видимости. В остальном спецификация inline

не влияет на семантику вызова.