Перегрузка функций. Подставляемые (встраиваемые) функции. Перегрузка функций Что такое перегрузка функции в языке с
Аннотация: В лекции рассматриваются понятия, объявление и использование в программах подставляемых и перегруженных функций в С++, механизмы выполнения подстановки и перегрузки функций, рекомендации по повышению эффективности программ за счет перегрузки или подстановки функций.
Цель лекции : изучить подставляемые (встраиваемые) функции и перегрузки функций, научиться разрабатывать программы с использованием перегрузки функций на языке C++.
Подставляемые функции
Вызов функции , передача в нее значений, возврат значения – эти операции занимают довольно много процессорного времени. Обычно при определении функции компилятор резервирует в памяти только один блок ячеек для сохранения ее операторов. После вызова функции управление программой передается этим операторам, а по возвращении из функции выполнение программы возобновляется со строки, следующей после вызова функции.
При неоднократных вызовах каждый раз программа будет отрабатывать один и тот же набор команд, не создавая копий для каждого вызова в отдельности.
Каждый переход к области памяти, содержащей операторы функции, замедляет выполнение программы. Если функция занимает небольшой объем, то можно получить выигрыш во времени при многократных вызовах, дав компилятору команду встроить код функции непосредственно в программу по месту вызова. Такие функции называется подставляемыми . В этом случае, говоря об эффективности, прежде всего, подразумевается скорость выполнения программы.
Подставляемые или встраиваемые (inline) функции – это функции, код которых вставляется компилятором непосредственно на место вызова, вместо передачи управления единственному экземпляру функции.
Если функция является подставляемой, компилятор не создает данную функцию в памяти, а копирует ее строки непосредственно в код программы по месту вызова. Это равносильно вписыванию в программе соответствующих блоков вместо вызовов функций. Таким образом, спецификатор inline определяет для функции так называемое внутреннее связывание , которое заключается в том, что компилятор вместо вызова функции подставляет команды ее кода. Подставляемые функции используют, если тело функции состоит из нескольких операторов.
Этот подход позволяет увеличить скорость выполнения программы, так как из программы исключаются команды микропроцессора , требующиеся для передачи аргументов и вызова функции.
Например:
/*функция возвращает расстояние от точки с координатами(x1,y1) до точки с координатами (x2,y2)*/ inline float Line(float x1,float y1,float x2, float y2) { return sqrt(pow(x1-x2,2)+pow(y1-y2,2)); }
Однако следует обратить внимание, что использование подставляемых функций не всегда приводит к положительному эффекту. Если такая функция вызывается в программном коде несколько раз, то во время компиляции в программу будет вставлено столько же копий этой функции, сколько ее вызовов. Произойдет значительное увеличение размера программного кода, в результате чего ожидаемого повышения эффективности выполнения программы по времени может и не произойти.
Пример 1 .
#include "stdafx.h"
#include Перечислим причины, по которым функция
со спецификатором inline
будет трактоваться как обычная не подставляемая функция
: Ограничения на выполнение подстановки в основном зависят от реализации. Если же для функции со спецификатором inline
компилятор
не может выполнить подстановку из-за контекста, в который помещено обращение к ней, то функция
считается статической и выдается предупреждающее сообщение
. Еще одной из особенностей подставляемых функций является невозможность их изменения без перекомпиляции всех частей программы, в которых эти функции вызываются. При определении функций в программах необходимо указывать тип возвращаемого функцией значения, а также количество параметров и тип каждого из них. Если на языке С++ была написана функция
с именем add_values
, которая работала с двумя целыми значениями, а в программе было необходимо использовать подобную функцию для передачи трех целых значений, то тогда следовало бы создать функцию с другим именем. Например, add_two_values
и add_three_values
. Аналогично, если необходимо использовать подобную функцию для работы со значениями типа float
, то нужна еще одна функция
с еще одним именем. Чтобы избежать дублирования функции, C++ позволяет определять несколько функций с одним и тем же именем. В процессе компиляции C++ принимает во внимание количество аргументов, используемых каждой функцией, и затем вызывает именно требуемую функцию. Предоставление компилятору выбора среди нескольких функций называется перегрузкой
. Перегрузка функций
– это создание нескольких функций с одним именем, но с разными параметрами. Под разными параметрами понимают, что должно быть разным количество аргументов
функции и/или их тип
. То есть перегрузка
функций позволяет определять несколько функций с одним и тем же именем и типом возвращаемого значения. Перегрузка
функций также называется полиморфизмом функций
. "Поли" означает много, "морфе" – форма, то есть полиморфическая функция
– это функция
, отличающаяся многообразием форм. Под полиморфизмом
функции понимают существование в программе нескольких перегруженных версий функции, имеющих разные значения. Изменяя количество или тип параметров, можно присвоить двум или нескольким функциям одно и тоже имя. При этом никакой путаницы не будет, поскольку нужная функция
определяется по совпадению используемых параметров. Это позволяет создавать функцию, которая сможет работать с целочисленными, вещественными значениями или значениями других типов без необходимости создавать отдельные имена для каждой функции. Таким образом, благодаря использованию перегруженных функций
, не следует беспокоиться о вызове в программе нужной функции, отвечающей типу передаваемых переменных. При вызове перегруженной функции компилятор
автоматически определит, какой именно вариант функции следует использовать. Например, следующая программа
перегружает функцию с именем add_values
. Первое определение
функции складывает два значения типа int
. Второе определение
функции складывает три значения типа int
. В процессе компиляции C++ корректно определяет функцию, которую необходимо использовать: #include "stdafx.h"
#include Таким образом, программа
определяет две функции с именами add_values
. Первая функция
складывает два значения, в то время как вторая складывает три значения одного типа int
. Компилятор
языка С++ определяет, какую функцию следует использовать, основываясь на предлагаемых программой параметрах. Одним из наиболее общих случаев использования перегрузки
является применение функции для получения определенного результата, исходя из различных параметров. Например, предположим, что в программе есть функция с именем day_of_week
, которая возвращает текущий день недели (0 для воскресенья, 1 для понедельника, ... , 6 для субботы). Программа могла бы перегрузить эту функцию таким образом, чтобы она верно возвращала день недели, если ей передан юлианский день в качестве параметра, или если ей переданы день, месяц и год. int day_of_week(int julian_day) {
// операторы
}
int day_of_week(int month, int day, int year) {
// операторы
} При использовании перегруженных функций
часто допускается ряд ошибок. Например, если функции отличаются только типом возвращаемого значения, но не типами аргументов, такие функции не могут иметь одинаковое имя. Также недопустим следующий вариант перегрузки
: int имя_функции(int имя_аргумента);
int имя_функции(int имя_аргумента);
/*недопустимая перегрузка имени: аргументы имеют одинаковое количество и одинаковый тип*/ Пример 2
. /*Перегруженные функции имеют одинаковые имена, но разные списки параметров и возвращаемые значения*/
#include "stdafx.h"
#include При определении функций в своих программах вы должны указать тип возвращаемого функцией значения, а также количество параметров и тип каждого из них. В прошлом (если вы программировали на языке С), когда у вас была функция с именем add_values, которая работала с двумя целыми значениями, а вы хотели бы использовать подобную функцию для сложения трех целых значений, вам следовало создать функцию с другим именем. Например, вы могли бы использовать add_two_values иadd_three_values. Аналогично если вы хотели использовать подобную функцию для сложения значений типа float, то вам была бы необходима еще одна функция с еще одним именем. Чтобы избежать дублирования функции, C++ позволяет вам определять несколько функций с одним и тем же именем. В процессе компиляции C++ принимает во внимание количество аргументов, используемых каждой функцией, и затем вызывает именно требуемую функцию. Предоставление компилятору выбора среди нескольких функций называется перегрузкой. В этом уроке вы научитесь использовать перегруженные функции. К концу данного урока вы освоите следующие основные концепции: Перегрузка функций позволяет вам использовать одно и то же имя для нескольких функций с разными типами параметров. Для перегрузки функций просто определите две функции с одним и тем же именем и типом возвращаемого значения, которые отличаются количеством параметров или их типом. Перегрузка функций является особенностью языка C++, которой нет в языке С. Как вы увидите, перегрузка функций достаточно удобна и может улучшить удобочитаемость ваших программ. Перегрузка функций позволяет вашим программам определять несколько функций с одним и тем же именем и типом возвращаемого значения. Например, следующая программа перегружает функцию с именемadd_values. Первое определение функции складывает два значения типаint. Второе определение функции складывает три значения. В процессе компиляции C++ корректно определяет функцию, которую необходимо использовать: #include int add_values(int a,int b) { int add_values (int a, int b, int c) ( { Как видите, программа определяет две функции с именами add_valuesПервая функция складывает два значения типа int, в то время как вторая складывает три значения. Вы не обязаны что-либо предпринимать специально для того, чтобы предупредить компилятор о перегрузке, просто используйте ее. Компилятор разгадает, какую функцию следует использовать, основываясь на предлагаемых программой параметрах. Подобным образом следующая программа MSG_OVR.CPP перегружает функцию show_message. Первая функция с именем show_message выводит стандартное сообщение, параметры ей не передаются. Вторая выводит передаваемое ей сообщение, а третья выводит два сообщения: #include void show_message(void) { void show_message(char *message) { void show_message(char *first, char *second) { { Одним из наиболее общих случаев использования перегрузки является применение функции для получения определенного результата, исходя из различных параметров. Например, предположим, что в вашей программе есть функция с именем day_of_week, которая возвращает текущий день недели (0 для воскресенья, 1 для понедельника, …, 6 для субботы). Ваша программа могла бы перегрузить эту функцию таким образом, чтобы она верно возвращала день недели, если ей передан юлианский день в качестве параметра, или если ей переданы день, месяц и год: int day_of_week(int julian_day) { int day_of_week(int month, int day, int year) { По мере изучения объектно-ориентированного программирования в C++, представленного в следующих уроках, вы будете использовать перегрузку функций для расширения возможностей своих программ. Перегрузка функций улучшает удобочитаемость программ Перегрузка функций C++ позволяет вашим программам определять несколько функций с одним и тем же именем. Перегруженные функции должны возвращать значения одинакового типа*, но могут отличаться количеством и типом параметров. До появления перегрузки функций в C++ программисты языка С должны были создавать несколько функций с почти одинаковыми именами. К сожалению программисты, желающие использовать такие функции, должны были помнить, какая комбинация параметров соответствует какой функции. С другой стороны, перегрузка функций упрощает задачу программистов, требуя, чтобы они помнили только одно имя функции.* Перегруженные функции не обязаны возвращать значения одинакового типа по той причине, что компилятор однозначно идентифицирует функцию по ее имени и набору ее аргументов. Для компилятора функции с одинаковыми именами, но различными типами аргументов - разные функции, поэтому тип возвращаемого значения - прерогатива каждой функции. - Прим.перев. Перегрузка функций позволяет вам указать несколько определений для одной и той же функции. В процессе компиляции C++ определит, какую функцию следует использовать, основываясь на количестве и типе передаваемых параметров. Из данного урока вы узнали, что перегружать функции достаточно просто. Из урока 14 вы узнаете, как ссылки C++ упрощают процесс изменения параметров внутри функций. Однако, прежде чем перейти к уроку 14, убедитесь, что вы изучили следующие основные концепции: В языке С++
реализована возможность использования
одного идентификатора для функций,
выполняющих различные действия над
различными типами данных, в результате
чего можно использовать несколько
функций с одним и тем же именем, но с
разными списками параметров, как по
количеству, так и по типу. Такие функции
называют перегруженными
, а сам
механизм –перегрузка
функций
. Компилятор
определяет, к какой из функций с одним
и тем же именем следует обратиться
путем сравнения типов фактических
аргументов с типами формальных параметров
в заголовках всех этих функций, т.е.
компилятор в зависимости от типа и
количества аргументов будет формировать
необходимое обращение к соответствующей
функции. Поиск функции,
которую надо вызвать, осуществляется
за три отдельных шага: 1. Поиск функции
с точным соответствием параметров и
ее использование, если она найдена. 2. Поиск соответствующей
функции, используя встроенные
преобразования типов данных. 3. Поиск соответствующей
функции, используя преобразования,
определенные пользователем. Приведем пример
функции S
1 с двумя
параметрамих
,у
, работающая в
зависимости от типа передаваемых
аргументов, следующим образом: – если тип
параметров целочисленный, функция S
1
складывает их значения и возвращает
полученную сумму; – если тип
параметров long
, функцияS
1 перемножает их
значения и возвращает полученное
произведение; – если тип
параметров вещественный, функция S
1
делит их значения и возвращает частное
от деления. #
include int
S1 (int x, int y) { long
S1 (long x, long y) { double
S1 (double x, double y) { int
a = 1, b = 2, c; long
i = 3, j = 4, k; double
x = 10, y = 2, z; printf("\n
c = %d; k = %ld; z = %lf . \n", c, k, z); В результате
получим: c
= 3;
k
= 12;
z
= 5.000000 .
Многоточие в
списке параметров пользовательской
функции используется тогда, когда число
аргументов заранее неизвестно. При
этом неопределенное количество
параметров можно указать в ее прототипе
следующим образом: void
f1
(int
a
,
double
b
, …);
Такая запись
указывает компилятору на то, что за
обязательными фактическими аргументами
для параметров a
иb
могут следовать, а могут и не следовать
другие аргументы при вызове этой
функции. Перечислим
основные особенности использования
данного механизма. 1. Используется
несколько макрокоманд для доступа к
параметрам таких функций, это: va
_
list
иva
_
start
– макрокоманды подготовки доступа к
параметрам; va
_
arg
– использование параметров; va
_
end
– отчистка перед выходом. Они
объявлены в заголовочном файле stdarg
.
h
. 2. Такая функция
должна иметь минимум один параметр
(именованный) для передачи ей количества
передаваемых аргументов. 3. Для макроса
va
_
start
необходимо передать два аргумента –
имя списка параметров, который задаетva
_
list
и их количество. 4. Нарушать указанный
порядок макрокоманд нельзя. Иначе можно
получить непредсказуемые последствия. 5. Для макроса
va
_
arg
нужно помимо имени списка параметров
передать и предполагаемый тип. При
несоответствии типов – ошибка. Использование
многоточий полностью выключает проверку
типов параметров. Многоточие необходимо,
только если изменяются и число
параметров, и их тип. Следующий пример
иллюстрирует эту возможность. #include
#include
void
f1(double s, int n ...) { va_start(p,
n); printf("
\n Double S = %lf ", s); for(int
i=1; i<=n; i++) { v
= va_arg(p, int); printf("\n
Argument %d = %d ", i, v); void
main(void) { f1(1.5,
3, 4, 5, 6); В результате
получим: Double
S
= 1.500000
Argument
1 = 4
Argument 2 = 5
Argument 3 = 6
Press
any key to continue
Под перегрузкой функции понимается, определение нескольких функций (две или больше) с одинаковым именем, но различными параметрами. Наборы параметров перегруженных функций могут отличаться порядком следования, количеством, типом. Таким образом перегрузка функций нужна для того, чтобы избежать дублирования имён функций, выполняющих сходные действия, но с различной программной логикой. Например, рассмотрим функцию areaRectangle() , которая вычисляет площадь прямоугольника. Float areaRectangle(float, float) //функция, вычисляющая площадь прямоугольника с двумя параметрами a(см) и b(см)
{
return a * b; // умножаем длинны сторон прямоугольника и возвращаем полученное произведение
}
Итак, это функция с двумя параметрами типа float , причём аргументы передаваемые в функцию должны быть в сантиметрах, возвращаемое значение типа float — тоже в сантиметрах. Предположим, что наши исходные данные (стороны прямоугольника) заданы в метрах и сантиметрах, например такие: a = 2м 35 см; b = 1м 86 см. В таком случае, удобно было бы использовать функцию с четырьмя параметрами. То есть, каждая длинна сторон прямоугольника передаётся в функцию по двум параметрам: метры и сантиметры. Float areaRectangle(float a_m, float a_sm, float b_m, float b_sm) // функция, вычисляющая площадь прямоугольника с 4-мя параметрами a(м) a(см); b(м) b(cм)
{
return (a_m * 100 + a_sm) * (b_m * 100 + b_sm);
}
В теле функции значения, которые передавались в метрах (a_m и b_m)переводятся в сантиметры и суммируются с значениями a_sm b_sm , после чего перемножаем суммы и получаем площадь прямоугольника в см. Конечно же можно было перевести исходные данные в сантиметры и пользоваться первой функцией, но сейчас не об этом. Теперь, самое главное – у нас есть две функции, с разной сигнатурой, но одинаковыми именами (перегруженные функции). Сигнатура – это комбинация имени функции с её параметрами. Как же вызывать эти функции? А вызов перегруженных функций ничем не отличается от вызова обычных функций, например: AreaRectangle(32, 43); // будет вызвана функция, вычисляющая площадь прямоугольника с двумя параметрами a(см) и b(см)
areaRectangle(4, 43, 2, 12); // будет вызвана функция, вычисляющая площадь прямоугольника с 4-мя параметрами a(м) a(см); b(м) b(cм)
Как видите, компилятор самостоятельно выберет нужную функцию, анализируя только лишь сигнатуры перегруженных функций. Минуя перегрузку функций, можно было бы просто объявить функцию с другим именем, и она бы хорошо справлялась со своей задачей. Но представьте, что будет, если таких функций надо больше, чем две, например 10. И для каждой нужно придумать осмысленное имя, а сложнее всего их запомнить. Вот именно поэтому проще и лучше перегружать функции, если конечно в этом есть необходимость. Исходный код программы показан ниже. #include "stdafx.h"
#include // код Code::Blocks // код Dev-C++ #include Результат работы программы показан на рисунке 1.Перегрузка функции
Использование перегрузки функции
Преимущества перегрузки функции:
ПЕРВОЕ ЗНАКОМСТВО С ПЕРЕГРУЗКОЙ ФУНКЦИЙ
return(a + b);
)
return(a + b + c);
)
cout << «200 + 801 = » << add_values(200, 801) << endl;
cout << «100 + 201 + 700 = » << add_values(100, 201, 700) << endl;
}
cout << «Стандартное сообщение: » << «Учимся программировать на C++» << endl;
}
cout << message << endl;
}
cout << first << endl;
cout << second << endl;
}
show_message();
show_message(«Учимся программировать на языке C++!»);
show_message(«B C++ нет предрассудков!»,»Перегрузка — это круто!») ;
}КОГДА НЕОБХОДИМА ПЕРЕГРУЗКА
// Операторы
}
// Операторы
}ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ
Пример перегрузки функций
Функции с переменным числом параметров