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

Из Википедии, бесплатной энциклопедии

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

Например, доТаск() и doTask(object o) — это перегруженные функции. Чтобы вызвать последний, объект должен быть передан в качестве параметра , тогда как первый не требует параметра и вызывается с пустым полем параметра. Распространенной ошибкой было бы присвоение значения по умолчанию объекту во второй функции, что привело бы к неоднозначной ошибке вызова , поскольку компилятор не знал бы, какой из двух методов использовать.

Другим примером является Функция Print(object o) , выполняющая различные действия в зависимости от того, печатается ли текст или фотографии. Две разные функции могут быть перегружены как Печать (text_object T); Печать(изображение_объект P) . Если мы напишем перегруженные функции печати для всех объектов, которые наша программа будет «печатать», нам никогда не придется беспокоиться о типе объекта и правильном вызове функции, вызов всегда будет: Распечатать (что-то) .

Языки, поддерживающие перегрузку [ править ]

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

Правила перегрузки функций [ править ]

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

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

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

В Java перегрузка функций также известна как полиморфизм времени компиляции и статический полиморфизм.

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

Пример: перегрузка функции в C++

#include   <iostream> 

 int   Volume  (  int   s  )   {    // Объем куба. 
    вернуть   с   *   с   *   с  ; 
  } 

 double   Volume  (  double   r  ,   int   h  )   {    // Объем цилиндра. 
    return   3.1415926   *   r   *   r   *   static_cast  <double>  h  (  )  ; 
  } 

 long   Volume  (  long   l  ,   int   b  ,   int   h  )   {    // Объем кубоида. 
    вернуть   л   *   б   *   ч  ; 
  } 

 int   main  ()   { 
   std  ::  cout   <<   Volume  (  10  ); 
    std  ::  cout   <<   Volume  (  2.5  ,   8  ); 
    std  ::  cout   <<   Объем  (  100л  ,   75  ,   15  ); 
  } 

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

Перегрузка конструктора [ править ]

Конструкторы , используемые для создания экземпляров объекта, также могут быть перегружены в некоторых объектно-ориентированных языках программирования . Поскольку во многих языках имя конструктора предопределено именем класса, может показаться, что конструктор может быть только один. Всякий раз, когда требуется несколько конструкторов, их следует реализовать как перегруженные функции. В C++ конструкторы по умолчанию объекта не принимают никаких параметров, создавая экземпляры членов с соответствующими значениями по умолчанию, «которые обычно равны нулю для числовых полей и пустой строке для строковых полей». [10] Например, конструктор по умолчанию для объекта счета в ресторане, написанного на C++, может установить чаевые равными 15 %:

Bill  () 
     :   чаевые  (  0,15  ),   // 
       общая сумма в  процентах (  0,0  ) 
 {   } 

Недостаток этого подхода в том, что для изменения значения созданного объекта Bill требуется два шага. Ниже показано создание и изменение значений в основной программе:

Билл   кафе  ; 
  кафе  .   чаевые   =   0,10  ; 
  кафе  .   итого   =   4,00  ; 

Перегрузив конструктор, можно было передать подсказку и сумму в качестве параметров при создании. Это показывает перегруженный конструктор с двумя параметрами. Этот перегруженный конструктор размещается в классе так же, как и исходный конструктор, который мы использовали ранее. Какой из них будет использоваться, зависит от количества параметров, предоставленных при создании нового объекта Bill (ни одного или два):

Счет  (  двойные   чаевые  ,   двойная   сумма  ) 
     :   чаевые  (  чаевые  ), 
       общая сумма  (  всего  ) 
 {   } 

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

Билл-   кафе  (  0.10  ,   4.00  ); 

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

