Учебный пример перегруженных функций. Иллюстрация перегрузки

Содержание

Встраивание

C++ предоставляет ключевое слово inline. Оно располагается перед объявлением функции, когда программист желает, чтобы код, составляющий тело функции, встраивался по месту вызова функции. Вы уже знаете, что при вызове функции выполнение программы "перепрыгивает" в тело функции и исполняются инструкции, описанные в функции. После исполнения инструкций функции выполнение программы возвращается туда, откуда была вызвана функция (на следующую строку за вызовом функции), и с нее продолжается выполнение программы дальше. Поэтому вызов функции занимает определенное количество времени. При объявлении функции как inline С++ каждый вызов этой функции заменяет телом этой функции. Давайте рассмотрим эту возможность на примере функции вычисляющей куб числа типа double:

inline double cube(double x) { return (x * x * x); }

При вызове этой функции, объявленной как inline

double res=cube(2.55);

на самом деле вместо этой строки С++ сам поставит такую строку:

double res=2.55*2.55*2.55;

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

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

Обычно для размещения в программе встроенного кода используют вызов функции, однако существует и другая схема — раскрытие макро. Директива препроцессора #define поддерживает макроподстановку, как показано в следующем примере:

#define SQR(X) ((X) * (X)) #define CUBE(X) (SQR(X)*(X)) #define ABS(X) (((X) < 0)? -(X): X)void main() { у = SQR(t + 8) - CUBE(t - 8); cout <<sqrt(ABS(y)); }

Здесь объявлены три макроса sqr(x), cube(x) и abs(x) с помощью директивы #define и далее в функции main происходит вызов этих макросов по имени. Препроцессор раскрывает макро и передает получившийся текст компилятору. Так, приведенное выше равносильно:

void main() { у = ((t+8) * (t+8)) - ((((t-8)) * (t-8)) * (t-8)); cout << sqrt(((y < 0)? -(y): y)); }

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

#define SQR(X) X * X у = SQR(t + 8); //раскроет макро t+8*t+8

То есть в приведенном выше примере при вызове макроса sqr сначала выполнится умножение 8 на t, а потом к результату прибавится значение переменной t и восьмерка, хотя нашей целью было получение квадрата суммы t+8.

Еще одна проблема возникает, когда макро приводит к вычислению параметра более одного раза, в то время как подразумевалось, что аргумент должен вычисляться только один раз, как при вызове функции. То есть вы уже видели, что при вызове макроса cube он на самом деле заменяется несколькими операциями над выражением t-8, таким образом, эта разность будет вычисляться несколько раз, что требует немного дополнительного времени.

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

Продолжаем исследовать волшебный мир С++! Как показывает опыт, большинство людей, которые изучают язык программирования, или совсем не обращают, или не уделяют особого внимания вопросу связанному с именами функций. Подумайте, как назвать переменную или функцию? Если у Вас появился ответ: "Главное чтобы функция работала, а как она будет называться неважно..." - то он имеет право на существование, но в большинстве ситуаций - неправильный. Объясним почему. Вы уже освоили азы С++, разработали большое количество программ. При создании программ у Вас вырабатывается собственный стиль написания (почти как почерк). Если проект разрабатывается группой программистов (а это все "серьезные" программы, такие как Corel, Photoshop и т.д.), то немаловажно, чтобы Ваш стиль написания был понятен другим программистам.

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

Перегрузка (от англ. overloading) использует одно и то же имя для нескольких вариантов функции.

Другими словами, несколько функций могут иметь одинаковые имена. Но тогда появляется вопрос: «А как компилятор сможет различить две одинаковые функции?» Т.е. если имена функций одинаковы, то как компилятор узнает, какую функцию необходимо вызвать в данный момент?.

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

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

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

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

Отметим, что в ранних версиях С++ в начале области видимости, в которых выполнялась перегрузка, требовалось ключевое слово overload. Но, спешим Вас обрадовать, в Visual C++ это слово не применяется и, более того, исключено из списка ключевых слов.

Рассмотрим пример перегруженных функций.

#include <iostream.h>#include <string.h>int noName(int first) //Первая функция{return first*first;}int noName(unsigned int first) //Вторая функция c тем же именем{return -first*first;}char noName(char first) //Третья функция c тем же именем{return first+3;}int noName(int first, char *second) //Четвертая функция c тем же именем{return first*strlen(second);}int noName(int first, char second) //Пятая функция c тем же именем{return first*second;}float noName(float r) //Шестая функция c тем же именем{return r*r;}double noName(double r) //Седьмая функция c тем же именем{return r*r;}void main(){cout<<noName(4)<<endl; //Вызов первой функцииcout<<noName((unsigned)4)<<endl;//Вызов второй функцииcout<<noName('a')<<endl; //Вызов третьей функцииcout<<noName(4,"abc")<<endl; //Вызов четвертой функцииcout<<noName(4,'a')<<endl; //Вызов пятой функцииcout<<noName((float)1.2)<<endl; //Вызов шестой функцииcout<<noName((double)1.2)<<endl;//Вызов седьмой функции}

Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: