что такое перегрузка функции в c
BestProg
Перегрузка функций. Перегрузка функций в классах. Перегрузка конструкторов класса. Доступ к перегруженной функции по указателю. Примеры
Содержание
Поиск на других ресурсах:
1. Какие функции называются «перегруженными»? Что означает термин «перегрузка»?
«Перегрузка» функции – это объявление функции с тем же именем несколько раз. Таким образом, в некоторой области видимости имя «перегруженной» функции объявляется несколько раз. Чтобы компилятор мог отличать «перегруженные» функции между собой, эти функции должны отличаться списком входных параметров.
В общем случае, объявление перегруженной функции в некоторой области видимости выглядит следующим образом:
2. По каким признакам отличаются перегруженные функции? Пример
Перегруженные функции отличаются по списку параметров. Списки параметров перегруженных функций должны отличаться по следующим признакам:
Например. Функция Max() есть перегруженная и отличается количеством параметров и типами параметров
3. Примеры перегрузки функций
Программный код, который демонстрирует использование функции Equal() следующий:
Программный код, демонстрирующий применение функции следующий:
4. Пример перегрузки функции в классе
Программный код, демонстрирующий применение «перегрузки» функций в классе имеет следующий вид:
5. Могут ли считаться перегруженными функции, которые имеют одинаковые имена, одинаковое количество и типы параметров, но которые возвращают значение разных типов?
Нет. Компилятор распознает перегруженные функции только по получаемым параметрам. Если две функции имеют одинаковые имена, одинаковое количество и типы параметров но возвращают разные значения, то такие функции считаются одинаковыми. В этом случае компилятор выдаст ошибку:
Например. В нижеследующем коде объявляются две функции с именем Inc5() :
Как видно из примера, функции возвращают значение разных типов. Поскольку функции имеют одинаковые имена, одинаковое количество и типы параметров, то вышеприведенный код выдаст ошибку компилятора
6. Для чего используется перегрузка конструкторов класса? Преимущества перегрузки конструкторов класса
Перегрузка конструкторов класса дает следующие преимущества:
7. Пример перегрузки конструкторов в классе
Пример. Задан класс Cylinder реализующий цилиндр. В классе перегружается конструктор, который может вызываться одним из трех способов:
8. Каким образом осуществляется доступ к перегруженной функции с помощью указателя на функцию? Пример
При объявлении указателя на «перегруженную» функцию, компилятор определяет нужную функцию для указателя по его сигнатуре при объявлении.
Нижеследующий код демонстрирует использование указателя на перегруженную функцию
Ключевое слово overload использовалось в ранних версиях C++ для указания того, что функция есть перегруженной. Общая форма объявления «перегруженной» функции с использованием ключевого слова overload имеет вид:
извещает компилятор о том, что функция Max() есть перегруженной.
Перегрузка функций в C++
C ++ — гибкий язык программирования общего назначения. Этот язык программирования был первоначально создан Бьярном Страуструпом, датским ученым-компьютерщиком, еще в 1985 году. C ++ поддерживает полиморфизм, наследование и многое другое. В этой статье рассматривается перегрузка функций для достижения полиморфизма во время компиляции в языке программирования C ++.
Что такое функция?
Функция — это не что иное, как определенный фрагмент кода, который выполняет конкретную задачу на основе предоставленных входных данных, и он возвращает запрошенные результаты пользователю в форме вывода. Функции используются для устранения повторяющегося кода в больших базах кода.
После определения функции вы можете повторно использовать ее позже, либо в той же программе, либо в другой программе.
Синтаксис функции
Функция в C ++ имеет следующий синтаксис:
returnType functionName ( parameter_list )
Операторы returnType, parameter_list и return необязательны. Функция в C ++ может возвращать не более одного значения. Если функция не возвращает никакого значения, returnType должен быть определен как void.
Что такое перегрузка функций?
В C ++ несколько определений функций могут иметь одно и то же имя, но с разными параметрами. Это называется перегрузкой функции. С помощью функции перегрузки функций полиморфизм времени компиляции может быть достигнут в C ++.
Функции могут быть перегружены следующими способами:
Однако возвращаемое значение не учитывается при перегрузке функции.
Перегружены следующие функции:
Как видите, с помощью функции перегрузки функций в C ++ может быть несколько определений / функций с одним и тем же именем функции и в одной области.
Без функции перегрузки функций вам нужно было бы написать отдельную функцию [например, add_1 (), addition_2 () и т. Д.] Для каждого варианта. Например, вам может потребоваться написать add_1 (), чтобы добавить два целых числа, add_2 (), чтобы добавить два числа с плавающей запятой, и так далее. Однако, как вы можете видеть выше, функцию перегрузки функции можно использовать для определения нескольких вариантов функции «add ()», сохраняя при этом то же имя функции.
Следующие функции не считаются перегруженными, поскольку единственное различие между ними — это тип возвращаемого значения (тип возвращаемого значения не учитывается при перегрузке функций в C ++):
Примеры
Теперь, когда вы понимаете концепцию перегрузки функций, мы рассмотрим несколько рабочих примеров программ, чтобы лучше понять эту концепцию. Мы рассмотрим следующие примеры:
Первые два примера объясняют, как обычные функции работают в C ++, а последние три примера демонстрируют возможность перегрузки функций в C ++.
Пример 1: простая функция
В этом примере мы продемонстрируем, как можно определить и вызвать простую функцию в C ++. Мы определим класс под названием «Display» и общедоступную функцию под названием «display ()». Из функции «main ()» мы вызовем функцию «display ()» с помощью объекта класса «Display» (d).
Перегрузка функций
C++ позволяет определять несколько функций с одинаковым именем в одной области. Эти функции называются перегруженными функциями. Перегруженные функции позволяют указать другую семантику для функции в зависимости от типов и числа аргументов.
Можно перегружать как функции-члены, так и функции, не являющиеся членами. В следующей таблице указаны компоненты объявления функций, используемые языком C++ для различения групп функций с одинаковым именем в одной области.
Заметки по перегрузке
Элемент объявления функции | Использование для перегрузки |
---|---|
Тип возвращаемого функцией значения | Нет |
Число аргументов | Да |
Тип аргументов | Да |
Наличие или отсутствие многоточия | Да |
Использование typedef имен | Нет |
Незаданные границы массива | Нет |
const или volatile | Да, при применении ко всей функции |
Квалификаторы ref | Да |
Пример
В следующем примере показано использование перегрузки.
В приведенном выше коде отображается перегрузка функции print в области видимости файла.
Аргумент по умолчанию не считается частью типа функции. Поэтому он не используется при выборе перегруженных функций. Две функции, которые различаются только в своих аргументах, считаются множественными определениями, а не перегруженными функциями.
Аргументы по умолчанию не могут быть указаны для перегруженных операторов.
Сопоставление аргументов
Перегруженные функции выбираются для оптимального соответствия объявлений функций в текущей области аргументам, предоставленным в вызове функции. Если подходящая функция найдена, эта функция вызывается. Подходящее значение в этом контексте означает:
Точное соответствие найдено.
Тривиальное преобразование выполнено.
Восходящее приведение целого типа выполнено.
Стандартное преобразование в требуемый тип аргумента существует.
Пользовательское преобразование (оператор преобразования или конструктор) в требуемый тип аргумента существует.
Аргументы, представленные многоточием, найдены.
Компилятор создает набор функций-кандидатов для каждого аргумента. Функции-кандидаты — это функции, в которых фактический аргумент в данной позиции можно преобразовать в тип формального аргумента.
Для каждого аргумента создается набор наиболее подходящих функций, и выбранная функция представляет собой пересечение всех наборов. Если на пересечении находится несколько функций, перегрузка является неоднозначной и выдает ошибку. Функция, которая выбирается в конечном итоге, всегда является самой подходящей по сравнению с остальными функциями в группе по крайней мере для одного аргумента. Если нет ничего ясного, вызов функции приведет к ошибке.
Рассмотрим следующий оператор.
Представленный выше оператор создает два набора.
Набор 1. Функции-кандидаты, имеющие первый аргумент дробного типа | Set 2: функции-кандидаты, второй аргумент которого можно преобразовать в тип int |
---|---|
Variant 1 | Вариант 1 ( int можно преобразовать в long использование стандартного преобразования) |
Variant 3 |
Функции в наборе 2 — это функции, для которых существуют неявные преобразования фактического типа параметра в формальный тип параметра, а среди таких функций есть функция, для которой «стоимость» преобразования фактического типа параметра в формальный тип параметра является наименьшей.
Пересечением этих двух наборов является функция Variant 1. Ниже представлен пример неоднозначного вызова функции.
В предыдущем вызове функции создаются следующие наборы.
Set 1: потенциальные функции, имеющие первый аргумент типа int | Set 2: потенциальные функции с вторым аргументом типа int |
---|---|
Вариант 2 ( int можно преобразовать в long использование стандартного преобразования) | Вариант 1 ( int можно преобразовать в long использование стандартного преобразования) |
Поскольку пересечение этих двух наборов пусто, компилятор выдает сообщение об ошибке.
Для сопоставления аргументов функция с n аргументами по умолчанию обрабатывается как n+ 1 отдельных функций, каждая из которых имеет разное число аргументов.
Многоточие (. ) выступает в качестве подстановочного знака; оно соответствует любому фактическому аргументу. Это может привести к созданию множества неоднозначных наборов, если вы не разрабатываете перегруженные наборы функций с крайней осторожностью.
Неоднозначность перегруженных функций не может быть определена до тех пор, пока не будет обнаружен вызов функции. На этом этапе наборы создаются для каждого аргумента в вызове функции, и можно определить, существует ли неоднозначная перегрузка. Это означает, что неоднозначности могут оставаться в коде до тех пор, пока они не будут вызваны конкретным вызовом функции.
Различия типов аргументов
По той же причине аргументы функции типа, измененные или, const volatile не обрабатываются иначе, чем базовый тип для перегрузки.
Однако механизм перегрузки функций может различать ссылки, уточняющие их на const volatile базовый тип и. Он делает код следующим:
Выходные данные
Указатели const на volatile объекты и также считаются отличными от указателей на базовый тип в целях перегрузки.
Сопоставление аргументов и преобразования
Когда компилятор пытается сопоставить фактические аргументы с аргументами в объявлениях функций и точное соответствие найти не удается, для получения правильного типа он может выполнять стандартные или пользовательские преобразования. Для преобразований действуют следующие правила:
последовательности преобразований, содержащие несколько пользовательских преобразований, не учитываются;
последовательности преобразований, которые могут быть сокращены путем удаления промежуточных преобразований, не учитываются.
Получающаяся последовательность преобразований (если таковые имеются), называется наилучшей последовательностью сопоставления. Существует несколько способов преобразования объекта типа int в тип unsigned long с помощью стандартных преобразований (см. описание в разделе стандартные преобразования).
Первая последовательность, хотя она достигает требуемой цели, не является наилучшей совпадающей последовательностью — существует более короткая последовательность.
В представленной ниже таблице показана группа преобразований, называемых тривиальными. Они оказывают ограниченное влияние на определение наилучшей последовательности сопоставления. В списке, приведенном после таблицы, рассматриваются экземпляры, в которых тривиальные преобразования влияют на выбор последовательности.
Тривиальные преобразования
Тип, из которого выполняется преобразование | Тип, в который выполняется преобразование |
---|---|
имя типа | имя типа**&** |
имя типа**&** | имя типа |
Type-Name [] | имя типа* |
Type-Name ( Argument-List ) | ( * Type-Name ) ( Argument-List ) |
имя типа | ** const **имя типа |
имя типа | ** volatile **имя типа |
имя типа* | ** const **имя типа* |
имя типа* | ** volatile **имя типа* |
Ниже приведена последовательность, в которой делаются попытки выполнения преобразований.
Точное соответствие. Точное соответствие между типами, с которыми функция вызывается, и типами, объявленными в прототипе функции, всегда является наилучшим соответствием. Последовательности тривиальных преобразований классифицируются как точные соответствия. Однако последовательности, которые не делают ни одно из этих преобразований, рассматриваются лучше, чем последовательности, которые преобразуют:
От указателя к указателю на const ( type * to const type * ).
От указателя к указателю на volatile ( type * to volatile type * ).
Ссылка на ссылку на const ( type & to const type & ).
Ссылка на ссылку на volatile ( type & to volatile type & ).
Сопоставление с использованием стандартных преобразований. Любая последовательность, не классифицированная как точное соответствие или сопоставление с использованием повышений и содержащая только стандартные и тривиальные преобразования, классифицируется как сопоставление с использованием стандартных преобразований. В этой категории применяются следующие правила:
преобразование из указателя на производный класс в указатель на базовый класс создает тем более хорошее соответствие, чем ближе базовый класс к прямому базовому классу. Предположим, что иерархия классов имеет вид, показанный на следующем рисунке.
Graph отображения предпочтительных преобразований
Это же правило применяется для преобразований ссылок. Преобразование из типа D& в тип C& предпочтительнее преобразования из типа D& в тип B& и т. д.
Это же правило применяется для преобразований указателей на член. Преобразование из типа T D::* в тип T C::* предпочтительнее преобразования из типа T D::* в тип T B::* и т. д. ( T — тип члена.)
Предыдущее правило применяется только в определенном пути наследования. Рассмотрим граф, показанный на следующем рисунке.
Граф множественного наследования, демонстрирующий предпочтительные преобразования
Сопоставление с пользовательскими преобразованиями. Эта последовательность не может классифицироваться как точное совпадение, сопоставление с помощью рекламных акций или соответствие с помощью стандартных преобразований. Чтобы последовательность можно было классифицировать как сопоставление с пользовательскими преобразованиями, она должна содержать только пользовательские, стандартные или тривиальные преобразования. Сопоставление с пользовательскими преобразованиями лучше сопоставления с многоточием, но хуже сопоставления со стандартными преобразованиями.
Сопоставление с многоточием. Любая последовательность, соответствующая многоточию в объявлении, классифицируется как сопоставление с многоточием. Это считается самым слабым совпадением.
Пользовательские преобразования применяются при отсутствии встроенного повышения или преобразования. Эти преобразования выбираются на основе типа сопоставляемого аргумента. Рассмотрим следующий код.
В процессе сопоставления аргументов стандартные преобразования можно применять как к аргументу, так и к результату пользовательского преобразования. Поэтому следующий код работает.
Если какие-либо определенные пользователем преобразования должны соответствовать аргументу, стандартные преобразования не используются при вычислении наилучшего соответствия. Даже если для нескольких потенциальных функций требуется определенное пользователем преобразование, эти функции считаются равными. Пример:
Обеим версиям Func требуется определенное пользователем преобразование для преобразования типа int в аргумент типа класса. Возможные преобразования:
Преобразование из типа int в тип UDC1 (определяемое пользователем преобразование).
Преобразование из типа int в тип long ; затем преобразование в тип UDC2 (преобразование из двух шагов).
Несмотря на то, что второй требуется как стандартное преобразование, так и определяемое пользователем преобразование, два преобразования по-прежнему считаются равными.
Пользовательские преобразования считаются преобразованиями посредством создания или инициализации (функции преобразования). При рассмотрении наилучшего соответствия оба метода считаются одинаковыми.
Сопоставление аргументов и указатель this
Для этих нестатических функций-членов требуется, чтобы подразумеваемый this указатель совпадал с типом объекта, через который вызывается функция, или для перегруженных операторов требуется, чтобы первый аргумент соответствовал объекту, к которому применяется оператор. (Дополнительные сведения о перегруженных операторах см. в разделе перегруженные операторы.)
Квалификаторы ref для функций-членов
Ограничения на перегрузку
К допустимому набору перегруженных функций применяется несколько ограничений.
Любые две функции в наборе перегруженных функций должны иметь разные списки аргументов.
Перегрузка функций со списками аргументов одного типа лишь на основании возвращаемого типа недопустима.
Блок, относящийся только к системам Microsoft
Оператор New можно перегружать только на основе возвращаемого типа, а именно на основе указанного модификатора модели памяти.
Завершение блока, относящегося только к системам Майкрософт
Функции элементов не могут быть перегружены только на основе одного статического, а другого нестатического.
typedef объявления не определяют новые типы; они представляют синонимы для существующих типов. Они не влияют на механизм перегрузки. Рассмотрим следующий код.
Перечисляемые типы являются отдельными типами и могут использоваться для различения перегруженных функций.
Типы «массив» и «указатель на» считаются идентичными в целях различения перегруженных функций, но только для одноэлементных измерений массивов. Вот почему эти перегруженные функции конфликтуют и создают сообщение об ошибке:
В случае многомерных массивов второе и все последующие измерения являются частью типа. Поэтому они используются для различения перегруженных функций.
Перегрузка, переопределение и скрытие
Любые два объявления функции с одинаковым именем в одной области видимости могут ссылаться на одну функцию или на две разные перегруженные функции. Если списки аргументов в объявлениях содержат аргументы эквивалентных типов (как описано в предыдущем разделе), эти объявления относятся к одной и той же функции. В противном случае они ссылаются на две различные функции, которые выбираются с использованием перегрузки.
Область класса строго наблюдалась; Поэтому функция, объявленная в базовом классе, находится не в той же области, что и функция, объявленная в производном классе. Если функция в производном классе объявлена с тем же именем, что и виртуальная функция в базовом классе, функция производного класса переопределяет функцию базового класса. Дополнительные сведения см. в разделе виртуальные функции.
Если функция базового класса не объявлена как Virtual, то говорят, что функция производного класса скрывает ее. Переопределение и скрытие отличаются от перегрузки.
Область видимости блока строго наблюдалась; Поэтому функция, объявленная в области файла, не находится в той же области, что и функция, объявленная локально. Если локально объявленная функция имеет то же имя, что и функция, объявленная в области файла, локально объявленная функция скрывает функцию области файла, не вызывая перегрузки. Пример:
В случае перегруженных функций-членов различным версиям функции могут предоставляться разные права доступа. Они по-прежнему считаются находящимися в области видимости включающего класса и, таким образом, являются перегруженными функциями. Рассмотрим следующий код, в котором функция-член Deposit перегружена; одна версия является открытой, вторая — закрытой.
Вызов Deposit в Account::Deposit вызывает закрытую функцию члена. Этот вызов является правильным, так как Account::Deposit является функцией-членом и имеет доступ к закрытым членам класса.
Урок №102. Перегрузка функций
Обновл. 13 Сен 2021 |
На этом уроке мы рассмотрим перегрузку функций в языке C++, что это такое и как её эффективно использовать.
Перегрузка функций
Перегрузка функций — это возможность определять несколько функций с одним и тем же именем, но с разными параметрами. Например:
Здесь мы выполняем операцию вычитания с целыми числами. Однако, что, если нам нужно использовать числа типа с плавающей запятой? Эта функция совсем не подходит, так как любые параметры типа double будут конвертироваться в тип int, в результате чего будет теряться дробная часть значений.
Одним из способов решения этой проблемы является определение двух функций с разными именами и параметрами:
Но есть и лучшее решение — перегрузка функции. Мы можем просто объявить еще одну функцию subtract(), которая принимает параметры типа double:
Теперь у нас есть две версии функции subtract():
Следовательно, можно определить функцию subtract() и с большим количеством параметров:
Хотя здесь subtract() имеет 3 параметра вместо 2, это не является ошибкой, поскольку эти параметры отличаются от параметров других версий subtract().
Типы возврата в перегрузке функций
Обратите внимание, тип возврата функции НЕ учитывается при перегрузке функции. Предположим, что вы хотите написать функцию, которая возвращает рандомное число, но вам нужна одна версия, которая возвращает значение типа int, и вторая — которая возвращает значение типа double. У вас может возникнуть соблазн сделать следующее:
Компилятор выдаст ошибку. Эти две функции имеют одинаковые параметры (точнее, они отсутствуют), и, следовательно, второй вызов функции getRandomValue() будет рассматриваться как ошибочное переопределение первого вызова. Имена функций нужно будет изменить.
Псевдонимы типов в перегрузке функций
Поскольку объявление typedef (псевдонима типа) не создает новый тип данных, то следующие два объявления функции print() считаются идентичными:
Вызовы функций
Выполнение вызова перегруженной функции приводит к одному из трех возможных результатов:
Совпадение найдено. Вызов разрешен для соответствующей перегруженной функции.
Совпадение не найдено. Аргументы не соответствуют любой из перегруженных функций.
Найдены несколько совпадений. Аргументы соответствуют более чем одной перегруженной функции.
При компиляции перегруженной функции, C++ выполняет следующие шаги для определения того, какую версию функции следует вызывать:
Шаг №1: C++ пытается найти точное совпадение. Это тот случай, когда фактический аргумент точно соответствует типу параметра одной из перегруженных функций. Например:
Шаг №2: Если точного совпадения не найдено, то C++ пытается найти совпадение путем дальнейшего неявного преобразования типов. На уроке №55 мы говорили о том, как определенные типы данных могут автоматически конвертироваться в другие типы данных. Если вкратце, то:
char, unsigned char и short конвертируются в int;
unsigned short может конвертироваться в int или unsigned int (в зависимости от размера int);
float конвертируется в double;
Шаг №3: Если неявное преобразование невозможно, то C++ пытается найти соответствие посредством стандартного преобразования. В стандартном преобразовании:
Любой числовой тип будет соответствовать любому другому числовому типу, включая unsigned (например, int равно float).
enum соответствует формальному типу числового типа данных (например, enum равно float).
Ноль соответствует типу указателя и числовому типу (например, 0 как char * или 0 как float ).
Обратите внимание, все стандартные преобразования считаются равными. Ни одно из них не считается выше остальных по приоритету.
Шаг №4: C++ пытается найти соответствие путем пользовательского преобразования. Хотя мы еще не рассматривали классы, но они могут определять преобразования в другие типы данных, которые могут быть неявно применены к объектам этих классов. Например, мы можем создать класс W и в нем определить пользовательское преобразование в тип int:
То, как делать пользовательские преобразования в классах, мы рассмотрим на соответствующих уроках.
Несколько совпадений
Если каждая из перегруженных функций должна иметь уникальные параметры, то как могут быть возможны несколько совпадений? Поскольку все стандартные и пользовательские преобразования считаются равными, то, если вызов функции соответствует нескольким кандидатам посредством стандартного или пользовательского преобразования, результатом будет неоднозначное совпадение (т.е. несколько совпадений). Например:
В случае с print(‘b’) C++ не может найти точного совпадения. Он пытается преобразовать b в тип int, но версии print(int) тоже нет. Используя стандартное преобразование, C++ может преобразовать b как в unsigned int, так и во float. Поскольку все стандартные преобразования считаются равными, то получается два совпадения.
С print(0) всё аналогично. 0 — это int, а версии print(int) нет. Путем стандартного преобразования мы опять получаем два совпадения.
Неоднозначное совпадение считается ошибкой типа compile-time. Следовательно, оно должно быть устранено до того, как ваша программа скомпилируется. Есть два решения этой проблемы:
Решение №1: Просто определить новую перегруженную функцию, которая принимает параметры именно того типа данных, который вы используете в вызове функции. Тогда C++ сможет найти точное совпадение.
Заключение
Перегрузка функций может значительно снизить сложность программы, в то же время создавая небольшой дополнительный риск. Хотя этот урок несколько долгий и может показаться сложным, но, на самом деле, перегрузка функций обычно работает прозрачно и без каких-либо проблем. Все неоднозначные случаи компилятор будет отмечать, и их можно будет легко исправить.
Правило: Используйте перегрузку функций для упрощения ваших программ.
Поделиться в социальных сетях: