Przeciążenie (przeładowanie nazw) funkcji dla większości języków (w tym C) zasada w programie może być tylko jedna funkcja o danej nazwie kompilator C++ - dopuszcza więcej niż jedną funkcję o tej samej nazwie o ile różnią się one liczbą i/lub typem argumentów Przekład: Mamy dwie funkcje: void wypisz(int); void wypisz(char, float, char); Teraz wywołanie: wypisz( A, 3.14, F ); Czy mamy wątpliwości której funkcji użyć? taką sytuację nazywamy przeciążeniem (przeładowaniem nazwy) funkcji Przykład: #include <stdio.h> void wypisz(int liczba); void wypisz(char znak, float x, char *tekst); void wypisz(int liczba, char znak); void wypisz(char znak, int liczba); main() wypisz(123); wypisz('d',7); wypisz('a',82.3,"stopnie Celsjusza"); wypisz(4,'z');
void wypisz(int liczba) printf("liczba typu int: %d\n",liczba); void wypisz(char znak, float x, char *tekst) printf("blok %c : %.1f %s\n",znak,x,tekst); void wypisz(int liczba, char znak) printf("%d razy wystapil stan %c\n",liczba,znak); void wypisz(char znak, int liczba) printf("%c. %d\n",znak,liczba); Liczba typu int: 123 D. 7 Blok A : 82.3 stopnie Celsjusza 4 razy wystapil stan Z o 4 funkcje o tej samej nazwie o można odróżnić nawet funkcję o tej samej nazwie, liczbie i typie argumentów (o ile różnią się kolejnością) o działanie: kompilator analizuje wywołanie, przegląda argumenty i dobiera funkcję, która pasuje Podsumowanie: przeciążenie (przeładowanie) funkcji nadanie funkcji wielu znaczeń poprzez istnienie kilku funkcji o identycznej nazwie wybór wersji funkcji zależy od kontekstu jej użycia (ilości, typu i kolejności argumentów
Po co i kiedy przeciążać (przeładowywać): gdy przeciążane funkcje robią to samo (np. wypisują na ekran) a my chcemy, by nazwa zdawała sprawę z tego, co funkcja robi konstruktory i destruktory klas (nazwa jest ustalona) Przeładowanie funkcji a argumenty domyślne możliwe niejednoznaczności (błędy) void fun(int a) void fun(int b, int c=0) main() fun(1); // niejednoznaczność - błąd Jak to działa naprawdę? kompilator C++ modyfikuje nazwy funkcji void wypisz(int liczba) wypisz_fi void wypisz(int liczba, char znak) wypisz_fic Tak zmodyfikowane nazwy są już unikalne: wypisz(3, A ) wypisz_fic(3, A )
Linkowanie z modułami innych języków zamiana nazwy funkcji dla wszystkich funkcji w C++ nie jest dokonywana przez kompilatory innych języków źródło problemów w sytuacjach, gdy linkujemy moduły skompilowane różnymi kompilatorami Przykład: Moduł 1 (w C) : mod1.c mod1.o Moduł 2: (w C++) : mod2.cpp mod2.o Niech mod1.c zawiera funkcję void wypisz(int, char), chcemy ją użyć w mod2.cpp Zwykła procedura deklaracja extern w mod2.cpp: extern void wypisz(int, char); nie wystarcza!!! Powód kompilator C++ zmieni nazwę funkcji; każe linkerowi szukać funkcji: void wypisz Fic(int, char); Wyjście: należy zastosować deklarację: extern C void wypisz(int, char); lub, gdy chcemy włączyć kilka funkcji: extern C int jeden(int); float dwa(double); //. Tak można włączać w program w C++ moduły z innych języków (nie tylko C) np. asemblera, Fortranu itp.
Przeładowanie zasłonięcie: przeładowanie tylko wtedy, gdy zakres ważności funkcji identyczny w przeciwnym wypadku zasłonięcie Przykład: Plik1: #include <iostream> void dzwiek(int a) cout << a << " nuty" << endl; void dzwiek(float x) cout << "Czestotliwosc : " << x << " hercow " << endl; Funkcje wykorzystane w: #include <iostream> using namespace std; extern void dzwiek(int); main() dzwiek(4); extern void dzwiek(float); dzwiek(2); dzwiek(1.37); dzwiek(3); dzwiek(2.71); 4 nuty Częstotliwość : 2 hercow Częstotliwość: 1.37 hercow 3 nuty 2 nuty
Przeładowanie przypadki szczególne przeładowanie możliwe, gdy mamy różnice w liście argumentów nie każde różnice typów dają podstawy do rozróżnień warunek konieczny jednoznaczność ilości i rodzaju argumentów aktualnych (inicjalizatorów) przykłady przypadków, które nie pozwalają na zastosowanie mechanizmu przeładowania: o typy uzyskiwanie przez typedef są w istocie identyczne; nie możemy dokonać przeładowania: typedef int calk; void ff(int); void ff(calk); o tablica vs. wskaźnik również są identyczne z punktu widzenia przeładowania double oblicz(double vector[ ] ); double oblicz(double *wsk); o zmienna vs. referencja na przykład obie poniższe funkcje void fun1(int k); void fun1(int &k); mogą być wywołane z takim samym argumentem aktualnym (inicjalizatorem) są z punktu widzenia przeładowania identyczne int m; fun1(m); o identyczność typu T, const T, volatile T funkcje: void ff(int) ; void ff(const int a) ; void ff(volatile int c); mogą być wywolane identycznie: int r=3; ff(r); (dostaję kopię, dodatkowo obiecuję, że nic nie zmienię (nie będę robił żadnych optymalizacji)
przypadki gdzie przeładowanie mozliwe o Typ wyliczeniowy przyjmuje wartości całkowite, ale w istocie to odrębne typy rozróżnialne w przeładowaniu enum kolor zielony=3, zolty =6, czerwony=9; void zapal(int); void zapal(kolor); o Typy: T*, volatile T*, const T* argumentami są kolejno : adres obiektu typu T, adres obiektu stałego typu T, adres obiektu volatile typu T, obiekty tak wskazane są różnego typu, są one dostępne w miejscu wywołania (oryginały) i nie mogą być użyte zamiennie czyli każdy z nich może wybierać właściwą wersję funkcji przeładowanie działa o podobnie typy T &, volatile T &, const T & Przeładowanie etapy dopasowania w sytuacji przeładowania niekoniecznie musimy znaleźć wersje funkcji która będzie dokładnie odpowiadać liczbie i rodzajowi aktualnych argumentów wywołania dopasowanie jest O.K. gdy kompilator znajdzie dokładnie jedną wersję funkcji, która pasuje lepiej niż inne gdy dwie lub więcej wersji pasuje równie dobrze błąd (niejednoznaczność) zasady poszukiwania właściwej wersji: o dopasowanie dokładne, dopasowanie z trywialną konwersją lista trywialnych konwersji T T& T& T T[ ] T* T(arg) (*T)(arg) T const T T volatile T T* const T* T* volatile
o dopasowanie z awansem (np. zamiana float double, zamiana bez utraty informacji) dla argumentów całkowitych do int (lub unsigned int) o dopasowanie z użyciem konwersji standardowych (równeż takich, które gubią część informacji np. double float, float int) o dopasowania z użyciem konwersji własnych programisty o dopasowanie do funkcji z wielokropkiem gdy mamy kilka argumentów wymagających dopasowania, to procedura jest wykonywana dla każdego z nich wynik dopasowania wersja, do której parametry pasują tak samo lub lepiej niż do innych Przykład: mamy przeładowane wersje funkcji: f(char*, int); f(double, float); f(double, unsigned int); oraz wywołanie: f(2, 3); Która wersja zostanie wykonana?