Pobierz prezentację
Pobieranie prezentacji. Proszę czekać
1
Programowanie obiektowe 2013/2014
Łukasz Sztangret Katedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu o materiały Danuty Szeligi i Pawła Jerzego Matuszyka
2
Konwersje Konwersja polega na przekształceniu obiekty jednej klasy (typu) na inny. Konwersje najczęściej zachodzą niejawnie.
3
Kiedy konwersja się przydaje?
#include<iostream> using namespace std; class cmpx { friend ostream &operator<<(ostream &, const cmpx &); friend cmpx operator+(const cmpx &,const cmpx &); double re; double im; public: cmpx():re(0),im(0){} cmpx(double a, double b) {re=a;im=b;} }; ostream &operator<<(ostream &S, const cmpx &A) S<<A.re<<showpos<<A.im<<"i"; return S; } cmpx operator+(const cmpx &A,const cmpx &B) { cmpx C; C.re=A.re+B.re; C.im=A.im+B.im; return C; } int main() cmpx A(1,2), B(3,4), C, D; C=A+B; cout<<C<<endl; D=A+1; cout<<D<<endl; return 0; 4+6i BŁĄD
4
Co można zrobić??? Przeładować operator+: cmpx=operator+(cmpx,double); Wówczas wywołanie D=A+1; nie wygeneruje błędu. Ale wywołanie D=1+A; tak -> kolejne przeładowania. W klasie cmpx zdefiniować konstruktor konwertujący.
5
Konstruktor konwertujący
To taki konstruktor, który jako argument przyjmuje jeden obiekt takiego typu, z którego konwersje przeprowadzamy. Konwersje mają zachodzić niejawnie dlatego nie może być z przydomkiem explicit. U nas: cmpx(double a):re(a),im(0){} lub jeszcze prościej zmieniamy konstruktor już istniejący tak aby mógł służyć do konewrsji: cmpx(double a, double b=0) {re=a;im=b;}
6
Kiedy konwersja się przydaje?
#include<iostream> using namespace std; class cmpx { friend ostream &operator<<(ostream &, const cmpx &); friend cmpx operator+(const cmpx &,const cmpx &); double re; double im; public: cmpx():re(0),im(0){} cmpx(double a, double b=0) {re=a;im=b;} }; ostream &operator<<(ostream &S, const cmpx &A) S<<A.re<<showpos<<A.im<<"i"; return S; } cmpx operator+(const cmpx &A,const cmpx &B) { cmpx C; C.re=A.re+B.re; C.im=A.im+B.im; return C; } int main() cmpx A(1,2), B(3,4), C, D; C=A+B; cout<<C<<endl; D=A+1; cout<<D<<endl; return 0; 4+6i 2+2i
7
Konwersja z jednej klasy na drugą
class kwadrat { friend class prostokat; double a; public: kwadrat():a(0){} kwadrat(double bok):a(bok){} }; class prostokat friend double pole(const prostokat &); double a, b; prostokat():a(0),b(0){} prostokat(double bok1, double bok2) {a=bok1;b=bok2;} prostokat(const kwadrat &A):a(A.a),b(A.a) {cout<<"Konwersja\n";} double pole(const prostokat &A) { return A.a*A.b; } int main() prostokat A(3,4); kwadrat B(2); cout << pole(A)<<endl; cout << pole(B)<<endl; return 0; 12 Konwersja 4
8
Funkcja konwertująca Konstruktor konwertujący nie nadaje się do dokonywania konwersji na typ wbudowany (nie możemy zdefiniować konstruktora konwertującego dla klasy np. double). Funkcja konwertująca może wykonać przekształcenie z typu zdefiniowanego przez użytkownika na dowolny inny.
9
Funkcja konwertująca z typu K na typ T to
funkcja składowa klasy K o nazwie: K::operator T() Jej cechy: Jest funkcją składową klasy -> jest w niej znany wskaźnik this. Nie ma określonego typu, który zwraca (musi to być typ T). Ma pustą listę argumentów -> nie można jej przeładować. Jest dziedziczona. Może być funkcją wirtualną.
10
Funkcja konwertująca Konwersja 9 4
class kwadrat { friend double pole(const kwadrat &); double a; public: kwadrat():a(0){} kwadrat(double bok):a(bok){} }; class prostokat double a, b; prostokat():a(0),b(0){} prostokat(double bok1, double bok2) {a=bok1;b=bok2;} operator kwadrat() {cout<<"Konwersja\n"; return a;} double pole(const kwadrat &A) { return A.a*A.a; } int main() prostokat A(3,4); kwadrat B(2); cout << pole(A)<<endl; cout << pole(B)<<endl; return 0; Konwersja 9 4
11
Funkcja konwertująca class kwadrat { friend double pole(const kwadrat &); double a; public: kwadrat():a(0){} kwadrat(double bok):a(bok){} }; class prostokat double a, b; prostokat():a(0),b(0){} prostokat(double bok1, double bok2) {a=bok1;b=bok2;} operator kwadrat() {cout<<"Konwersja\n"; return kwadrat(a);} double pole(const kwadrat &A) { return A.a*A.a; } int main() prostokat A(3,4); kwadrat B(2); cout << pole(A)<<endl; cout << pole(B)<<endl; return 0;
12
Funkcja konwertująca K::operator T() – zmiana K->T Typ T nie może być: Tym samym typem co K. Typem &K. Typem void. Typem jakiejś funkcji. Typem reprezentującym tablicę Typem klasy podstawowej dla K.
13
Konstruktor konwertujący vs funkcja konwertująca
Nie można zdefiniować konstruktora dla typu wbudowanego. Konstruktor musi mieć dostęp do zmiennych klasy z której konwertujemy. Konstruktora się nie dziedziczy. Wniosek: Wybieramy funkcję konwertującą!
14
Kiedy zachodzi konwersja
Przy wywoływaniu funkcji. Przy zwracaniu wartości przez funkcję W obecności operatorów. W instrukcjach sterujących (if, switch, while, for). Przy inicjalizacji obiektów.
15
Kilka ciekawych sytuacji
Mamy obiekt OB klasy KL, konwersję KL->int oraz dwie funkcję: void fun(KL); void fun(int); Wywołujemy: fun(OB); Wywołana zostanie funkcja nr 1. Nie ma potrzeby wykonywania konwersji więc kompilator jej nie wykona.
16
Kilka ciekawych sytuacji
Mamy obiekt OB klasy KL, konwersję KL->int oraz jedną funkcję: void fun(double); Wywołujemy: fun(OB); Funkcja zostanie wywołana. Nie mamy wprawdzie zdefiniowanej konwersji KL->double, ale mamy KL->int. Konwersję int->double wykona kompilator (konwersja standardowa).
17
Kilka ciekawych sytuacji
Mamy obiekt OB klasy KL, konwersję int->KL oraz dwie funkcję: void fun(KL); void fun(double); Wywołujemy: fun(1); Wywołana zostanie funkcja nr 2. Zawsze kompilator wcześniej próbuje dopasować typy przy pomocy konwersji standardowych. Dlatego wykona konwersję int->double.
18
Kilka ciekawych sytuacji
Mamy obiekt OB klasy KL, dwie konwersję KL->int i KL->double oraz dwie funkcję: void fun(double); void fun(int); Wywołujemy: fun(OB); Wystąpi BŁĄD. Kompilator nie wie którą konwersję zastosować.
19
Kilka ciekawych sytuacji
Mamy obiekt OB klasy KL, dwie konwersję KL->int i KL->double oraz dwie funkcję: void fun(double); void fun(long); Wywołujemy: fun(OB); Wywołana zostanie funkcja nr 1. Wywołanie funkcji nr 1 wymaga wykonanie konwersji KL->double. Wywołanie funkcji nr 2 wymagałoby wykonania konwersji KL->int->long – dłuższa droga.
20
Kilka ciekawych sytuacji
Mamy obiekt OB klasy KL, dwie konwersję KL->int (zdefiniowana w części prywatnej) i KL->double (zdefiniowana w części publicznej) oraz dwie funkcję: void fun(double); void fun(int); Wywołujemy: fun(OB); Wystąpi BŁĄD. Kompilator nie wie którą konwersję zastosować pomimo, że ma dostęp tylko do jednej.
21
Kilka ciekawych sytuacji
Mamy obiekt OB_A klasy KL_A, obiekt OB_B klasy KL_B i dwie konwersję KL_A->KL_B (konstruktor konwertujący w klasie KL_B) KL_A->KL_B (funkcja konwertująca w klasie KL_A. Błąd wystąpi w momencie, gdy kompilator będzie miał wykonać konwersję niejawną. Kompilator nie będzie wiedział którą konwersję zastosować.
22
Kilka ciekawych sytuacji
Mamy obiekt OB_A klasy KL_A, obiekt OB_B klasy KL_B, dwie konwersję KL_A->KL_B, KL_B->KL_A oraz dwie funkcję: void fun_A(KL_A); void fun_B(KL_B); Po wywołanach: fun_A(OB_A) - brak konwersji fun_A(OB_B) - konwersja KL_B->KL_A fun_B(OB_A) - konwersja KL_A->KL_B fun_B(OB_B) - brak konwersji
23
Dziedziczenie Dziedziczenie – technika pozwalająca na definiowanie nowej klasy w oparciu o klasę zdefiniowaną wcześniej Jeżeli w trakcie realizacji programu konieczne jest zbudowanie nowej klasy, która różni się od klasy zdefiniowanej wcześniej kilkoma składowymi lub wymaga określenia tylko kilku nowych składowych, to można do budowy tej klasy wykorzystać klasę zdefiniowaną wcześniej wykorzystując mechanizm dziedziczenia
24
Dziedziczenie class cmpx //klasa podstawowa { public: double re,im; double modul(); }; class cmpx2: public cmpx //klasa pochodna string nazwa;
25
Dziedziczenie Klasa cmpx2 wywodzi się z klasy cmpx
26
Dziedziczenie vs zawieranie
Klasa A składa się z klasy B – zawieranie class A { ... class B }; Klasa A jest rodzajem klasy B – dziedziczenie class B { ... }; class A: public B
27
Dziedziczenie W klasie pochodnej można: Zdefiniować nowe dane składowe (nadanie klasie nowych cech) Zdefiniować nowe funkcje składowe (wyposażenie klasy w nowe zachowania) Zdefiniować składnik, najczęściej funkcję, już istniejący w klasie podstawowej (możliwość korekty lub zmiany już zdefiniowanych zachowań w klasie podstawowej)
28
class cmpx2: public cmpx
Lista pochodzenia class cmpx2: public cmpx Lista pochodzenia jest informacją, od czego pochodzi klasa pochodna. Definicja klasy na liście pochodzenia musi być znana (nie wystarczy deklaracja zapowiadająca). Lista pochodzenia definiowana jest po nazwie klasy pochodnej i dwukropku : Klasa pochodna dziedziczy wszystkie składniki klasy podstawowej. Do składników z klasy podstawowej można odnosić się tak, jakby zdefiniowane były w klasie pochodnej.
29
Zagnieżdżanie zakresów
Składniki odziedziczone mają zakres klasy podstawowej, na to nakłada się zakres klasy pochodnej Składniki z klasy pochodnej zasłaniają składniki odziedziczone { //zakres klasy podstawowej double re,im; double modul(); { //zakres klasy pochodnej }
30
Zagnieżdżanie zakresów
Odniesienie się do zasłoniętych składników klasy podstawowej wymaga użycia operatora zakresu :: cmpx2 liczba; double Z; Z=liczba.modul(); //wywołanie //funkcji z //klasy cmpx2 Z=liczba.cmpx::modul(); //wywołanie //funkcji z //klasy cmpx
31
Sposoby dziedziczenia
Sposób dziedziczenia private protected public Składniki z klasy podstawowej Są w klasie pochodnej niedostępne
32
Kolejności wywoływania konstruktorów
Najpierw pracują konstruktory klas podstawowy, potem konstruktory klas składowych. Dla hierarchii przedstawionej po prawej kolejność ta będzie następująca: B A D C E
33
Czego się nie dziedziczy?
„Nie dziedziczy się”: konstruktorów, destruktorów, operatora przypisania „Nie dziedziczy się” – składniki te są dostępne w klasie pochodnej i można z nich korzystać (jeżeli są nie prywatne), ale konstruktor, … klasy podstawowej nie staje się konstruktorem, … klasy pochodnej.
34
Operator= w warunkach dziedziczenia
Operator przypisania nie jest generowany automatycznie gdy: klasa podstawowa ma zdefiniowany operator przypisania jako prywatny, klasa składowa ma zdefiniowany operator przypisania jako prywatny, klasa ma składnik const lub będący referencją.
35
Konstruktor kopiujący w warunkach dziedziczenia
Konstruktor kopiujący nie jest generowany automatycznie gdy: klasa podstawowa ma zdefiniowany konstruktor kopiujący jako prywatny, klasa składowa ma zdefiniowany konstruktor kopiujący jako prywatny,
36
Inicjalizacja i przypisanie według stałego obiektu
Automatycznie generowany konstruktor kopiujący (operator=) zapewni nietykalność obiektowi wzorcowemu jeżeli: konstruktory kopiujące (operatory=) we wszystkich klasach podstawowych zapewniają nietykalność, konstruktory kopiujące (operatory=) we wszystkich klasach składowych zapewniają nietykalność.
37
Definiowanie operatora przypisania i konstruktora kopiującego dla klasy pochodnej
class cmpx { public: double re,im; cmpx(double a=0,double b=0); cmpx(const cmpx &A); cmpx &operator=(const cmpx &A); double modul(); }; class cmpx2: public cmpx string nazwa; cmpx2(double a=0,double b=0,string n="NN"); cmpx2(const cmpx2 &A); cmpx2 &operator=(const cmpx2 &A);
38
Definiowanie operatora przypisania dla klasy pochodnej
cmpx2 &cmpx2::operator=(const cmpx2 &A) { (*this).cmpx::operator=(A); nazwa=A.nazwa; return *this; } wywołanie operatora przypisania dla części odziedziczonej przypisanie części nie odziedziczonej
39
Definiowanie konstruktora kopiującego dla klasy pochodnej
cmpx2::cmpx2(const cmpx2 &A):cmpx(A) { nazwa=A.nazwa; } wywołanie konstruktora kopiującego dla części odziedziczonej przypisanie części nie odziedziczonej
40
Dziedziczenie wielokrotne
Dziedziczenie od kilku klas podstawowych. class A {...}; class B class C: public A, public B Dana klasa podstawowa może pojawić się liście pochodzenia tylko raz.
41
Dziedziczenie wielokrotne – kolejność wywoływania konstruktorów
class A {A(int i);}; class B {B(int i);}; class C: public A, public B {C(int i);}; C::C(int i):A(i),B(i){...} Kolejność jest zgodna z kolejnością na liście inicjalizacyjnej (czyli: 1.A, 2.B, 3.C).
42
Wieloznaczność Wieloznaczność to sytuacja, gdy wyrażenie odnoszące się do składnika klasy podstawowej, równie dobrze może odnosić się do innego składnika drugiej klasy podstawowej, np. C.x – BŁĄD C.A::x – OK C.B::x – OK
43
Wieloznaczność Bliższe pokrewieństwo nie usuwa wieloznaczności. D.x – BŁĄD D::A.x – OK D::C.x – OK D::B.x – OK – wystarczy wskazać gałąź
44
Wieloznaczność Najpierw sprawdzana jest jednoznaczność, a dopiero potem ewentualny dostęp. Dlatego, odniesienie się do składnika x wywoła błąd. C.x – BŁĄD
45
Konwersje standardowe przy dziedziczeniu
Jeśli klasa podstawowa jest dziedziczona publicznie, to: Wskaźnik do obiektu klasy pochodnej może być niejawnie przekształcony na wskaźnik dostępnej jednoznacznie klasy podstawowej Referencja do obiektu klasy pochodnej może być niejawnie przekształcona na referencje dostępnej jednoznacznie klasy podstawowej
46
Konwersje standardowe przy dziedziczeniu
class A { public: int liczba; }; class B: public A ... void fun(A &ob) ob.liczba=100; } void fun1(A *wsk) wsk->liczba=0; int main() { A a; B b; fun(a); fun(b); //niejawna konwersja fun1(&a); fun1(&b); //niejawana konwersja return 0; }
47
Konwersje standardowe przy dziedziczeniu
Niejawne konwersje standardowe wskaźnika lub referencji zachodzą w przypadku: przesyłania argumentów do funkcji zwracania przez funkcję wyniku będącego wskaźnikiem lub referencją wyrażeń inicjalizujących Niemożliwe są konwersje: kl_pochodna** -> kl_podstawowa** *kl_pochodna[] -> *kl_podstawowa[]
48
Dziedziczenie wirtualne
Dana jest hierarchia: W obrębie klasy D występują dwukrotnie składniki odziedziczone od klasy A.
49
Dziedziczenie wirtualne
Stosując dziedziczenie wirtualne otrzymujemy hierarchię: Co zyskujemy? W obrębie klasy D składniki odziedziczone od klasy A występują jednokrotnie – klasa D zajmuje mniej miejsca. Nie występuje wieloznaczność.
50
Dziedziczenie wirtualne
class A {...}; class B: public virtual A class C: public virtual A class D: public B, public C Na liście inicjalizacyjnej klasy D słówko virtual już nie występuje
51
Funkcje wirtualne – POLIMORFIZM
Polimorfizm – wywołanie funkcji wirtualnej przez wskaźnik lub referencje do obiektu klasy podstawowej Obiekt, na który pokazuje wskaźnik decyduje, którą wersję funkcji wybrać (nie typ wskaźnika) Funkcja identyczna – funkcja o takiej samej nazwie, argumentach i typie zwracanego wyniku Jeśli funkcja składowa klasy jest zadeklarowana jako wirtualna, to wszystkie funkcje o takiej samej sygnaturze, zdefiniowane w klasach pochodnych od tej klasy, są automatycznie wirtualne
52
Funkcje wirtualne class figura{ public: double a; virtual double pole();}; double figura::pole() {return 0;} class kolo: public figura{ double pole() {return 3.14*a*a;}}; class kwadrat: public figura{ {return a*a;}}; int main(){ figura f,*wf; kolo ko; kwadrat kw; f.a=1; ko.a=2; kw.a=3; wf=&f; cout<<wf->pole()<<endl; wf=&ko; wf=&kw; system("pause"); return 0;} Funkcja pole z klasy figura Funkcja pole z klasy kolo Funkcja pole z klasy kwadrat
53
Funkcje wirtualne Funkcja wirtualna nie może być typu static. Funkcja wirtualna może być zadeklarowana przez inną klasę jako friend. Funkcja zaprzyjaźniona nie może zachowywać się polimorficznie wobec klasy, z którą się przyjaźni. Za wirtualność się płaci: Klasa zawierająca funkcje wirtualne zajmuje więcej miejsca w pamięci. Wywołanie funkcji wirtualnej zajmuje więcej czasu.
54
Wczesne i późnie wiązanie
Wczesne wiązanie to wiązanie na etapie kompilacji. Oznacza to, że w trakcie kompilowania programu odbywa się powiązanie wywołań funkcji z adresami określającymi, gdzie te funkcje są. Późnie wiązanie to wiązanie na etapie wykonywania. Kompilator wygeneruje taki kod, że decyzja o powiązaniu wywołania funkcji z określoną wersją funkcji wirtualnej będzie podejmowane dopiero na etapie wykonywania programu.
55
Wczesne wiązanie funkcji wirtualnych
Wywołanie na rzecz obiektu. Jawne użycie kwalifikatora zakresu. Wywołanie z konstruktora (destruktora) klasy podstawowej.
56
Funkcje czysto wirtualne
class figura { public: double a; virtual double pole()=0; }; Klasa jest klasą abstrakcyjną jeżeli ma co najmniej jedną funkcją czysto wirtualną. Klasa abstrakcyjna Funkcja czysto wirtualna
57
Klasy abstrakcyjne Jeżeli klasa jest klasą abstrakcyjną to nie można zdefiniować żadnego obiektu tej klasy: figura f; - BŁĄD void funkcja(figura f); - BŁĄD figura funkcja(); - BŁĄD Klasa abstrakcyjna nie może być typem jawnej konwersji. W jakim celu tworzyć klasę, która nie może mieć żadnego obiektu??? Po to by ją dziedziczyć.
58
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:
Podobne prezentacje
© 2024 SlidePlayer.pl Inc.
All rights reserved.