Как сделать перегрузку функции c

Обновлено: 07.07.2024

Тема замечательная - перегрузка функций в языке C++ . Она несколько перекликается с темой шаблонов функций . Но перегрузка мне нравится больше. Да, шаблоны позволяют сократить размер программы, но перегрузка делает текст программы понятнее и красивее.

В чем суть перегрузки функций в C++

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

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

Пример перегрузки функций в языке C++

Посмотрите на пример программы ov4000.cpp . В программе определены два множества перегруженных функций: add и print . В первой группе add означает сложение для числовых входных параметров и конкатенацию для строковых переменных. Во второй группе все функции выводят на консоль входные параметры разных типов.

Ну, до скорого на моем канале Old Programmer . Жду вас. Подписывайтесь и ставьте "ЛАЙКИ".

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

Говорят, что язык 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);
>

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

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

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

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

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

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

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

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

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

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

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

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

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

Здравствуйте! Пытаюсь сделать универсальную функцию, которая работает с переменным числом аргументов. Дело в том что библиотека с этой функцией должна работать как в ARM-коде на Си, так и в Windows-приложении на С++. Ввиду того что эта библиотека компилируется разными компиляторами, то для доступа к аргументам va_start() va_arg() и va_end() не везде работают. Стал делать указателями. Знаю что аргументы передаются через стек, и компиляторы делают различные выравнивания адресов в стеке (32 или 64 бита). Делать копии библиотек под разные компиляторы нельзя - её ещё редактировать и развивать надо - запутаюсь в этих версиях.
Но вот под Windows столкнулся с такой проблемой:

0 0 0 44 254 40 0
167 137 193 0 44 254 40 0
84 206 40 19 88 206 40 0
50 27 64 0 0 0 0
0 0 0 20 0 0 0
0 0 0 40 0 0 0
.

То есть по-сути данные начинаются с 28 байта через 4 (выравнивание адреса в стеке)
а вот если сделать функцию таким образом:

206 40 0 0 0 0
44 254 40 0 167 137 185 0
44 254 40 0 84 206 40 23
88 206 40 0 56 27 64 0
0 0 0 10 0 0 0 0
20 0 0 0 30 0 0 0
0 0 0 0 0 0 0
.

Вопрос - что должна делать функция и какой тип у ее аргументов? Пожалуйста, не вставляйте в комментарии HTML код, потому что он будет показан полностью. Начало и конец кода: code и /code заключаются в квадратные скобки. Перенос строки - br/ в квадратных скобках.

void my_func(uint8_t a, . ) uint8_t * pp = &a;
pp += step_stack;
uint8_t p1;
p1 = *pp;
pp += step_stack;
uint8_t p2;
p2 = *pp;
pp += step_stack;
uint8_t p3;
p3 = *pp;
pp += step_stack;
uint8_t p4;
p4 = *pp;
.
>

как определялась переменная step_stack уже не помню, вроде как я делал какую-то хитрую функцию с переменным числом аргументов и она в процессе инициализации рассчитывала этот step_stack независимо от компилятора. Сейчас восстановить исходники той программы не могу.
В данный момент мне нужно сделать библиотеку с несколькими подобными функциями, и вот эту портянку хотел бы заменить макросом чтобы более лаконичный текст был. Хотел сделать указатель на структуру, присвоить ему адрес переменной (&a) и далее в теле функции обращаться к значениям параметров как к полям структуры - когда текст скомпилируется в ассемблерный, то там не должно быть никаких дополнительных команд, только смещение адреса от вершины стека. Но компилятор MinGW создает какой-то непонятный кадр в стеке при вызове такой функции.

Если все аргументы имеют одинаковый тип, то почему не создать массив? И передать в функцию указатель на массив и размер массива - все элементы будут доступны в функции

Это будет совсем не лаконичный вызов функции. В каждом модуле, вызывающем функцию, надо будет городить свои массивы и отслеживать чтобы не наложились друг на друга при вызове как из основного цикла, так и из прерываний с различными приоритетами. То есть делать ещё какую-то буферную inline функцию (потеря процессорного времени) или макрос. Вызов должен быть максимально лаконичным и эффективным как strlen(). А так как в чистом си нет перегрузки функций как в С++, то приходится идти таким путём.
Сделать функцию с максимальным количеством аргументов и вызывать, заполняя ненужные нулями - тоже не лучший выход. Дело в том что в одной программе требуется максимум 2 дополнительных аргумента, в другой 6. Операция вызова функции помещает в стек 2 байта или +4 ненужных нуля.
Если передавать как составные байты 32-битного параметра - это будет лапша-код при вызове функции и дополнительные операции на упаковку/распаковку.
Написать 12 функций my_func2(a,p1,p2); my_func3(a,p1,p2,p3); my_func4(a,p1,p2,p3,p4) и т.д. как-то корявенько это.
Важно чтобы при написании и отладке более высокоуровневых модулей программы не отвлекаться на это. Вызов функции в тексте программы должен быть максимально лаконичным, удобочитаемым, интуитивно-понятным.
Я могу конечно расписать задачу более подробно - почему и так не так, и эдак не пойдёт. Думаю это никому не интересно, так как у каждого свои спектры приоритетов.
Вопрос мой в том - как максимально эффективно ловить аргументы в функции с переменным количеством аргументов? Пока вижу только step_stack в сборке проекта прописывать каждому компилятору опцию -Dstep_stack=4 (как предопределённый define). А в теле функции макрос типа

Перфекционизм в этом месте нужен максимальный. И на ассемблере универсально не могу сделать - у ARM и у PC разные наборы инструкций, если ARM ещё хоть как-то понимаю, то с PC-ассемблером у меня дружбы пока нет.
C MinGW32 мне наверно надо самостоятельно разобраться почему так криво стек наполняется. На MinGW64 работало штатно через 8 байт без всякого "мусора".

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

Поправьте меня, если ошибаюсь, при inline функция\процедура загружается в память с запуском программы, без inline - в момент вызова. Т.е. если функция вызывается откуда-нибудь из цикла, то для избежания постоянных выделений\освобождений памяти для нее ее желательно инлайнить. НО, inline может быть проигнорирован компилятором, т.к. он по своему принципу определяет, что нужно инлайнить, а что нет(сугубо мое субъективное мнение).

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

выдаст ошибку из-за неоднозначности толкования sum(). *** Елена, не могли бы вы пояснить в чем заключается неоднозначность толкования.

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

Видеоурок

Что такое перегрузка методов?

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

Как создать перегрузку?

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

Теперь при вызове метода компилятор будет понимать какие параметры были переданы и в зависимости от этого будут вызываться разные методы.

В этом руководстве мы узнаем о перегрузке функций в C++ на примерах.

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

Эти функции, имеющие одинаковое имя, но разные аргументы, известны, как перегруженные функции. Например:

Здесь все 4 функции являются перегруженными.

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

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

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

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

В этой программе мы перегружаем функцию absolute(). В зависимости от типа параметра, переданного во время вызова функции, вызывается соответствующая функция.

Перегрузка функций с использованием разного количества параметров

Здесь функция display() вызывается трижды с разными аргументами. В зависимости от количества и типа переданных аргументов вызывается соответствующая функция display().

Функция display()

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

Примечание. В C++ многие стандартные библиотечные функции перегружены. Например, функция sqrt() может принимать в качестве параметров double, float, int и т.д., поскольку функция перегружена.

Читайте также: