Функции в С++: перегрузка, значения по умолчанию

В С++, в отличие от языка программирования C, в ситуациях, когда фактические параметры не соответствуют типам подходящих формальных параметров, не предполагаются автоматические изменения. По этой причине часто говорят, что язык С++ гарантирует строгий контроль типов. Из-за этого свойства контроль соответствия типов фактических и формальных аргументов в языке программирования C++ выполняется ещё на этапе компиляции. Обязательное строжайшее соответствие типов между фактическими и формальными аргументами нуждается в том, чтобы до первого обращения к функции в модуле было помещено её определение или описание (прототип), заключающее в себе данные о типе возвращающего значения (результата) и о типах всех аргументов. Такой выбор прототипа или полного определения обеспечивает возможность компилятору выполнять проверку на соответствие типов всех аргументов.

Описание или прототип функции внешне может практически полностью соответствовать заголовку ее определения:

тип имя (СпецификацияФормальныхАгрументов);

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

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

тип имя

Значение по умолчанию

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

тип имя=ЗначениеПоУмолчанию

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

Пример.

Найти значение N в степени K, где наиболее частое значение K = 2. Рекурсивная функция со значением K = 2 по умолчанию:

int pow(int n, int k = 2) // по умолчанию k=2
{
if (k == 2) return(n*n);
else return(pow(n, k — 1)*n);
}

Вызвать данную функцию можно, используя два способа:

t = pow(i); // t = i*i;
q = pow(i, 5); // q = i*i*i*i*i;

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

Перегрузка функций

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

int max(int *a, int n);
float max(float *a, int n);
double max(double *a, int n);

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

int sum(int a, int b=1) { return(a+b); }
int sum(int a) { return(a+a); }

то вызов

int r = sum(2); // ошибка

из-за неоднозначности толкования sum() выведется ошибка.

Встраиваемые функции

В стандартном варианте языка С директива препроцессора #define дает возможность воспользоваться макроопределениями для записи вызова малых, частых в использовании структур. По причине некорректной записи макроопределения могут возникать ошибки, найти которые достаточно сложно. Макроопределения не дают возможности квалифицировать локальные переменные и не производят проверку и преобразование аргументов. В случае, когда используется не макроопределение, а функция, тогда объектный код удлиняется, а время работы программы увеличивается. Более того, во время работы с макроопределениями нужно досконально отслеживать раскрытие макросов, к примеру

#define SUMMA(a, b) a + b
rez = SUMMA(x, y)*10;

После выполнения процессором работы получим:

rez = x + y*10;

В C++ также может использовать ключевое слово inline, с целью определить функцию, которая должна встраиваться как макроопределение. Результатом обращения к такой функции является внесение кода inline-функции внутрь вызывающей программы. Идентификация такой функции может иметь такой вид:

inline double SUMMA(double a, double b)
{
return(a + b);
}

При вызове этой функции

rez = SUMMA(x,y)*10;

результатом будет являться:

rez=(x+y)*10;

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

При задании и использовании встраиваемых функций нужно соблюдать некоторые правила:

  1. Задание и объявление функций должны быть соединены и находиться перед первичным вызовом встраиваемой функции;
    2. Есть смысл в том, чтобы определять inline лишь функции небольшого размера, так как любая inline-функция расширяет программный код;
    3. Сложность встраиваемых функций ограничивается различными компиляторами. Компилятор самостоятельно принимает решение, будет ли функция встраиваемой. Если она не может быть встраиваемой, тогда компилятор берет её в рассмотрение как обычную функцию.

Заключение.

Итак, используя ключевые слова inline для встраиваемых функций и const для идентификации констант, возможно практически полное исключение директив препроцессора #define из употребления.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *