Pobieranie prezentacji. Proszę czekać

Pobieranie prezentacji. Proszę czekać

Programowanie obiektowe III rok EiT dr inż. Jerzy Kotowski Wykład XII.

Podobne prezentacje


Prezentacja na temat: "Programowanie obiektowe III rok EiT dr inż. Jerzy Kotowski Wykład XII."— Zapis prezentacji:

1 Programowanie obiektowe III rok EiT dr inż. Jerzy Kotowski Wykład XII

2 Program wykładu Język C++ –Klasy, c.d. przeciążanie (przeładowanie) operatorów 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 Reguły przeciążania operatorów Przeciążenia operatora dokonuje się definiując swoją własną funkcję, która: –Nazywa się jest operatorem, o którego przeładowanie nam chodzi. –Co najmniej jeden z argumentów jest obiektem danej klasy. Składnia: typ Lista operatorów, które mogą być przeładowane: + - * / % ^ & | ~ ! = += -= *= /= %= ^= &= |= > >>= = && || ++ --, ->* -> new delete () [] Operatory & * - + mogą być przeciążane w zarówno w wersji jednoargumentowej jak i dwuargumentowej. Lista operatorów, które nie mogą być przeciążane:..* :: ?:

5 Reguły przeciążania operatorów c.d. Przeciążać można wymienione operatory i tylko te. Za wyjątkiem operatorów new i delete nie ma ograniczeń na typ zwracanej wartości. Nie można zmieniać priorytetu operatora. Nie można zmieniać liczby argumentów operatora. Nie można zmieniać łączności operatora. Ten sam operator można przeciążyć wielokrotnie. Przynajmniej jeden z argumentów musi być typu zdefiniowanego przez użytkownika. Nie ma znaczenia który. Jeżeli funkcja operatorowa jest zdefiniowana jako funkcja zewnętrzna (tzn. nie jako funkcja składowa w klasie) to liczba argumentów na liście musi się zgadzać z liczbą, na której pracuje operator. W przypadku operatora definiowanego jako funkcja składowa lista argumentów nie zawiera lewego operandu. Jest nim obiekt typu klasa, w której dokonuje się takiego przeciążenia. Operatory predefiniowane: = &, new delete

