Przeciążanie funkcji - Function overloading
Wielopostaciowość |
---|
Polimorfizm ad hoc |
Polimorfizm parametryczny |
Podpisywanie |
W niektórych językach programowania , funkcja przeciążenia lub metoda przeciążenie jest możliwość tworzenia wielu funkcji o tej samej nazwie z różnych wdrożeń. Wywołania przeciążonej funkcji uruchomią konkretną implementację tej funkcji odpowiednią do kontekstu wywołania, umożliwiając jednemu wywołaniu funkcji wykonywanie różnych zadań w zależności od kontekstu.
Na przykład doTask()
i doTask(object o)
są przeciążonymi funkcjami. Aby wywołać to drugie, obiekt musi być przekazany jako parametr , podczas gdy pierwszy nie wymaga parametru i jest wywoływany z pustym polem parametru. Częstym błędem byłoby przypisanie wartości domyślnej do obiektu w drugiej funkcji, co skutkowałoby niejednoznacznym błędem wywołania , ponieważ kompilator nie wiedziałby, której z dwóch metod użyć.
Innym przykładem jest Print(object o)
funkcja, która wykonuje różne akcje w zależności od tego, czy drukuje tekst, czy zdjęcia. Dwie różne funkcje mogą być przeciążone jako Print(text_object T); Print(image_object P)
. Jeśli napiszemy przeciążone funkcje drukowania dla wszystkich obiektów, nasz program "wypisze", nigdy nie będziemy musieli martwić się o typ obiektu i ponownie poprawne wywołanie funkcji , wywołanie to zawsze: Print(something)
.
Języki wspierające przeciążanie
Języki, które obsługują przeciążanie funkcji obejmują między innymi:
Zasady przeciążania funkcji
- Ta sama nazwa funkcji jest używana dla więcej niż jednej definicji funkcji
- Funkcje muszą różnić się arnością lub typami ich parametrów
Jest to klasyfikacja statycznego polimorfizmu, w której wywołanie funkcji jest rozwiązywane przy użyciu algorytmu „najlepszego dopasowania”, w którym konkretna funkcja do wywołania jest rozwiązywana przez znalezienie najlepszego dopasowania formalnych typów parametrów z rzeczywistymi typami parametrów. Szczegóły tego algorytmu różnią się w zależności od języka.
Przeciążanie funkcji jest zwykle kojarzone z językami programowania z typami statycznymi, które wymuszają sprawdzanie typu w wywołaniach funkcji . Przeciążona funkcja to tak naprawdę zestaw różnych funkcji, które mają tę samą nazwę. Ustalenie, której funkcji użyć dla konkretnego wywołania, jest rozstrzygane w czasie kompilacji .
W Javie przeciążanie funkcji jest również znane jako polimorfizm czasu kompilacji i polimorfizm statyczny.
Przeciążania funkcji nie należy mylić z formami polimorfizmu, w których wyboru dokonuje się w czasie wykonywania, np. za pomocą funkcji wirtualnych zamiast statycznie.
Przykład: Przeciążanie funkcji w 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);
}
W powyższym przykładzie objętość każdego składnika jest obliczana za pomocą jednej z trzech funkcji o nazwie „objętość”, z wyborem opartym na różnej liczbie i rodzaju rzeczywistych parametrów.
Przeciążenie konstruktora
Konstruktory używane do tworzenia instancji obiektu mogą być również przeciążone w niektórych obiektowych językach programowania . Ponieważ w wielu językach nazwa konstruktora jest z góry zdeterminowana nazwą klasy, wydawałoby się, że konstruktor może być tylko jeden. Gdy potrzebnych jest wiele konstruktorów, należy je zaimplementować jako przeciążone funkcje. W C++ , domyślne konstruktory nie przyjmują parametrów, tworząc wystąpienia członków obiektu z ich odpowiednimi wartościami domyślnymi. Na przykład domyślny konstruktor obiektu rachunku w restauracji napisany w C++ może ustawić wskazówkę na 15%:
Bill()
: tip(0.15), // percentage
total(0.0)
{ }
Wadą tego jest to, że trzeba wykonać dwa kroki, aby zmienić wartość utworzonego obiektu Bill. Poniżej przedstawiono tworzenie i zmianę wartości w programie głównym:
Bill cafe;
cafe.tip = 0.10;
cafe.total = 4.00;
Przeciążając konstruktor, można by podczas tworzenia przekazać wskazówkę i sumę jako parametry. Pokazuje przeciążony konstruktor z dwoma parametrami. Ten przeciążony konstruktor jest umieszczony w klasie, podobnie jak oryginalny konstruktor, którego używaliśmy wcześniej. To, który z nich zostanie użyty, zależy od liczby parametrów podanych podczas tworzenia nowego obiektu Bill (brak lub dwa):
Bill(double tip, double total)
: tip(tip),
total(total)
{ }
Teraz funkcja, która tworzy nowy obiekt Bill, może przekazać dwie wartości do konstruktora i ustawić składowe danych w jednym kroku. Poniżej przedstawiono tworzenie i ustawianie wartości:
Bill cafe(0.10, 4.00);
Może to być przydatne w zwiększeniu wydajności programu i skróceniu długości kodu.
Innym powodem przeciążenia konstruktora może być wymuszenie obowiązkowych członków danych. W tym przypadku domyślny konstruktor jest zadeklarowany jako prywatny lub chroniony (lub najlepiej usunięty od C++11 ), aby był niedostępny z zewnątrz. Dla Billa powyższa suma może być jedynym parametrem konstruktora – ponieważ Bill nie ma rozsądnej wartości domyślnej dla sumy – podczas gdy wskazówka domyślnie wynosi 0,15.
Komplikacje
Dwa problemy wchodzą w interakcje i komplikują przeciążanie funkcji: maskowanie nazw (ze względu na zakres ) i niejawna konwersja typów .
Jeśli funkcja jest zadeklarowana w jednym zakresie, a następnie inna funkcja o tej samej nazwie jest zadeklarowana w zakresie wewnętrznym, istnieją dwa naturalne możliwe zachowania przeciążające: deklaracja wewnętrzna maskuje deklarację zewnętrzną (niezależnie od podpisu) lub obie deklaracje wewnętrzne a zewnętrzna deklaracja są zawarte w przeciążeniu, przy czym wewnętrzna deklaracja maskuje zewnętrzną deklarację tylko wtedy, gdy pasuje podpis. Pierwsza z nich jest używana w C++: „w C++ nie ma przeciążania między zakresami”. W rezultacie, aby uzyskać zestaw przeciążeń z funkcjami zadeklarowanymi w różnych zakresach, należy jawnie zaimportować funkcje z zakresu zewnętrznego do zakresu wewnętrznego za pomocą using
słowa kluczowego.
Niejawna konwersja typu komplikuje przeciążanie funkcji, ponieważ jeśli typy parametrów nie są dokładnie zgodne z sygnaturą jednej z przeciążonych funkcji, ale mogą być zgodne po konwersji typu, rozwiązanie zależy od wybranej konwersji typu.
Mogą one łączyć się w mylące sposoby: niedokładne dopasowanie zadeklarowane w zakresie wewnętrznym może maskować dokładne dopasowanie zadeklarowane na przykład w zakresie zewnętrznym.
Na przykład, aby mieć klasę pochodną z przeciążoną funkcją pobierającą a double
lub int
, używając funkcji pobierającej a int
z klasy bazowej w C++, należałoby napisać:
class B {
public:
void F(int i);
};
class D : public B {
public:
using B::F;
void F(double d);
};
Nie można uwzględnić using
wyników w int
parametrze przekazanym do F
klasy pochodnej konwertowanej na podwójną i pasującą do funkcji w klasie pochodnej, a nie w klasie bazowej; Uwzględnienie using
powoduje przeciążenie w klasie pochodnej, a tym samym dopasowanie funkcji w klasie bazowej.
Zastrzeżenia
Jeśli metoda została zaprojektowana z nadmierną liczbą przeciążeń, deweloperom może być trudno rozpoznać, które przeciążenie jest wywoływane po prostu przez odczytanie kodu. Jest to szczególnie prawdziwe, jeśli niektóre z przeciążonych parametrów są typów, które są dziedziczonymi typami innych możliwych parametrów (na przykład "object"). IDE może wykonać rozdzielczość przeciążenia i wyświetlić (lub przejść do) poprawnego przeciążenia.
Przeciążanie oparte na typie może również utrudniać konserwację kodu, gdzie aktualizacje kodu mogą przypadkowo zmienić przeciążenie metody wybrane przez kompilator.
Zobacz też
- Abstrakcja (informatyka)
- Konstruktor (informatyka)
- Dynamiczna wysyłka
- Wzór metody fabrycznej
- Podpis metody
- Zastępowanie metody
- Programowanie obiektowe
- Przeciążenie operatora
Bibliografia
Zewnętrzne linki
- Meyer, Bertrand (październik 2001). „Przeciążenie a technologia obiektowa” (PDF) . Kolumna Eiffla. Dziennik programowania obiektowego . 101 Komunikacja LLC. 14 (4): 3–7 . Pobrano 27 sierpnia 2020 .