Подставляемые (встраиваемые) функции. Перегрузка функций

При определении функций в своих программах вы должны указать тип возвращаемого функцией значения, а также количество параметров и тип каждого из них. В прошлом (если вы программировали на языке С), когда у вас была функция с именем add_values, которая работала с двумя целыми значениями, а вы хотели бы использовать подобную функцию для сложения трех целых значений, вам следовало создать функцию с другим именем. Например, вы могли бы использовать add_two_values иadd_three_values. Аналогично если вы хотели использовать подобную функцию для сложения значений типа float, то вам была бы необходима еще одна функция с еще одним именем. Чтобы избежать дублирования функции, C++ позволяет вам определять несколько функций с одним и тем же именем. В процессе компиляции C++ принимает во внимание количество аргументов, используемых каждой функцией, и затем вызывает именно требуемую функцию. Предоставление компилятору выбора среди нескольких функций называется перегрузкой. В этом уроке вы научитесь использовать перегруженные функции. К концу данного урока вы освоите следующие основные концепции:

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

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

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

ПЕРВОЕ ЗНАКОМСТВО С ПЕРЕГРУЗКОЙ ФУНКЦИЙ

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

#include

int add_values(int a,int b)

{
return(a + b);
)

int add_values (int a, int b, int c)

(
return(a + b + c);
)

{
cout << «200 + 801 = » << add_values(200, 801) << endl;
cout << «100 + 201 + 700 = » << add_values(100, 201, 700) << endl;
}

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

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

#include

void show_message(void)

{
cout << «Стандартное сообщение: » << «Учимся программировать на C++» << endl;
}

void show_message(char *message)

{
cout << message << endl;
}

void show_message(char *first, char *second)

{
cout << first << endl;
cout << second << endl;
}

{
show_message();
show_message(«Учимся программировать на языке C++!»);
show_message(«B C++ нет предрассудков!»,»Перегрузка — это круто!») ;
}

КОГДА НЕОБХОДИМА ПЕРЕГРУЗКА

Одним из наиболее общих случаев использования перегрузки является применение функции для получения определенного результата, исходя из различных параметров. Например, предположим, что в вашей программе есть функция с именем 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. В процессе компиляции C++ определит, какую функцию следует вызвать, основываясь на количестве и типе передаваемых параметров.
  4. Перегрузка функций упрощает программирование, позволяя программистам работать только с одним именем функции.

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

Такие функции называют перегруженными , а сам механизм –перегрузка функций .

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

Поиск функции, которую надо вызвать, осуществляется за три отдельных шага:

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

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

Цель лекции : изучить подставляемые (встраиваемые) функции и перегрузки функций, научиться разрабатывать программы с использованием перегрузки функций на языке 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 using namespace std; inline int Cube(int x); int _tmain(int argc, _TCHAR* argv){ int x=2; float y=3; double z=4; cout<

Перечислим причины, по которым функция со спецификатором inline будет трактоваться как обычная не подставляемая функция :

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

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

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

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

При определении функций в программах необходимо указывать тип возвращаемого функцией значения, а также количество параметров и тип каждого из них. Если на языке С++ была написана функция с именем add_values , которая работала с двумя целыми значениями, а в программе было необходимо использовать подобную функцию для передачи трех целых значений, то тогда следовало бы создать функцию с другим именем. Например, add_two_values и add_three_values . Аналогично, если необходимо использовать подобную функцию для работы со значениями типа float , то нужна еще одна функция с еще одним именем. Чтобы избежать дублирования функции, C++ позволяет определять несколько функций с одним и тем же именем. В процессе компиляции C++ принимает во внимание количество аргументов, используемых каждой функцией, и затем вызывает именно требуемую функцию. Предоставление компилятору выбора среди нескольких функций называется перегрузкой .

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

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

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

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

Например, следующая программа перегружает функцию с именем add_values . Первое определение функции складывает два значения типа int . Второе определение функции складывает три значения типа int . В процессе компиляции C++ корректно определяет функцию, которую необходимо использовать:

#include "stdafx.h" #include using namespace std; int add_values(int a,int b); int add_values (int a, int b, int c); int _tmain(int argc, _TCHAR* argv){ cout << "200+801=" << add_values(200,801) << "\n"; cout << "100+201+700=" << add_values(100,201,700) << "\n"; system("pause"); return 0; } int add_values(int a,int b) { return(a + b); } int add_values (int a, int b, int c) { return(a + b + c); }

Таким образом, программа определяет две функции с именами 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 имя_аргумента); /*недопустимая перегрузка имени: аргументы имеют одинаковое количество и одинаковый тип*/

Преимущества перегрузки функции:

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

Пример 2 .

/*Перегруженные функции имеют одинаковые имена, но разные списки параметров и возвращаемые значения*/ #include "stdafx.h" #include using namespace std; int average(int first_number, int second_number, int third_number); int average(int first_number, int second_number); int _tmain(int argc, _TCHAR* argv){// главная функция int number_A = 5, number_B = 3, number_C = 10; cout << "Целочисленное среднее чисел " << number_A << " и "; cout << number_B << " равно "; cout << average(number_A, number_B) << ".\n\n"; cout << "Целочисленное среднее чисел " << number_A << ", "; cout << number_B << " и " << number_C << " равно "; cout << average(number_A, number_B, number_C) << ".\n"; system("PAUSE"); return 0; }// конец главной функции /*функция для вычисления целочисленного среднего значения 3-х целых чисел*/ int average(int first_number, int second_number, int third_number) { return((first_number + second_number + third_number)/3); } // конец функции /*функция для вычисления целочисленного среднего значения 2-х целых чисел*/ int average(int first_number, int second_number) { return((first_number + second_number)/2); } // конец функции

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

Перегруженное объявление представляет собой объявление, объявленное с тем же именем, что и ранее объявленное объявление в той же области видимости, за исключением того, что обе декларации имеют разные аргументы и, очевидно, другое определение (реализация).

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

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

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

Ниже приведен пример, когда одна и та же функция print () используется для печати разных типов данных -

#include using namespace std; class printData { public: void print(int i) { cout << "Printing int: " << i << endl; } void print(double f) { cout << "Printing float: " << f << endl; } void print(char* c) { cout << "Printing character: " << c << endl; } }; int main(void) { printData pd; // Call print to print integer pd.print(5); // Call print to print float pd.print(500.263); // Call print to print character pd.print("Hello C++"); return 0; }

Printing int: 5 Printing float: 500.263 Printing character: Hello C++

Перегрузка операторов в C ++

Вы можете переопределить или перегрузить большинство встроенных операторов, доступных на C ++. Таким образом, программист может также использовать операторы с определенными пользователем типами.

Перегруженные операторы - это функции со специальными именами: ключевое слово «оператор», за которым следует символ для определяемого оператора. Как и любая другая функция, перегруженный оператор имеет тип возврата и список параметров.

Box operator+(const Box&);

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

Box operator+(const Box&, const Box&);

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

#include using namespace std; class Box { public: double getVolume(void) { return length * breadth * height; } void setLength(double len) { length = len; } void setBreadth(double bre) { breadth = bre; } void setHeight(double hei) { height = hei; } // Overload + operator to add two Box objects. Box operator+(const Box& b) { Box box; box.length = this->length + b.length; box.breadth = this->breadth + b.breadth; box.height = this->height + b.height; return box; } private: double length; // Length of a box double breadth; // Breadth of a box double height; // Height of a box }; // Main function for the program int main() { Box Box1; // Declare Box1 of type Box Box Box2; // Declare Box2 of type Box Box Box3; // Declare Box3 of type Box double volume = 0.0; // Store the volume of a box here // box 1 specification Box1.setLength(6.0); Box1.setBreadth(7.0); Box1.setHeight(5.0); // box 2 specification Box2.setLength(12.0); Box2.setBreadth(13.0); Box2.setHeight(10.0); // volume of box 1 volume = Box1.getVolume(); cout << "Volume of Box1: " << volume <

Когда приведенный выше код компилируется и выполняется, он производит следующий результат:

Volume of Box1: 210 Volume of Box2: 1560 Volume of Box3: 5400

Перегружаемый / Non-overloadableOperators

Ниже приведен список операторов, которые могут быть перегружены.