6 Przeciążamy co popadnie.. \test0.sln.. \test0.sln Program vect1.cpp class vect { int *p; int size; public: int ub;// upper bound = size-1 vect(int n=10);// konstruktor z domniemanym argumentem vect(const vect &v);// konstruktor kopiujący ~vect() { delete p; }// destruktor vect& operator =(const vect &v);// operator podstawiania int operator ==(const vect &v) const;// porównanie wektorów int& operator[](int i);// operator indeksowy vect operator+(const vect &v) const;// dodawanie wektorów vect operator-(const vect &v) const;// odejmowanie wektorów int operator*(const vect &v) const; // iloczyn skalarny vect operator*(int a) const;// mnożenie wektora przez skalar void operator !() const;// print };

7 Opis interfejsu klasy Operator podstawiania i operator indeksowy udostępniają odpowiednie obiekty. Przeciążenie operatora podstawiania jest niezbędne do poprawnej pracy programu. Wszystkie pozostałe przeciążone operatory obiecują nie zmieniać swoich operandów. Podobnie deklaruje konstruktor kopiujący. Wielokrotnie korzystano ze słowa kluczowego this np. w składni vect temp(*this); Mnożenie wektora przez liczbę zdefiniowano jako funkcję składową w klasie. Było to możliwe bo lewy argument jest w tym przypadku obiektem klasy. Mnożenie liczby przez wektor zdefiniowano jako funkcję zewnętrzną ze względu na to, że lewy argument jest obiektem typu build-in. Definicja ta korzysta z funkcji składowej i jest bardzo prosta ale formalnie jest niezbędna: vect operator*(int a, const vect &v) { return v*a; }

8 Kod klienta - opis Zakładamy trzy wektory trójelementowe a, b, c. Do dwóch pierwszych coś podstawiamy za pomocą przeciążonego operatora indeksowego. Do wektora c podstawiamy wartość wyrażenia 2*a-b. Wyprowadzamy wartości wektorów a, b, c przy pomocy przeciążonego operatora logicznej negacji. Na koniec wyprowadzamy wartość iloczynu skalarnego a*b. Prawidłowy jest napis vect d[5]; Jest to 5-elementowa tablica obiektów typu vect zakładanych przy pomocy konstruktora domniemanego, to znaczy 10-elementowych. Użycie: d[2][3] = 7; W powyższej instrukcji użyto dwóch różnych operatorów indeksowych: wbudowanego i przeciążonego.

9 Klasa histogram.. \test0.sln.. \test0.sln Cel: przeciążenie operatora << dopisującego nową wartość do histogramu. Interfejs klasy zawiera tylko niezbędne elementy. Na potrzeby kodu klienta potrzebne są poniższe własności klasy: –Dynamiczna rezerwacja pamięci –Klasa ma mieć zabezpieczenie przed wyjściem poza obszar histogramu. –Łączność operatora << w celu umożliwienia napisów typu: h << a << b; histogram.cpp

10 Definicja klasy histogram class histogram { double min;// dolna granica double max;// górna granica int przedz;// liczba przedziałów double * adlk;// wskaźnik do tablicy liczników double szer; // szerokość przedziału public: histogram(double=0.0,double=1.0,int=10); // konstruktor ~histogram (){ delete [] adlk; }// destruktor histogram & operator << (double);// dorzuca wartość int operator [] (int); // licznik - zwraca wartość pola };

11 Najważniejsze elementy klasy Przeciążony operator udostępnia siebie co rozwiązuje problem łączności operatora: –histogram& –return (*this); histogram& histogram::operator<<(double w) { int np=(w-min)/szer; if((np >= 0)&&(np<=przedz-1)) adlk[np]++; //tylko "dobre" wartości return (*this); } int histogram::operator [] (int n) { if((n =przedz)) n=0; return adlk[n]; }

12 Kod klienta void main(void) { const int lprzedz=4; double min=0, max=5, szer=(max-min)/lprzedz; histogram h(min,max,lprzedz);// cztery przedziały od 0 do 5 h << 1.5 << 2.4 << 3.8 << 3.0 << 2.0 << 3.5 << 2.8 << 4.6; h << 12.0 << -3.5; for (int i=0;i<10;i++) h << i/2.0; cout << "Przedzial liczba\n"; for(i=0;i

13 Przeciążanie operatorów inkrementacji i dekrementacji Nienazwany argument pozwala przeciążać operatory inkrementacji i dekrementacji jako składowe klasy tak, aby można było odróżnić znaczenia post i prefiksowe. Przypomnienie: void Ala(int) Nienazwany argument występuje w definicji i w prototypie. Operator z jednym argumentem udaje operator dwuargumentowy i ten drugi argument może być argumentem nienazwanym. Odpowiada to składni x operator y, gdzie pierwszy argument to ja sam a drugiego może nie być. Czyli: x++ to przeciążenie operatora z dwoma a ++x przeciążenie operatora z jednym argumentem. Przykład: class String { public: String& operator ++(); String operator ++(int); };

14 Klasa String.. \test0.sln.. \test0.sln class String{// String dynamiczny IV.cpp char *s; int len; static Stan; public: String(void); String(int n); String(const char *p); String(const String& str); ~String() { delete s; } int lenght() const { return(len); } String& operator =(const String &str);// operator podstawiania void print() const {cout << s << "\nLenght: " << len << "\n";} void operator !() const {print();} // print operator char *() const { return(s); }// operator konwersji static void switch_stan(void); String& operator ++(void); // małe na duże i zwraca nowy String operator ++(int); // małe na duże i zwraca stary String& operator --(void); // duże na małe i zwraca nowy String operator --(int); // duże na małe i zwraca stary friend String operator +(const String str1, const String str2);};

15 Interesujące elementy klasy String Klasa String podobnie jak poprzednio ma cztery konstruktory Konstruktor kopiujący korzysta z przeciążonego operatora podstawiania Pojawił się przeciążony operator dodawania zdefiniowany jako funkcja globalna zaprzyjaźniona z klasą: friend String operator +(const String str1, const String str2); Wszędzie gdzie jest to możliwe pojawiło się słowo kluczowe const Dokonano przeciążenia jednoargumentowego operatora logicznego ! String& operator ++(void); zamienia małe litery na duże i zwraca nowy string jako referencję String operator ++(int); zamienia małe litery na duże i zwraca stary string przez kopiowanie String& operator --(void); zamienia duże litery na małe i zwraca nowy string jako referencję String operator --(int); zamienia duże litery na małe i zwraca stary string przez kopiowanie

16 Wybrane definicje funkcji String::String(const String& str) { s=0; (*this)=str; } String& String::operator ++(void) { char mm = 'A' - 'a'; for(int i=0; i='a' && s[i] <='z') s[i] += mm; return (*this); } String String::operator ++(int) { String temp(*this); char mm = 'A' - 'a'; for(int i=0; i='a' && s[i] <='z') s[i] += mm; return temp; } String operator +(const String str1, const String str2) { String temp(str1.len+str2.len); strcpy(temp.s,str1.s); strcat(temp.s,str2.s); return temp;}

17 Coś za coś Operator dodawania obiektów klasy String: String operator +(const String str1, const String str2) można zdefiniować jako funkcję składową w klasie: String String::operator +(const String& str) const Korzyści: Niepotrzebna jest już przyjaźń Straty: Nie ma możliwości dokonywania niejawnej konwersji argumentów. Związane jest to z faktem, że napis a+b jest równoważny napisowi a.operator+(b). Nie można zatem w kodzie klienta napisać na przykład c=a+b jeżeli a nie jest typu String. Ewentualne pytanka na klasówkę: –W jaki sposób wykonywana jest linia: cout << a+b; –W jaki sposób wykonywana jest linia: cout << str1+b; –Czy kompilator przeżyje coś takiego: cout << str1+str2;

18 Analogie – klasa Complex Należy usunąć konwersję na typ double bo prowadzi ona do niejednoznaczności w wyrażeniu 1+a: error C2666: 'operator`+'' : 2 overloads have similar conversions class Complex { double Re,Im; public: Complex(float x=0, float y=0): Re(x), Im(y){} void print() { cout << Re << "+" << Im << "i "; } // operator double() { return(sqrt(Re*Re+Im*Im)); } trzeba usunąć!!! friend Complex operator+(Complex z1, Complex z2); }; Complex operator+(Complex z1, Complex z2) { // Operator dodawania przeciążony jako funkcja globalna Complex temp; temp.Re=z1.Re+z2.Re; temp.Im=z1.Im+z2.Im; return temp; } void main(void) { Complex a(1,5),b(0,-2),c; cout << "\na= "; a.print(); cout << "\nb= "; b.print(); c=1+a+b; cout << "\n1+a+b= "; c.print(); // Niejawna konwersja argumentu getch(); }


Pobierz ppt "Programowanie obiektowe III rok EiT dr inż. Jerzy Kotowski Wykład XII."

Podobne prezentacje


Reklamy Google