Чтение онлайн

ЖАНРЫ

Программирование. Принципы и практика использования C++ Исправленное издание
Шрифт:

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

{

return x*x; // возвращаем квадрат числа x

}

Для функции

square
эта работа тривиальна: мы вычисляем квадрат аргумента и возвращаем его в качестве результата. Выразить это на языке С++ проще, чем на естественном языке. Это типично для простых идей. Помимо всего прочего, язык программирования предназначен именно для простого и точного выражения таких простых идей.

Синтаксис определения функции можно описать так:

тип идентификатора (список параметров) тело функции

За типом (возвращаемого значения) следует идентификатор (имя функции),

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

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

void
(означающее “ничего”). Рассмотрим пример.

void write_sorry // не принимает никаких аргументов;

// ничего не возвращает

{

cout << "Извините \n";

}

Специфические аспекты, связанные с языком программирования, будут описаны в главе 8.

4.5.1. Зачем нужны функции

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

• Эти вычисления логически отделены от других.

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

• Функцию можно использовать в разных местах программы.

• Использование функций упрощает отладку программы.

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

x*x
, или
7*7
, или
(x+7)*(x+7)
, а не
square(x)
,
square(7)
или
square(x+7)
. Однако функция square сильно упрощает такие вычисления. Рассмотрим теперь извлечение квадратного корня (в языке С++ эта функция называется
sqrt
): можете написать выражение
sqrt(x)
, или
sqrt(7)
, или
sqrt(x+7)
, а не повторять код, вычисляющий квадратный корень, запутывая программу. И еще один аргумент: можете даже не интересоваться, как именно вычисляется квадратный корень числа в функции
sqrt(x)
, — достаточно просто передать функции аргумент
x
.

В разделе 8.5 мы рассмотрим множество технических деталей, связанных с функциями, а пока рассмотрим еще один пример. Если мы хотим действительно упростить цикл в функции

main
, то можно было бы написать такой код:

void print_square(int v)

{

cout << v << '\t' << v*v << '\n';

}

int main

{

for (int i = 0; i<100; ++i) print_square(i);

}

Почему же мы не использовали версию программы на основе функции

print_square
? Дело в том, что эта программа ненамного проще, чем версия, основанная на функции
square
, и, кроме того,

• функция

print_square
является слишком специализированной и вряд ли будет использована в другой программе, в то время как функция
square
, скорее всего, будет полезной для других пользователей;

• функция

square
не требует подробной документации, а функция
print_square
очевидно требует пояснений.

Функция

print_square
выполняет два логически отдельных действия:

• печатает числа;

• вычисляет квадраты.

Программы легче писать и понимать, если каждая функция выполняет отдельное логическое действие. По этой причине функция

square
является более предпочтительной.

В заключение попробуем ответить, почему мы использовали функцию

square(i)
, а не выражение
i*i
, использованное в первой версии программы? Одной из целей функций является упрощение кода путем распределения сложных вычислений по именованным функциям, а для программы 1949 года еще не было аппаратного обеспечения, которое могло бы непосредственно выполнить операцию “умножить”. По этой причине в первоначальной версии этой программы выражение
i*i
представляло собой действительно сложное вычисление, как если бы вы выполняли его на бумаге. Кроме того, автор исходной версии, Дэвид Уилер, ввел понятие функций (впоследствии названных процедурами) в современном программировании, поэтому было вполне естественно, что он использовал их в своей программе.

ПОПРОБУЙТЕ

Реализуйте функцию

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

4.5.2. Объявления функций

Вы заметили, что вся информация, необходимая для вызова функции, содержится в первой строке ее объявления? Рассмотрим пример.

int square(int x)

Этой строки уже достаточно, чтобы написать инструкцию

int x = square(44);

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

sqrt
? Мы знаем, что она извлекает квадратный корень из своего аргумента. А зачем нам знать, как устроено тело функции
square
? Разумеется, в нас может разжечься любопытство. Но в подавляющем большинстве ситуаций достаточно знать, как вызвать функцию, взглянув на ее определение. К счастью, в языке С++ существует способ, позволяющий получить эту информацию, не заглядывая в тело функции. Эта конструкция называется объявлением функции.

int square(int); // объявление функции square

double sqrt(double); // объявление функции sqrt

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

int square(int x) // определение функции square

{

return x*x;

}

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

#include
. Определение функции может быть в любом другом месте. Это “любое другое место” мы укажем в разделах 8.3 и 8.7. В более крупных программах разница между объявлениями и определениями становится существеннее. В этих программах определения позволяют сосредоточиться на локальном фрагменте программы (см. раздел 4.2), не обращая внимания на остальную часть кода.

4.6. Вектор

Для того чтобы программа делала полезную работу, необходимо хранить коллекцию данных. Например, нам может понадобиться список телефонных номеров, список игроков футбольной команды, список книг, прочитанных в прошлом году, список курсов, график платежей за автомобиль, список прогнозов погоды на следующую неделю, список цен на фотокамеру в интернет-магазине и т.д. Этот перечень можно продолжать до бесконечности, а потому и в программах эти списки встречаются очень часто. В дальнейшем мы рассмотрим множество способов хранения коллекций данных (контейнерные классы, описанные в главах 20 и 21). Пока начнем с простейшего и, вероятно, наиболее полезного способа хранения данных: типа

vector
(вектор).

Поделиться с друзьями: