Язык программирования C++. Пятое издание - Стенли Липпман
Шрифт:
Интервал:
Закладка:
Поскольку у функции fact() один параметр типа int, при каждом ее вызове следует предоставить один аргумент, который может быть преобразован в тип int (см. раздел 4.11):
fact("hello"); // ошибка: неправильный тип аргумента
fact(); // ошибка: слишком мало аргументов
fact(42, 10, 0); // ошибка: слишком много аргументов
fact(3.14); // ok: аргумент преобразуется в int
Первый вызов терпит неудачу потому, что невозможно преобразование значения типа const char* в значение типа int. Второй и третий вызовы передают неправильные количества аргументов. Функцию fact() следует вызывать с одним аргументом; ее вызов с любым другим количеством аргументов будет ошибкой. Последний вызов допустим, поскольку значение типа double преобразуется в значение типа int. В этом случае аргумент неявно преобразуется в тип int (с усечением). После преобразования этот вызов эквивалентен следующему:
fact(3);
Список параметров функцииСписок параметров функции может быть пустым, но он не может отсутствовать. При определении функции без параметров обычно используют пустой список параметров. Для совместимости с языком С можно также использовать ключевое слово void, чтобы указать на отсутствие параметров:
void f1() { /* ... */ } // неявно указанный пустой список параметров
void f2(void) { /* ... */ } // явно указанный пустой список параметров
Список параметров, как правило, состоит из разделяемого запятыми списка параметров, каждый из которых выглядит как одиночное объявление. Даже когда типы двух параметров одинаковы, объявление следует повторить:
int f3(int v1, v2) { /* ... */ } // ошибка
int f4(int v1, int v2) { /* ... */} // ok
Параметры не могут иметь одинаковые имена. Кроме того, локальные переменные даже в наиболее удаленной области видимости в функции не могут использовать имя, совпадающее с именем любого параметра.
Имена в определении функций не обязательны, но все параметры обычно именуют. Поэтому у каждого параметра обычно есть имя. Иногда у функций есть не используемые параметры. Такие параметры зачастую оставляют безымянными, указывая, что они не используются. Наличие безымянного параметра не изменяет количество аргументов, которые следует передать при вызове. Аргумент при вызове должен быть предоставлен для каждого параметра, даже если он не используется.
Тип возвращаемого значения функцииВ качестве типа возвращаемого значения функции применимо большинство типов. В частности, типом возвращаемого значения может быть void, это означает, что функция не возвращает значения. Но типом возвращаемого значения не может быть массив (см. раздел 3.5) или функция. Однако функция может возвратить указатель на массив или функцию. Определение функции, возвращающей указатель (или ссылку) на массив, рассматривается в разделе 6.3.3, а указателя на функцию — в разделе 6.7.
Упражнения раздела 6.1Упражнение 6.1. В чем разница между параметром и аргументом?
Упражнение 6.2. Укажите, какие из следующих функций ошибочны и почему. Предложите способ их исправления.
(a) int f() {
string s;
// ...
return s;
}
(b) f2(int i) { /* ... */ }
(c) int calc(int v1, int v1) /* ... */ }
(d) double square(double x) return x * x;
Упражнение 6.3. Напишите и проверьте собственную версию функции fact().
Упражнение 6.4. Напишите взаимодействующую с пользователем функцию, которая запрашивает число и вычисляет его факториал. Вызовите эту функцию из функции main().
Упражнение 6.5. Напишите функцию, возвращающую абсолютное значение ее аргумента.
6.1.1. Локальные объекты
В языке С++ имя имеет область видимости (см. раздел 2.2.4), а объекты — продолжительность существования (object lifetime). Обе эти концепции важно понимать.
• Область видимости имени — это часть текста программы, в которой имя видимо.
• Продолжительность существования объекта — это время при выполнении программы, когда объект существует.
Как уже упоминалось, тело функции — это блок операторов. Как обычно, блок формирует новую область видимости, в которой можно определять переменные. Параметры и переменные, определенные в теле функции, называются локальными переменными (local variable). Они являются локальными для данной функции и скрывают (hide) объявления того же имени во внешней области видимости.
Объекты, определенные вне любой из функций, существуют на протяжении выполнения программы. Такие объекты создаются при запуске программы и не удаляются до ее завершения. Продолжительность существования локальной переменной зависит от того, как она определена.
Автоматические объектыОбъекты, соответствующие обычным локальным переменным, создаются при достижении процессом выполнения определения переменной в функции. Они удаляются, когда процесс выполнения достигает конца блока, в котором определена переменная. Объекты, существующие только во время выполнения блока, известны как автоматические объекты (automatic object). После выхода процесса выполнения из блока значения автоматических объектов, созданных в этом блоке, неопределенны.
Параметры — это автоматические объекты. Место для параметров резервируется при запуске функции. Параметры определяются в пределах тела функции. Следовательно, они удаляются по завершении функции.
Автоматические объекты, соответствующие параметрам функции, инициализируются аргументами, переданными функции. Автоматические объекты, соответствующие локальным переменным, инициализируются, если их определение содержит инициализатор. В противном случае они инициализируются значением по умолчанию (см. раздел 2.2.1), а это значит, что значения неинициализированных локальных переменных встроенного типа неопределенны.
Локальные статические объектыИногда полезно иметь локальную переменную, продолжительность существования которой не прерывается между вызовами функции. Чтобы получить такие объекты, при определении локальной переменной используют ключевое слово static. Каждый локальный статический объект (local static object) инициализируется прежде, чем выполнение достигнет определения объекта. Локальная статическая переменная не удаляется по завершении функции; она удаляется по завершении программы.
В качестве простого примера рассмотрим функцию, подсчитывающую количество своих вызовов:
size_t count_calls() {
static size_t ctr = 0; // значение сохраняется между вызовами
return ++ctr;
}
int main() {
for (size_t i = 0; i != 10; ++i)
cout << count_calls() << endl;
return 0;
}
Эта программа выводит числа от 1 до 10 включительно.
Прежде чем процесс выполнения впервые достигнет определения переменной ctr, она уже будет создана и получит исходное значение 0. Каждый вызов осуществляет инкремент переменной ctr и возвращает ее новое значение. При каждом запуске функции count_calls() переменная ctr уже существует и имеет некое значение, возможно, оставленное последним вызовом функции. Поэтому при втором вызове значением переменной ctr будет 1, при третьем — 2 и т.д.
Если у локальной статической переменной нет явного инициализатора, она инициализируется значением по умолчанию (см. раздел 3.3.1), следовательно, локальные статические переменные встроенного типа инициализируются нулем.
Упражнения раздела 6.1.1Упражнение 6.6. Объясните различия между параметром, локальной переменной и локальной статической переменной. Приведите пример функции, в которой каждая из них могла бы быть полезной.
Упражнение 6.7. Напишите функцию, которая возвращает значение 0 при первом вызове, а при каждом последующем вызове возвращает последовательно увеличивающиеся числа.
6.1.2. Объявление функций
Как и любое другое имя, имя функции должно быть объявлено прежде, чем его можно будет использовать. Подобно переменным (см. раздел 2.2.2), функция может быть определена только однажды, но объявлена может быть многократно. За одним исключением, которое будет описано в разделе 15.3, можно объявить функцию, которая не определяется до тех пор, пока она не будет использована.
Объявление функции подобно ее определению, но у объявления нет тела функции. В объявлении тело функции заменяет точка с запятой.