Функции

Материал из Deeptown Manual
Версия от 16:15, 14 сентября 2007; Korvin (обсуждение | вклад)

(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

Ни один современный язык программирования не был бы возможен без функций. Функции это "кирпичики", из которых складывается программа. Для работы функциям, подобно обычным программам могут потребоваться входные данные — их называют аргументами, или параметрами функции. Функции так же могут возвращать результат. С этой точки зрения, функцию можно сравнить с "черным ящиком", имеющим несколько входов и один выход. Однако, в отличие классического "черного ящика", функция так же может изменять сами входные значения. Но в целом, смысл функций тот же, что и в математике.

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

Содержание

Объявление

Объявление любой функции начинается с указания ключевого слова function следом за которым указывается тип возвращаемого значения, после которого идет идентификатор имени функции. Указание типа можно опустить, тогда будет подразумеваться, что функция возвращает переменную динамического типа. После указания имени функции идет блок описания параметров, или аргументов функции. Блок заключается в круглые скобки; сами параметры, в ходе описания отделяются друг от друга запятой. Завершает конструкцию тело функции, которое записывается в фигурных скобках.

Вот примеры объявления функций: <source lang="kpp"> function MyFunction() { return 5; } function Compare(x, y) { return x < y; } </source>

Первая функция не принимает параметров, но возвращает числовую константу 5. Согласитесь, не очень полезный код. Вторая функция немного "поумнее": она принимает два объекта и пытается их сравнить, используя оператор отношения "меньше". Результатом выполнения такой функции будет логическое значение "истина", если x и вправду меньше чем y, либо ложь в противном случае. Для правильной работы этой функции требуется, чтобы передаваемые параметры допускали возможность сравнения (то есть, были бы определены соответствующие операторы). Если этого нет, — будет сгенерировано исключение, то есть ошибка времени исполнения. Поскольку типы параметров никак не указаны, компилятор не имеет возможности контролировать фактические типы передаваемых параметров, а следовательно не может предупредить программиста если по его мнению что-то не так. Тем не менее, это обеспечивает программиста возможностью написания гибких программ и функций, которые не зависят от конкретных типов передаваемых данных.

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

Аргументы

Аргументы функции — это та информация, которую программист хочет передать в функцию ее для последующей обработки. Как уже было показано ранее, в качестве аргументов функций могут передаваться любые объекты, любых типов. При этом, будет генерироваться динамический код, не привязанный к конкретным типам данных. Такие функции могут применяться в случаях, когда они должны принимать в качестве параметров целый набор объектов различных типов. Однако, это негативно сказывается на производительности кода (нет возможности прямого вызова методов и проверки типов). Чтобы повысить эффективность кода, следует применять типизацию аргументов (рекомендуется).

Типизация аргументов

Если функция подразумевает передачу параметров строго определенного типа, то применяется расширенная форма записи аргументов. При этом, идентификатор имени параметра предворяется именем типа, который следует принимать. Например, вышеописанную функцию Compare() мы можем переписать так, чтобы она принимала в качестве параметров только объекты, представляющие собой целые числа. При этом, имена переменных x и y мы дополняем сведениями о типе:

<source lang="kpp"> function Compare(int x, int y) { return x < y; } </source>

Практически не изменившись, функция из динамической превратилась в статическую. Таким образом, компилятор обладает достаточными сведениями для генерации эффективного, статического кода. Так же, в целях уменьшения вероятности ошибок, при компиляции вызовов такой функции, комилятор будет проверять соответствие типов фактически переданных параметров и типов, указанных в объявлении функции. Если типы различны, то компилятор попытается выполнить операцию приведения типов, если же типы неприводимы — будет сгенерирована ошибка времени компиляции.

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

<source lang="kpp"> function MyFunction(int p1, string p2, block p3) { /* тело функции */ } function OtherFunction(MyClass p1, p2) { /* тело функции */ } </source>

Как видно из кода, функция MyFunction() имеет три параметра: целочисленный p1, строковый p2 и блок p3.

Функция OtherFunction(), в качестве параметров может принимать экземпляры класса MyClass (параметр p1), и объекты любого типа в качестве параметра p2. Обратите внимание, что может показаться что тип MyClass указан для обоих аргументов, но на самом деле это не так. Тип привязывается только к переменной, указанной сразу после него. Более подробно, эта проблема рассмотрена в главе Объявление переменных и констант.

Приведем несколько примеров вызова функции с различными наборами параметров: <source lang="kpp" line="1"> var myblock = { |x| x += 2; } MyFunction(10, "hello", myblock); //верно, типы фактических параметров совпадают MyFunction("20", 10, myblock); //частично верно, выполняется операция приведения MyFunction([1,2,3], myblock, 5); //неверно. переданы неприводимые типы

var o1 = new MyClass; var o2 = new OtherClass; OtherFunction(o1, o2); OtherFunction(o2, o1); OtherFunction(o1, o1); OtherFunction(o2, o2); </source>


1
Мы создаем экземпляр переменной-блока, который будет передаватсья в качестве параметра функциям. Здесь он не имеет особого значения, так что на него можно практически не обращать внимания (важен только его тип).
2-4
Производятся вызовы функции MyFunction() с различными наборами параметров. В первом вызове типы фактических параметров точно совпадают с типами в описании функции, следовательно никакого приведения не происходит и все работает как есть. Во втором вызове происходит приведение строковой константы "20" к числу, а числа 10 к строке. "Частично" верна эта конструкция потому, что на момент компиляции невозможно определить, сработает ли первая операция приведения (строки к числу) или нет. Если строка содержит нецифровые символы, то в результате операции приведения будет сгенерировано исключение. Разумеется в данном случае, все будет хорошо. Второе приведение, а именно числа 20 к строке так же выполнится успешно, потому что абсолютно любое число можно представить в виде строки символов, соответствующих цифрам числа.
Третий вызов функции MyFunction() является совершенно неверным, поскольку все фактические параметры переданные в функцию, являются неприводимыми к соответствующим типам формальных параметров.
6-11
Здесь создаются две инстанции классов MyClass и некоторого класса OtherClass. Далее выполняется вызов функции OtherFunction(), которой в разных комбинациях передаются вышесозданные объекты. В качестве упражнения, Читателю предлагается самому решить, какие из вызовов верные а какие нет.
Постарайтесь ответить на следующие вопросы:
  • Какие из вызовов приведут к ошибке времени компиляции?
  • Какие из вызовов приведут к исключению (ошибке времени исполнения)?
  • Что изменится, если реализовать оператор приведения типа MyClass к OtherClass?
  • Что изменится, если реализовать оператор приведения типа OtherClass к MyClass?
  • Что изменится, если типы будут взаимноприводимыми?

Инициализаторы аргументов (значения по умолчанию)

<source lang="kpp"> function MyFunction(

   int p1 = 5, 
   string p2 = "hello world", 
   block p3 = { |x| x + 1; }

) { /* тело функции */ } </source>

Модификаторы и копирование

Возврат значения

Локальные переменные

Объявление

Область видимости

Экспортирование функций

Перегрузка функций и операторов

Персональные инструменты
Пространства имён

Варианты
Действия
Навигация
информация
документация
Инструменты