1 / 18

Programowanie obiektowe III rok EiT

Programowanie obiektowe III rok EiT. dr inż. Jerzy Kotowski Wykład XI. Program wykładu. Język C++ Klasy, c.d. 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. Literatura.

alyssa
Download Presentation

Programowanie obiektowe III rok EiT

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Programowanie obiektoweIII rok EiT dr inż. Jerzy Kotowski Wykład XI

  2. Program wykładu • Język C++ • Klasy, c.d. • 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 unsignedshort są promowane do typu unsigned. • Następnie: • Jeżeli wyrażenie jest dalej typu mixedexpression, wtedy zgodnie z hierarchią typów:int < unsigned < long < unsigned long < float < doubleoperand 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 • Z wydruku widać, że wszystkie cztery obiekty zostały utworzone przy pomocy operatora konwersji tzn. konstruktora z jednym argumentem: String::String(char *p) 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();

  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 longoraz 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 • 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. 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 "; } operatordouble() { return(sqrt(Re*Re+Im*Im)); } // konwersja }; inlineint greater(int i,int j) { return(i>j?i:j); } inlinedouble greater(double x,double y) { return(x>y?x:y); } inline Complex greater(Complex w,Complex z) { return(w>z?w:z); }

  11. Kod klienta • 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. 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();

  12. Funkcje zaprzyjaźnione – friend functions • Słowo kluczowe friend daje dostęp funkcji, która nie jest memberfunction do ukrytych składowych klasy. • Filozofia języka C++, a w ogólności OOP, prowadzi do koncepcji separacji obiektów. Friendfunctions ł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 friendfunction 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 clientcode. Ma to w szczególności miejsce przy przeciążaniu operatorów. • Friendfunction 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 wszystkimifunkcjami 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 • 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? 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; }

  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 operatorsł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 mpypowinna zastąpić linia: friend vect operator* (vect& v,matrix& m); • nagłówek funkcji mpy powinien zostać zastąpiony przezvect operator* (vect& v, matrix& m) • W segmencie main zamiast y=mpy(x,A); ma się pojawić: y=x*A;

  18. Operator konwersji • 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 K T konwersja konstruktor

More Related