C++/Function prototype

Материал из Энциклопедия о программировании
Перейти к: навигация, поиск

Прототип функции в C и C++ — предварительное объявление функции, i.e. объявление функции, не содержащее тела этой функции, но указывающее имя функции, арность, типы аргументов и возвращаемый тип данных.

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

Значимость

Прототипы функций являются очень важными особенностями языков C и C++.

Расположение

Как правило, прототипы функций указываются в заголовочных файлах.

Понятие прототипа

«Прототип функции» и «объявление функции» (без инициализации) это одно и то же. Прототип функции это декларация функции (function declaration), а определение функции (function definition) это реализация функции. Пример:

void f(int i = 1); // function declaration in header (.h) file 
void f(int i) {} // function definition in code (.cpp) file

Требования к прототипу

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

При создании прототипа важно след.:

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

Всё это должно совпадать в прототипе и определении функции, чтобы компилятор потом их правильно распознал.

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

Значения параметров по-умолчанию указываются только/исключительно в прототипе, и не указываются в реализации.

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

Если был описан прототип функции, то обязательно должна быть её реализация, если это, конечно, не абстрактная/виртуальная или чисто виртуальная функция. Причём, это касается и конструктора, в том числе и конструктора по-умолчанию. Если была его декларация, то обязательно должна быть описана и реализация, хоть и пустая. Иначе будет ошибка!

Объявление функции требуется только в случае её использования. Определение функции может располагаться где угодно в программе.

Параметры со значением по-умолчанию

В отличие от других лангов программирования (Java, C#, etc), где значением по-умолчанию для параметров пишутся сразу в дефинации т.к. деклараций просто нет, в C++ значения по-умолчанию параметров/аргументов функций/методов указываются только в объявлении/декларации прототипа функции/метода, и не могут указываться в определении/реализации функции/метода. Указание значения по-умолчанию для параметра в определении/реализации функции/метода вызывает ошибку компилятора. В компиляторе C++ от Microsoft для IDE Visual Studio это ошибка C2572. Пример:

Декларация функции:

void f(int i = 1);

Дефинация функции:

void f(int i = 1) {} // error C2572

Вместо этого нужна следующая дефинация:

void f(int i) {}

Использование

Уведомление компилятора

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

Пример:

#include <stdio.h>
int foo(int n); // Прототип функции
int main(void) { // Функция, что вызовет функцию foo
  printf("%d\n", foo()); // ОШИБКА: у foo отсутствует аргумент!
  return 0;
}
int foo(int n) { // Вызываемая функция
  if (n == 0)
    return 1;
  else
    return n * foo(n - 1);
}

При реализации этого прототипа компилятор выдаст сообщение об ошибке в main(). Если прототип будет пропущен, то и сообщения об ошибке не будет.

Функция «foo» ожидает аргумент целого типа, находящийся в стеке при вызове. Если прототип пропущен, компилятор не может это обработать и «foo» завершит операцию на некоторых других данных стека (вероятно, это будет обратный адрес или значение переменной, не входящей в область допустимых значений). Включением прототипа функции вы информируете компилятор о том, что функция «foo» принимает один аргумент целого типа и вы тем самым позволяете компилятору обрабатывать подобные виды ошибок.

Создание библиотечных интерфейсов

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

Объявления класса

В C++ прототипы функций также используются в определении классов.

Примеры

Объявление прототипа функции func, которая принимает один аргумент n целого типа int и возвращает целое число int:

int func(int n);

Создание прототипа функции fnc без её определения:

float fnc(float, float = 0.0f, int c = 2);

Ссылки

См. также