Перегрузка функций
В этой статье есть несколько проблем. Пожалуйста, помогите улучшить его или обсудите эти проблемы на странице обсуждения . ( Узнайте, как и когда удалять эти шаблонные сообщения )
|
Полиморфизм |
---|
Специальный полиморфизм |
Параметрический полиморфизм |
Подтипирование |
В некоторых языках программирования перегрузка функций или перегрузка методов — это возможность создавать несколько функций с одинаковым именем и разными реализациями. Вызовы перегруженной функции будут запускать конкретную реализацию этой функции, соответствующую контексту вызова, позволяя одному вызову функции выполнять различные задачи в зависимости от контекста.
Например, доТаск() и doTask(object o) — это перегруженные функции. Чтобы вызвать последний, объект должен быть передан в качестве параметра , тогда как первый не требует параметра и вызывается с пустым полем параметра. Распространенной ошибкой было бы присвоение значения по умолчанию объекту во второй функции, что привело бы к неоднозначной ошибке вызова , поскольку компилятор не знал бы, какой из двух методов использовать.
Другим примером является Функция Print(object o) , выполняющая различные действия в зависимости от того, печатается ли текст или фотографии. Две разные функции могут быть перегружены как Печать (text_object T); Печать(изображение_объект P) . Если мы напишем перегруженные функции печати для всех объектов, которые наша программа будет «печатать», нам никогда не придется беспокоиться о типе объекта и правильном вызове функции, вызов всегда будет: Распечатать (что-то) .
Языки, поддерживающие перегрузку
[ редактировать ]Языки, поддерживающие перегрузку функций, включают, помимо прочего, следующее:
- Есть
- Апекс
- С++
- С#
- Кложур [1]
- Быстрый
- Фортран
- Котлин [2]
- Ява [3]
- Юлия
- PostgreSQL [4] и PL/SQL [5]
- Скала
- Машинопись
- Визуальный Бейсик (.NET)
- Вольфрам Язык
- Эликсир
- Nim [6]
- Кристалл [7]
- Дельфи [8]
Правила перегрузки функций
[ редактировать ]- Одно и то же имя функции используется для более чем одного определения функции в определенном модуле, классе или пространстве имен.
- Функции должны иметь разные сигнатуры типов , т.е. отличаться количеством или типами формальных параметров (как в C++) или, кроме того, типом возвращаемого значения (как в Ada). [9]
Перегрузка функций обычно связана со статически типизированными языками программирования, которые обеспечивают проверку типов при вызовах функций . Перегруженная функция — это набор различных функций, которые можно вызывать с одним и тем же именем. Для каждого конкретного вызова компилятор определяет, какую перегруженную функцию использовать, и решает это во время компиляции . Это справедливо для таких языков программирования, как Java. [10]
Перегрузка функций отличается от форм полиморфизма , где выбор осуществляется во время выполнения, например, с помощью виртуальных функций , а не статически.
Пример: перегрузка функции в C++
#include <iostream>
int Volume(int s) { // Volume of a cube.
return s * s * s;
}
double Volume(double r, int h) { // Volume of a cylinder.
return 3.1415926 * r * r * static_cast<double>(h);
}
long Volume(long l, int b, int h) { // Volume of a cuboid.
return l * b * h;
}
int main() {
std::cout << Volume(10);
std::cout << Volume(2.5, 8);
std::cout << Volume(100l, 75, 15);
}
В приведенном выше примере объем каждого компонента рассчитывается с использованием одной из трех функций под названием «объем», выбор которых основан на различном количестве и типе фактических параметров.
Перегрузка конструктора
[ редактировать ]Конструкторы , используемые для создания экземпляров объекта, также могут быть перегружены в некоторых объектно-ориентированных языках программирования . Поскольку во многих языках имя конструктора предопределено именем класса, может показаться, что конструктор может быть только один. Всякий раз, когда требуется несколько конструкторов, их следует реализовать как перегруженные функции. В C++ конструкторы по умолчанию объекта не принимают никаких параметров, создавая экземпляры членов с соответствующими значениями по умолчанию, «которые обычно равны нулю для числовых полей и пустой строке для строковых полей». [11] Например, конструктор по умолчанию для объекта счета в ресторане, написанного на C++, может установить чаевые равными 15 %:
Bill()
: tip(0.15), // percentage
total(0.0)
{ }
Недостаток этого подхода в том, что для изменения значения созданного объекта Bill требуется два шага. Ниже показано создание и изменение значений в основной программе:
Bill cafe;
cafe.tip = 0.10;
cafe.total = 4.00;
Перегрузив конструктор, можно было передать подсказку и сумму в качестве параметров при создании. Это показывает перегруженный конструктор с двумя параметрами. Этот перегруженный конструктор размещается в классе так же, как и исходный конструктор, который мы использовали ранее. Какой из них будет использоваться, зависит от количества параметров, предоставленных при создании нового объекта Bill (ни одного или два):
Bill(double tip, double total)
: tip(tip),
total(total)
{ }
Теперь функция, создающая новый объект Bill, может передавать в конструктор два значения и устанавливать элементы данных за один шаг. Ниже показано создание и установка значений:
Bill cafe(0.10, 4.00);
Это может быть полезно для повышения эффективности программы и уменьшения длины кода.
Другой причиной перегрузки конструктора может быть принудительное применение обязательных элементов данных. В этом случае конструктор по умолчанию объявляется закрытым или защищенным (или предпочтительно удаленным, начиная с C++11 ), чтобы сделать его недоступным извне. Для приведенного выше счета итоговая сумма может быть единственным параметром конструктора, поскольку у счета нет разумного значения по умолчанию для общей суммы, тогда как значение по умолчанию составляет 0,15.
Осложнения
[ редактировать ]Две проблемы взаимодействуют и усложняют перегрузку функций: маскирование имени (из-за области видимости ) и неявное преобразование типов .
Если функция объявлена в одной области, а затем другая функция с тем же именем объявлена во внутренней области, существует два естественных возможных варианта поведения перегрузки: внутреннее объявление маскирует внешнее объявление (независимо от сигнатуры) или оба внутренних объявления и внешнее объявление включены в перегрузку, причем внутреннее объявление маскирует внешнее объявление только в том случае, если подпись совпадает. Первое взято из C++: «в C++ нет перегрузки между областями действия». [12] В результате, чтобы получить набор перегрузок с функциями, объявленными в разных областях видимости, необходимо явно импортировать функции из внешней области видимости во внутреннюю, с помощью using
ключевое слово.
Неявное преобразование типов усложняет перегрузку функций, поскольку, если типы параметров не совсем совпадают с сигнатурой одной из перегруженных функций, но могут совпадать после преобразования типов, разрешение зависит от того, какое преобразование типа выбрано.
Они могут сочетаться запутанным образом: например, неточное совпадение, объявленное во внутренней области, может маскировать точное совпадение, объявленное во внешней области. [12]
Например, чтобы иметь производный класс с перегруженной функцией, принимающей double
или int
, используя функцию, принимающую int
из базового класса на C++ можно было бы написать:
class B {
public:
void F(int i);
};
class D : public B {
public:
using B::F;
void F(double d);
};
Не удалось включить using
приводит к int
параметр, переданный в F
в производном классе преобразуется в двойной и соответствует функции в производном классе, а не в базовом классе; Включая using
приводит к перегрузке производного класса и, таким образом, соответствует функции базового класса.
Предостережения
[ редактировать ]Если метод разработан с чрезмерным количеством перегрузок, разработчикам может быть сложно определить, какая перегрузка вызывается, просто прочитав код. Это особенно верно, если некоторые из перегруженных параметров имеют типы, унаследованные от других возможных параметров (например, «объект»). IDE может выполнить разрешение перегрузки и отобразить (или перейти к) правильную перегрузку.
Перегрузка на основе типов также может затруднить обслуживание кода, поскольку обновления кода могут случайно изменить перегрузку метода, выбранную компилятором. [13]
См. также
[ редактировать ]- Абстракция (информатика)
- Конструктор (информатика)
- Аргумент по умолчанию
- Динамическая отправка
- Шаблон фабричного метода
- Сигнатура метода
- Переопределение метода
- Объектно-ориентированное программирование
- Перегрузка оператора
Цитаты
[ редактировать ]- ^ «Clojure — Изучите Clojure — Функции» . Clojure.org . Проверено 13 июня 2023 г.
- ^ «Спецификация языка Котлин» . kotlinlang.org .
- ^ Блох 2018 , с. 238-244, §Глава 8 Пункт 52: Удалить непроверенные предупреждения.
- ^ «37.6. Перегрузка функций» . Документация PostgreSQL . 12 августа 2021 г. Проверено 29 августа 2021 г.
- ^ «Руководство и справочник пользователя по базам данных PL/SQL» . docs.oracle.com . Проверено 29 августа 2021 г.
- ^ «Руководство Нима» . nim-lang.org .
- ^ «Кристалл Документы» . Crystal-lang.org .
- ^ «Эмбаркадеро Дельфы» . embarcadero.com .
- ^ Ватт, Дэвид А.; Финдли, Уильям (1 мая 2004 г.). Концепции проектирования языков программирования . John Wiley & Sons, Inc., стр. 204–207. ISBN 978-0-470-85320-7 .
- ^ Блох 2018 , с. 238-244, §Глава 8, пункт 52: Используйте перегрузку разумно.
- ^ Чан, Джейми (2017). Изучите C# за один день и выучите его хорошо (пересмотренная ред.). п. 82. ИСБН 978-1518800276 .
- ^ Jump up to: а б Страуструп, Бьерн . «Почему перегрузка не работает для производных классов?» .
- ^ Браха, Гилад (3 сентября 2009 г.). «Системная перегрузка» . Комната 101.
Ссылки
[ редактировать ]- Блох, Джошуа (2018). «Эффективная Java: Руководство по языку программирования» (третье изд.). Аддисон-Уэсли. ISBN 978-0134685991 .
Внешние ссылки
[ редактировать ]- Мейер, Бертран (октябрь 2001 г.). «Перегрузка против объектной технологии» (PDF) . Эйфелева колонна. Журнал объектно-ориентированного программирования . 14 (4). ООО «101 Коммуникации»: 3–7 . Проверено 27 августа 2020 г.