Programowanie obiektowe III rok EiT

Post on 11-Jan-2016

40 views 1 download

description

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. - PowerPoint PPT Presentation

Transcript of Programowanie obiektowe III rok EiT

Programowanie obiektoweIII rok EiT

dr inż. Jerzy KotowskiWykł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

• C++ for C programmers, Ira Pohl, The Benjamin/Cummings Publishing Company, Inc.

• Symfonia C++, Jerzy Grębosz, Oficyna Kallimach, Kraków 1999

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.

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.

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();

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

Kod klienta

• Zamiast

• jest

• Usunięcie operatora konwersji skutkuje błędem kompilacji

cout << a; cout << b; cout << endl << c; cout << endl << d;

a.print(); b.print(); c.print(); d.print();

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.

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 "; } 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); }

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();

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.

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; … };

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.

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;}

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;}

Konstruktor kopiujący

• Definicja konstruktora kopiującego jest podejrzanie podobna:

• Można to wykorzystać, aby nie powielać kodu:

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?

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;

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 Tkonwersja konstruktor