1 / 18

Programowanie obiektowe III rok EiT

Programowanie obiektowe III rok EiT. dr inż. Jerzy Kotowski Wykład XII. Program wykładu. Język C++ Klasy, c.d. przeciążanie (przeładowanie) operatorów Przykład problemu Podstawy języka JAVA Klasówka. Literatura.

noam
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 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ę operator@ gdzie @ jest operatorem, o którego przeładowanie nam chodzi. • Co najmniej jeden z argumentów jest obiektem danej klasy. • Składnia: typ operator@(argumenty) • 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 • 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 intoperator ==(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 intoperator*(const vect &v) const; // iloczyn skalarny vect operator*(int a) const;// mnożenie wektora przez skalar voidoperator !() 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 • 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ść intoperator [] (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<0) || (n>=przedz))n=0; return adlk[n]; }

  12. Kod klienta void main(void) { constint 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<lprzedz;i++) cout << "[" << i*szer << ", " << (i+1)*szer << "]\t" << h[i] << "\n"; getch(); }

  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 class String{ // String dynamiczny IV.cpp char *s; int len; static Stan; public: String(void); String(int n); String(constchar *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";} voidoperator !() const {print();} // print operatorchar *() const { return(s); }// operator konwersji staticvoid 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<len; i++) if(s[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<len; i++) if(s[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 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(); } • 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

More Related