Przeładowywanie nazw funkcji w C++
Przeładowywanie funkcji w C++ to technika często wykorzystywana przez programistów na przykład podczas implementacji różnego rodzaju „specyficznych” algorytmów. Umożliwia ona napisanie kilka funkcji wykonujących tą samą operację, ale różniących się od siebie drobnymi szczegółami (innym typem argumentów oraz inną liczbą argumentów). Jak już wcześniej wspomniałem konstrukcję taką możemy wykorzystać przy różnego rodzaju implementacjach matematycznych algorytmów ale tak naprawdę w praktyce to wszystko zależy od danego programisty i stylu pisania kodu. Warto również zaznaczyć, że zrozumienie sposobu działania przeładowywania nazw funkcji to wstęp do programowania zorientowanego obiektowo, gdzie zamiast funkcji będziemy używać własne obiekty (klasy).
Przykład przeładowania funkcji o nazwie „dodawanie
„:
#include <iostream> using namespace std; int dodawanie(int liczba) { return liczba + 0; //return liczba; } int dodawanie(int liczba1, int liczba2) { return liczba1 + liczba2; } int main() { printf("dodawanie(10): %d, dodawanie(10, 10): %d.\n", dodawanie(10), dodawanie(10, 10)); return 0; }
Jaki będzie wynik działania tego programu? Otóż na wyjściu otrzymamy taką informację:
dodawanie(10): 10, dodawanie(10, 10): 20.
Powyższych wyników chyba nie muszę nikomu tłumaczyć pierwsza funkcja (ta przyjmująca jeden argument) wypisała 10
bo 10 + 0
to po prostu 10
, druga funkcja wypisała 20
bo 10 + 10
wynosi oczywiście 20
. To jednak nie jest ważne w tym przykładzie, chodzi tutaj napisanie działającego programu mającego dwie funkcje o takiej samej nazwie. Należy jednak zwrócić uwagę iż różnią się one liczbą argumentów.
Przyjrzyjmy się innemu przykładowi:
#include <iostream> using namespace std; int dodawanie(int liczba) { return liczba + 0; //return liczba; } /*int dodawanie(int jakasLiczba) { return jakasLiczba; }*/ /*double dodawanie(int liczba) { return static_cast<double>(liczba); }*/ int dodawanie(double liczba) { return static_cast<int>(liczba); } int main() { printf("dodawanie(10): %d, dodawanie(10, 9.344894): %d.\n", dodawanie(10), dodawanie(9.344894)); return 0; }
Tutaj popełniłem kilka błędów związanych z przeładowywaniem nazw funkcji. W pierwszym przypadku (linie 10-13) napisałem dokładnie tą samą funkcję nie różniącą się liczbą argumentów lub ich typem, zastosowałem tutaj inną nazwę argumentu ale to nie ma znaczenia gdyż kompilator nie będzie w stanie odróżnić o którą funkcje mi chodzi. Natomiast w drugim przypadku (lnie 15-18) napisałem funkcję „dodawanie
” różniącą się zwracanym typem danych. Tak też nie możemy zrobić gdyż mamy tą samą liczbę argumentów oraz ten sam ich typ – to rozwiązanie odpada.
Na samym końcu (linie 20-23) mamy poprawnie przeładowaną funkcję „dodawanie
” gdyż jako argument podajemy liczbę innego typu.
Szablony (ang. template)
Na początku napisałem, że zrozumienie przeładowywania nazw funkcji to wstęp do programowania zorientowanego obiektowo. Technika ta to również wstęp do nieco bardziej wyrafinowanego sposobu czyli szablonów (ang. template), nie będę wyjaśniał ich na łamach niniejszego artykułu ale krótko streszczę do czego służą i pokarzę przykład ich użycia.
Tak więc szablony stosujemy między innymi w sytuacjach w których mamy na przykład do napisania kilka funkcji działających tak samo, ale różniących się typem przyjmowanych argumentów. Możemy to oczywiście rozwiązać za pośrednictwem poznanej w tym artykule techniki czyli przeładowywania nazw funkcji i kopiowania kodu (w rzeczywistości każde przeładowanie to osobna funkcja) albo rozwiązać w nieco sprytniejszy sposób oczywiście za pomocą szablonów.
Jeżeli wybierzemy tą drugą opcję, to uchronimy się przede wszystkim przed większą objętością kodu, a także straconym czasem kiedy okaże się, że akurat dana funkcja miała działać inaczej bądź zmieniły się wymagania klienta. Gdybyśmy zastosowali przeładowywanie nazw funkcji to w takim wypadku trzeba było by poprawić kod w różnych miejscach, a znając życie pewnie o którymś byśmy zapomnieli. Jak więc to zrobić sprytnie za pomocą szablonów?
#include <iostream> using namespace std; template <typename Typ> Typ dodawanie(Typ arg1, Typ arg2) { return arg1 + arg2; } int main() { printf("dodawanie(10, 10): %d, dodawanie(9.344894, 10,2345): %f.\n", dodawanie(10, 10), dodawanie(9.344894, 10.2345)); return 0; }
Teraz do funkcji dodawanie
możemy „podać” liczby dowolnego typu.