Другой причиной перегрузки конструктора может быть принудительное применение обязательных элементов данных. В этом случае конструктор по умолчанию объявляется закрытым или защищенным (или предпочтительно удаленным, начиная с C++11 ), чтобы сделать его недоступным извне. Для приведенного выше счета итоговая сумма может быть единственным параметром конструктора, поскольку у счета нет разумного значения по умолчанию для общей суммы, тогда как значение по умолчанию составляет 0,15.

Осложнения [ править ]

Две проблемы взаимодействуют и усложняют перегрузку функций: маскирование имени (из-за области видимости ) и неявное преобразование типов .

Если функция объявлена ​​в одной области, а затем другая функция с тем же именем объявлена ​​во внутренней области, существует два естественных возможных варианта поведения перегрузки: внутреннее объявление маскирует внешнее объявление (независимо от сигнатуры) или оба внутренних объявления и внешнее объявление включены в перегрузку, причем внутреннее объявление маскирует внешнее объявление только в том случае, если подпись совпадает. Первое взято из C++: «в C++ нет перегрузки между областями действия». [11] В результате, чтобы получить набор перегрузок с функциями, объявленными в разных областях видимости, необходимо явно импортировать функции из внешней области во внутреннюю, с помощью using ключевое слово.

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

Они могут сочетаться запутанным образом: например, неточное совпадение, объявленное во внутренней области, может маскировать точное совпадение, объявленное во внешней области. [11]

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

класс   B   { 
  public  : 
   void   F  (  int   i  ); 
  }; 

  класс   D   :   public   B   { 
  public  : 
   using   B  ::  F  ; 
    пустота   F  (  двойной   d  ); 
  }; 

Не удалось включить using приводит к int параметр, переданный в Fв производном классе преобразуется в тип double и соответствует функции в производном классе, а не в базовом классе; Включая using приводит к перегрузке производного класса и, таким образом, соответствует функции базового класса.

Предостережения [ править ]

Если метод разработан с чрезмерным количеством перегрузок, разработчикам может быть сложно определить, какая перегрузка вызывается, просто прочитав код. Это особенно верно, если некоторые из перегруженных параметров имеют типы, унаследованные от других возможных параметров (например, «объект»). IDE может выполнить разрешение перегрузки и отобразить (или перейти к) правильную перегрузку.

Перегрузка на основе типов также может затруднить обслуживание кода, поскольку обновления кода могут случайно изменить перегрузку метода, выбранную компилятором. [12]

См. также [ править ]

Цитаты [ править ]

  1. ^ «Clojure — Изучите Clojure — Функции» . Clojure.org . Проверено 13 июня 2023 г.
  2. ^ «Спецификация языка Котлин» . kotlinlang.org .
  3. ^ Блох 2018 , с. 238-244, §Глава 8 Пункт 52: Удалить непроверенные предупреждения.
  4. ^ «37.6. Перегрузка функций» . Документация PostgreSQL . 12 августа 2021 г. Проверено 29 августа 2021 г.
  5. ^ «Руководство и справочник пользователя по базам данных PL/SQL» . docs.oracle.com . Проверено 29 августа 2021 г.
  6. ^ «Руководство Нима» . nim-lang.org .
  7. ^ «Кристалл Документы» . Crystal-Lang.org .
  8. ^ «Эмбаркадеро Дельфы» . embarcadero.com .
  9. ^ Блох 2018 , с. 238-244, §Глава 8, пункт 52: Используйте перегрузку разумно.
  10. ^ Чан, Джейми (2017). Изучите C# за один день и выучите его хорошо (пересмотренная ред.). п. 82. ИСБН  978-1518800276 .
  11. ^ Перейти обратно: а б Страуструп, Бьерн . «Почему перегрузка не работает для производных классов?» .
  12. ^ Браха, Гилад (3 сентября 2009 г.). «Системная перегрузка» . Комната 101.

Ссылки [ править ]

  • Блох, Джошуа (2018). «Эффективная Java: Руководство по языку программирования» (третье изд.). Аддисон-Уэсли. ISBN  978-0134685991 .

Внешние ссылки [ править ]