Podstawy informatyki 2013/2014 Łukasz Sztangret Katedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu o materiały Danuty Szeligi i Pawła Jerzego Matuszyka
Przeładownie nazwy funkcji polega na tym, że w danym zakresie ważności jest więcej niż jedna funkcja o takiej samej nazwie. To, która z nich zostaje w danym przypadku uaktywniona, zależy od liczby i typu argumentów wywołania jej. 1 1 Grębosz J., Symfonia C++ standard
Wybór funkcji do przeładowania Warto rozważyć przeładowanie deklaracji funkcji gdy: –funkcje posiadają pewną cechę wspólną a mają działać na obiektach różnych typów int wypiszWynik( const jakas_klasa & A); int wypiszWynik( const double A[], unsigned int size); –funkcje w sensie abstrakcyjnym wykonują analogiczną czynność, która w języku naturalnym ma tę samą nazwę double potega( double x, double y); int potega( int x, int y); I odwrotnie: nie przeładowywać deklaracji, jeśli nie potrzebujemy tej samej nazwy dla różnych działań
Lista argumentów przy przeładowaniu Dwie funkcje mają różną listę argumentów gdy: Funkcje posiadają różną liczbę argumentów Funkcje posiadają różne typy argumentów (na tych samych pozycjach) Funkcje posiadają różną kolejność argumentów Typ zwracany przez przeładowaną funkcję nie jest brany pod uwagę! Definicje funkcji o takiej samej nazwie i które nie spełniają powyższych wymagań są wzajemnie niejednoznaczne
Różna liczba argumentów #include using namespace std; void napis(int a){cout<<"1 x int\n";} void napis(int a,int b){cout<<"2 x int\n";} void napis(int a,int b,int c){cout<<"3 x int\n";} int main() { napis(1); napis(1,2); napis(1,2,3); return 0; } 1 x int 2 x int 3 x int
Różne typy argumentów #include using namespace std; void napis(int a){cout<<"int\n";} void napis(double a){cout<<"double\n";} void napis(char a){cout<<"char\n";} int main() { napis(1); napis(1.5); napis('a'); napis(static_cast (1.5)); return 0; } int double char int
Różna kolejność argumentów #include using namespace std; void napis(int a, double b){cout<<"int, double\n";} void napis(double b, int a){cout<<"double, int\n";} int main() { napis(1,1.5); napis(1.5,1); return 0; } int, double double, int
Różne typy funkcji #include using namespace std; int zwroc(double a){return static_cast (a);} double zwroc(double a){return a;} int main() { int a=zwroc(1.5); double b=zwroc(1.5); return 0; } BŁĄD – funkcje nierozróżnialne cout<<zwroc(1.5);
Argumenty domniemane #include using namespace std; void napis(int a){cout<<"int\n";} void napis(int a, double b=0){cout<<"int, double\n";} int main() { napis(1,1.5); napis(1); return 0; } BŁĄD – pasuje do obu funkcji
typedef #include using namespace std; typedef int calkowity; void napis(int a){cout<<"int\n";} void napis(calkowity a){cout<<"calkowity\n";} int main() { return 0; } BŁĄD – funkcje nierozróżnialne
enum #include using namespace std; enum litera {a=1,b,c,d}; void napis(int a){cout<<"int\n";} void napis(litera l){cout<<"litera\n";} int main() { litera l=a; int i=1; napis(l); napis(i); return 0; } litera int
Tablica i wskaźnik #include using namespace std; void napis(int a[]){cout<<"tablica\n";} void napis(int *a){cout<<"wkaznik\n";} int main() { return 0; } BŁĄD int [] -> int * - trywialna konwersja
Tablice wielowymiarowe #include using namespace std; void napis(int a[2][3]){cout<<"tablica 2x3\n";} void napis(int a[3][4]){cout<<"tablica 3x4\n";} int main() { int tab1[2][3]={1,2,3,4,5,6}; int tab2[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}; napis(tab1); napis(tab2); return 0; } tablica 2x3 tablica 3x4
Tablice wielowymiarowe #include using namespace std; void napis(int a[2][3]){cout<<"tablica 2x3\n";} void napis(int a[3][3]){cout<<"tablica 3x3\n";} int main() { return 0; } BŁĄD Pierwszy rozmiar tablicy nie jest brany pod uwagę
Referencja #include using namespace std; void napis(int a){cout<<"wartosc\n";} void napis(int &a){cout<<"referencja\n";} int main() { napis(1); int a=1; napis(a); return 0; } BŁĄD – dwuznaczne wywołanie
Typy: T, const T, volatile T #include using namespace std; void napis(int a){cout<<"int\n";} void napis(const int a){cout<<"const int\n";} void napis(volatile int a){cout<<"volatile int\n";} int main() { return 0; } BŁĄD const oraz volatile mówią kompilatorowi jak traktować zmienną wewnątrz funkcji
Typy: T*, const T*, volatile T* #include using namespace std; void napis(int *a){cout<<"int*\n";} void napis(const int *a){cout<<"const int*\n";} void napis(volatile int *a){cout<<"volatile int*\n";} int main() { int a=1; const int b=2; volatile int c=3; napis(&a); napis(&b); napis(&c); return 0; } int* const int* volatile int*
Typy: T&, const T&, volatile T& #include using namespace std; void napis(int &a){cout<<"int&\n";} void napis(const int &a){cout<<"const int&\n";} void napis(volatile int &a){cout<<"volatile int&\n";} int main() { int a=1; const int b=2; volatile int c=3; napis(a); napis(b); napis(c); return 0; } int& const int& volatile int&
Wskaźnik do przeładowanej funkcji #include using namespace std; void napis(int a){cout<<"int\n";} void napis(double a){cout<<"double\n";} void napis(char a){cout<<"char\n";} int main() { void (*wsk1)(int)=napis; void (*wsk2)(double)=napis; void (*wsk3)(char)=napis; wsk1(1); wsk2(1.5); wsk3('a'); wsk1(1.5); return 0; } int double char int
Wskaźnik do przeładowanej funkcji #include using namespace std; void napis(int a){cout<<"int\n";} void napis(double a){cout<<"double\n";} void wywolaj1(void (*wsk)(int)){ cout<<"void (*wsk)(int)\n"; wsk(1);} void wywolaj2(void (*wsk)(double)){ cout<<"void (*wsk)(double)\n"; wsk(1.5);} int main() { wywolaj1(napis); wywolaj2(napis); return 0; } void (*wsk)(int) int void (*wsk)(double) double
Konwersje przy dopasowaniu Kompilator dopasowując deklarację funkcji może przeprowadzić konwersje argumentów Dla każdego argumentu może zostać przeprowadzona oddzielna ścieżka konwersji, o określonej jakości Rozważana w dopasowaniu funkcja F1 jest lepsza od innej rozważanej funkcji F2 jeżeli: –Ścieżka konwersji F1 dla każdego argumentu jest nie gorsza niż ścieżka dla F2 ORAZ –Dla co najmniej jednego argumentu ścieżka konwersji jest lepsza dla F1 Ogólnie jakość konwersji zależy od stopnia jej skomplikowania oraz stratności
Etapy dopasowania 1.Dopasowanie dokładne –dopasowanie dokładne z trywialną konwersją 2.Dopasowanie na zasadzie awansowania 3.Dopasowanie z użyciem konwersji standardowych 4.Dopasowanie z użyciem konwersji zdefiniowanych przez użytkownika 5.Dopasowanie do funkcji z wielokropkiem
Konwersje przy dopasowaniu 1.Dopasowanie dosłowne Dopasowanie dosłowne z trywialną konwersją int [] -> int* 2.Dopasowanie z awansem float -> double char signed char unsigned char-> int -> unsigned int short int unsigned short int
Konwersje przy dopasowaniu 2.Dopasowanie z awansem c.d. bool -> int wchar_t enum-> int -> unsigned int -> long -> unsigned long 3.Dopasowanie za pomocą konwersji standartowych 1.typu całkowitego int -> unsigned int unsigned int -> int
Konwersje przy dopasowaniu 3.Dopasowanie za pomocą konwersji standartowych c.d. 2.typu zmiennoprzecinkowego double -> float float -> double 3.pomiędzy typem całkowitym a zmiennoprzecinkowym zmiennoprzecinkowy -> całkowity całkowity -> zmiennoprzecinkowy 5.wskaźników dowolny wskaźnik nie-const i nie-volatile -> void* wsk. do klasy pochodnej -> wsk. do klasy podstawowej 6.referencji ref. do klasy pochodnej -> ref. do klasy podstawowej
Konwersje przy dopasowaniu 4.Dopasowanie za pomocą konwersji definiowanych przez użytkownika 5.Dopasowanie do funkcji z wielokropkiem void f(int a){cout<<"int";} void f(double a){cout<<"double";} void f(...){cout<<"...";} f("aaa");
Konwersje wskaźników Konwersje wskaźników nie zachodzą! 1.int* -> double* - przykład z poprzedniego wykładu 2.int* -> int [] – tablica zna swój rozmiar, wskaźnik nie wie czy pokazuje na pojedynczy element, czy na tablicę 3.wskaźnik pokazujący na funkcję wywoływaną z argumentem typu int nie może pokazywać na funkcję wywoływaną z parametrem typu char UWAGA! Nawet typ zwracany jest tutaj istotny!
Przykłady konwersji void fun(double a){cout<<"double\n";} void fun(int a){cout<<"int\n";} int main() { float a=1.5; fun(a); } double
Przykłady konwersji void f(int a){cout<<"int\n";} void f(float a){cout<<"float\n";} int main() { double a=3.4; f(a); } BŁĄD
Przykłady konwersji void fun(double a){cout<<"double\n";} void fun(int a){cout<<"int\n";} int main() { char a='A'; fun(a); } int
Przykłady konwersji void fun(double a){cout<<"double\n";} void fun(char a){cout<<"char\n";} int main() { int a=1; fun(a); } BŁĄD
Konwersje kilku argumentów #include using namespace std; void f(double a, int b){cout<<"double, int\n";} void f(int a, double b){cout<<"int, double\n";} int main() { f(1,2.3); f(1.2,3); f(1,2); return 0; } BŁĄD Obie konwersje są takie same
Funkcja zwracająca większą wartość int wieksza(int a,int b) { return (a > b) ? a : b; } int main() { int a,b; a=5; b=2; cout<<wieksza(a,b)<<endl; double c,d; c=2.5; d=2.2; cout<<wieksza(c,d)<<endl; return 0; } 5 2
ctrl+c, ctrl+v int wieksza(int a,int b) { return (a > b) ? a : b; } double wieksza(double a,double b) { return (a > b) ? a : b; } int main() { int a,b; a=5; b=2; cout<<wieksza(a,b)<<endl; double c,d; c=2.5; d=2.2; cout<<wieksza(c,d)<<endl; return 0; } 5 2.5
Szablon funkcji template typ wieksza(typ a, typ b) { return (a > b) ? a : b; } int main() { int a,b; a=5; b=2; cout<<wieksza(a,b)<<endl; double c,d; c=2.5; d=2.2; cout<<wieksza(c,d)<<endl; return 0; } Parametr szablonu Argumenty funkcji
Funkcje szablonowe Kompilator generuje funkcję szablonową gdy: wywołujemy funkcję, odwołujemy się do jej adresu. Funkcja szablonowa z jednym zestawem parametrów generowana jest tylko raz. Podobnie jak w przypadku przeładowań nazw funkcji typ rezultatu funkcji się nie liczy.
Kilka przykładów template typ1 wieksza(typ1 a, typ2 b, typ1 c) {…} template typ wieksza(typ a, typ *b) {…} template typ wieksza(typ a, int b) {…} template typ_w wieksza(typ1 a, typ2 b, typ1 c, typ_w) {...}
Problemy z typami definiowanymi template typ wieksza(typ a, typ b) { return (a > b) ? a : b; } class zespolona { public: double re,im; }; int main() { zespolona A,B; A.re=5; A.im=2; B.re=1; B.im=3; cout<<wieksza(A,B)<<endl; return 0; }
Problemy z typami wbudowanymi template double funkcja(typ a) { double m=a.modul(); return m; } class zespolona { public: double re,im; double modul(){ return sqrt(re*re+im*im);} }; int main() { zespolona A; A.re=5; A.im=2; cout<<funkcja(A)<<endl; cout<<funkcja(1)<<endl; return 0; }
Funkcja specjalizowana template typ wieksza(typ a, typ b) { cout<<"Funkcja szablonowa\n"; return (a > b) ? a : b; } int wieksza(int a,int b) { cout<<"Funkcja specjalizowana\n"; return (a > b) ? a : b; } int main() { cout<<wieksza(1,2)<<endl; cout<<wieksza(1.5,2.7)<<endl; return 0; } 2 2.7
Kolejność argumentów funkcji template typ1 wieksza(typ1 a, typ2 b) { return (a > b) ? a : b; } int main() { double a=3.5; int b=2; cout<<wieksza(a,b)<<endl; cout<<wieksza(b,a)<<endl; return 0; } 3.5 3
Konflikt template typ1 wieksza(typ1 a, typ2 b) { return (a > b) ? a : b; } template typ2 wieksza(typ1 a, typ2 b) { return (a > b) ? a : b; } Szablony różnią się tylko typem zwracanym
Obiekty lokalne typu typ template typ dzialanie(typ a, typ b) { typ w; w=3*a+b; return w; } int main() { int a=3; int b=2; cout<<dzialanie(a,b)<<endl; return 0; } 11
Typy pochodne template void zmiana(typ &a, typ &b) { typ c=a; a=b; b=c; } int main() { int a=3; int b=2; cout<<a<<b<<endl; zmiana(a,b); cout<<a<<b<<endl; return 0; } 32 23
Typy pochodne template void zmiana(typ &a, typ &b) { typ c=a; a=b; b=c; } int main() { char *a="AAA"; char *b="BBB"; cout<<a<<b<<endl; zmiana(a,b); cout<<a<<b<<endl; return 0; } AAABBB BBBAAA
Szablon generujący funkcje inline template inline typ wieksza(typ a, typ b) { return (a > b) ? a : b; }
Obiekty lokalne statyczne template void funkcja(typ a) { static int licznik=0; licznik++; cout<<licznik<<endl; } int main() { funkcja(1); funkcja(1.0); funkcja(5); funkcja('a'); return 0; }
Run-Time Type Identyfication #include using namespace std; template void funkcja(typ a) { cout<<typeid(a).name()<<endl; } int main() { funkcja(1); funkcja(1.0); funkcja(5); funkcja('a'); return 0; } int double int char
Prezentacja udostępniona na licencji Creative Commons: Uznanie autorstwa, Na tych samych warunkach 3.0. Pewne prawa zastrzeżone na rzecz autorów. Zezwala się na dowolne wykorzystywanie treści pod warunkiem wskazania autorów jako właścicieli praw do prezentacji oraz zachowania niniejszej informacji licencyjnej tak długo, jak tylko na utwory zależne będzie udzielana taka sama licencja. Tekst licencji dostępny jest na stronie: