Pobieranie prezentacji. Proszę czekać

Pobieranie prezentacji. Proszę czekać

Programowanie obiektowe III rok EiT

Podobne prezentacje


Prezentacja na temat: "Programowanie obiektowe III rok EiT"— Zapis prezentacji:

1 Programowanie obiektowe III rok EiT
:39:26 Programowanie obiektowe III rok EiT dr inż. Jerzy Kotowski Wykład XI

2 Program wykładu Język C++ Klasówka Klasy, c.d. Przykład problemu
konwersja typów przeciążanie i selekcja funkcji funkcje zaprzyjaźnione przeciążanie operatorów – operator konwersji Przykład problemu Podstawy języka JAVA Klasówka

3 Literatura C++ for C programmers, Ira Pohl, The Benjamin/Cummings Publishing Company, Inc. Symfonia C++, Jerzy Grębosz, Oficyna Kallimach, Kraków 1999

4 Konwersja typów - tradycyjna
Wyrażenie arytmetyczne typu x+y charakteryzuje się wartością i typem wartości. W trakcie obliczania wartości wyrażeń ma miejsce automatyczna konwersja typów. Prawdziwe są następujące reguły konwersji typów: Wpierw: Typy char oraz short są promowane do typu int. unsigned char i unsigned short są promowane do typu unsigned. Następnie: Jeżeli wyrażenie jest dalej typu mixed expression, wtedy zgodnie z hierarchią typów: int < unsigned < long < unsigned long < float < double operand niższego typu jest promowany do typu drugiego operandu i wartość całego wyrażenia jest też tego typu. Automatyczna konwersja typu ma również miejsce w przypadku operatora podstawiania. Istnieje możliwość dokonywania jawnej konwersji przy pomocy operatora rzutowania. Priorytet 15, jednoargumentowy, prawostronnie łączny.

5 Konwersja abstrakcyjnych typów danych
Explicit conversion jest niezbędna jeżeli konwersja niejawna jest niepożądana oraz kiedy brak konwersji jest błędem składni. C++ w celu integracji ADT oraz typów built-in dysponuje możliwościami jawnej konwersji z użyciem składni: type_name (expression) Przykład: było x=(float)y; jest x=float(y); było p=(char *)q; jest p=char *(q); typedef char *ptr_c; p=ptr_c(q); Konstruktor z jednym argumentem jest operatorem konwersji typu z typu jaki ma argument do typu jaki określa klasa konstruktora. String dynamiczny II.cpp W przykładzie inny jest tylko kod klienta.

6 Kod klienta .. \test0.sln char *str1="Typem, z ktorego dokonuje sie przeksztalcenia "; char *str2="nie musi byc typ wbudowany."; String a(str1),b(str2); String c="Symfonia C++"; String d=String("Jerzy Grebosz"); cout << endl << " ** Wyniki ** " << endl << endl; a.print(); b.print(); c.print(); d.print(); getch(); Z wydruku widać, że wszystkie cztery obiekty zostały utworzone przy pomocy operatora konwersji tzn. konstruktora z jednym argumentem: String::String(char *p)

7 Konwersja z typu ADT .. \test0.sln
Przedstawiony program String dynamiczny II.cpp prezentuje konwersję typu built-in na typ zdefiniowany przez użytkownika czyli w tym przypadku konwersję z typu char* na typ String. Nie jest możliwe dodanie konwersji do istniejącego typu built-in z naszego typu ADT. Musimy taką konwersję zdefiniować sami wewnątrz naszej klasy. Dokonuje się w tym celu przeciążenia operatora rzutowania. Składnia ogólnie: operator type() { ... }. Ten operator jest funkcją składową, która nic nie zwraca i ma pustą listę argumentów. Nie można jej zatem przeciążać. Przykład: chcemy dokonać konwersji odwrotnej z typu String na char*. Dodajemy jedną składową w definicji klasy String. Postać operatora: operator char*() { return(s); } tzn. udostępniamy pole robocze obiektu klasy String. String dynamiczny III.cpp

8 Kod klienta Zamiast jest
Usunięcie operatora konwersji skutkuje błędem kompilacji a.print(); b.print(); c.print(); d.print(); cout << a; cout << b; cout << endl << c; cout << endl << d;

9 Przeciążanie i selekcja funkcji
Przy wywołaniu funkcji przeciążonej kompilator musi uruchomić algorytm selekcji w celu wyboru odpowiedniej funkcji. Postać algorytmu selekcji zależy od tego jakiego typu konwersje są dopuszczalne. Należy upewnić się jakie reguły dopuszcza kompilator, którym się posługujemy. Overloaded Function Selection Algorithm Kompilator próbuje znaleźć funkcję z idealnie pasującymi typami argumentów. Przyjmuje się przy tym, że: short, char oraz stała 0 odpowiadają typowi int, float odpowiada typowi double. Jeżeli to nie wystarcza kompilator próbuje dokonać klasycznej niejawnej konwersji typów dla obiektów typu built-in. Taka konwersja nie ma prawa prowadzić do utraty informacji. Dotyczy ona w związku z tym konwersji wskaźników i następujących rozszerzeń (projekcji na wyższe typy): int do long oraz int do double. Następnie kompilator próbuje wykorzystać typy zdefiniowane przez użytkownika o ile prowadzą one do jednoznacznych reguł konwersji.

10 Przykład – klasa Complex.cpp .. \test0.sln
class Complex { double Re,Im; public: Complex(float x=0, float y=0): Re(x), Im(y){} void assign(double r,double i) { Re=r; Im=i; } void print() { cout << Re << "+" << Im << "i "; } operator double() { return(sqrt(Re*Re+Im*Im)); } // konwersja }; inline int greater(int i,int j) { return(i>j?i:j); } inline double greater(double x,double y) { return(x>y?x:y); } inline Complex greater(Complex w,Complex z) { return(w>z?w:z); } W programie jest przeciążona trzykrotnie funkcja greater. Kompilator nie zgodził się na wywołanie jej z argumentami typu int oraz double: ‘greater' : 3 overloads have similar conversions Formal parameter lists are too similar to resolve ambiguity.

11 Kod klienta Complex u(1,3), v(2,2),z; z=greater(u,v); cout << "Compare "; u.print(); cout << " and "; v.print(); cout << " : greater is "; z.print(); Kompilator wymyślił sobie własną algebrę liczb zespolonych. Complex greater(Complex w,Complex z) {return(w>z?w:z);} Porównanie w>z dokonywane jest poprzez wstępną konwersję w i v do obiektów typu double bo te można porównać. Zwracaną wartością jest liczba zespolona o większym module. Nigdy nie zachodzi konwersja przy zwracaniu wartości ponieważ jeżeli była potrzebna to już została dokonana przy przekazywaniu argumentów. Usunięcie operatora konwersji Complex::operator double() skutkuje błędem kompilacji.

12 Funkcje zaprzyjaźnione – friend functions
Słowo kluczowe friend daje dostęp funkcji, która nie jest member function do ukrytych składowych klasy. Filozofia języka C++, a w ogólności OOP, prowadzi do koncepcji separacji obiektów. Friend functions łamią tę filozofię. Musimy mieć zatem istotny powód, aby z takiego mechanizmu korzystać. Powody: Chcemy zdefiniować funkcję, która będzie miała dostęp do ukrytych składowych więcej niż jednej klasy równocześnie. Wszystkie argumenty friend function ma przekazywane przez listę argumentów. Pozwala to na dokonywanie ukrytej konwersji typów i może być czasami bardzo wygodne i efektywne przy tworzeniu client code. Ma to w szczególności miejsce przy przeciążaniu operatorów. Friend function musi się pojawić w deklaracji klasy, do której jest zaprzyjaźniona. Nazwa funkcji musi być poprzedzona słowem friend. Nie ma znaczenia, czy będzie to w bloku private czy też w bloku public.

13 Przykłady składni przyjaźni .. \test0.sln
Przyjaźń z funkcją zewnętrzną: Przyjaźń z funkcją składową: Przyjaźń z wszystkimi funkcjami składowymi: Przykład z friend function - mnożenie wektor * macierz. Powód wykorzystania takiego mechanizmu - potrzeba dostępu do dwóch klas równocześnie. Vect_matrix.cpp class star { … friend void Ala(); int Ola(); … }; class galaxy { … friend int star::Ola(); … }; class cosmos { … friend class galaxy; … };

14 Opis programu Cel: Stworzenie narzędzia, które pomnoży wektor przez macierz. Mnożenie będzie zdefiniowane jako funkcja zewnętrzna. Wymaga to zadeklarowania przyjaźni w obu klasach Interesujące elementy programu: Forward reference – deklaracja zapowiadająca. Konstruktor kopiujący i przeciążony operator podstawiania w klasie vect. Oba elementy są niezbędne. Widać to po debuggingu. Przeciążony operator podstawiania pojawił się po raz pierwszy. Funkcja vect mpy(vect& v,matrix& m) korzysta z przyjaźni. Klasa matrix została potraktowana po macoszemu. Akurat w tym przykładzie nie było potrzeby utworzenia pełnego interfejsu klasy.

15 Przeciążony operator podstawiania
vect& vect::operator =(const vect &v) { size=v.size; if(p) delete p; p=new int[size]; ub=size-1; for(int i=0;i<size;i++) p[i]=v.p[i]; return *this; } Zdefiniowany jako funkcja składowa Na liście argumentów jest tylko prawy argument. Lewym argumentem jestem ‘ja sam’. Prawy argument jest udostępniany przez referencję i nie można go zmieniać. Operator udostępnia wynik (referencję), czyli lewy argument. Technika udostępnienia wymaga użycia wskaźnika this. Użycie: a=b=c; Referencja!!! Można napisać równieć: a.operator=(b); ale po co?

16 Konstruktor kopiujący
Definicja konstruktora kopiującego jest podejrzanie podobna: Można to wykorzystać, aby nie powielać kodu: vect& vect::operator =(const vect &v) { size=v.size; if(p) delete p; p=new int[size]; ub=size-1; for(int i=0;i<size;i++) p[i]=v.p[i]; return *this; } vect::vect(const vect &v) { size=v.size; p=new int[size]; ub=size-1; for(int i=0;i<size;i++) p[i]=v.p[i]; } vect::vect(const vect &v) { p=0; *this=v; } Ta linia musi być Dlaczego?

17 Słowo kluczowe operator
Słowo kluczowe operator służy do: tworzenia funkcji członkowskiej, która dokonuje konwersji typu przeciążania operatorów. W programie Vect_matrix.cpp nagłówek funkcji vect mpy(vect& v,matrix& m) może zostać zastąpiony przez vect operator* (vect& v, matrix& m) Wywołanie lepiej wygląda. Zamiast instrukcji y=mpy(x,A); pisze się: y=x*A; Lista WSZYSTKICH niezbędnych zmian w programie: W definicjach klas prototyp funkcji mpy powinna zastąpić linia: friend vect operator* (vect& v,matrix& m); nagłówek funkcji mpy powinien zostać zastąpiony przez vect operator* (vect& v, matrix& m) W segmencie main zamiast y=mpy(x,A); ma się pojawić: y=x*A;

18 K T Operator konwersji konwersja konstruktor
Do przekształcenia jednego typu na drugi mamy dwa narzędzia: Konstruktor jednoargumentowy: T::T(K& k); Funkcję konwertującą (operator konwersji): K::operator T(); Zdaniem J. Grębosza konwersja jest rozwiązaniem zawsze lepszym. Wynika to między innymi z następujących faktów: Nie można zdefiniować konstruktora dla typu wbudowanego. Nie można napisać konstruktora dla klasy, która nie jest naszą własnością. Konstruktor wymaga idealnego dopasowania argumentów. Konstruktor nie jest dziedziczony przez klasy pochodne a operator konwersji jest – tak samo jak wszystkie inne funkcje składowe


Pobierz ppt "Programowanie obiektowe III rok EiT"

Podobne prezentacje


Reklamy Google