460 likes | 591 Views
Programowanie obiektowe 2013/2014. Łukasz Sztangret Katedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu o materiały Danuty Szeligi i Pawła Jerzego Matuszyka. Dostęp do składników prywatnych. int main () { punkt A; A.wpisz (); return 0; }
E N D
Programowanie obiektowe2013/2014 Łukasz SztangretKatedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu o materiałyDanuty Szeligi i Pawła Jerzego Matuszyka
Dostęp do składników prywatnych intmain() { punkt A; A.wpisz(); return 0; } void wypisz(punkt &A) { cout<<"x = "<<A.x<<endl; cout<<"y = "<<A.y<<endl; } class punkt { double x,y; public: void wpisz(); }; voidpunkt::wpisz() { cin>>x>>y; } void wypisz(punkt &A); BŁĄD Funkcje nie będące składnikami klasy nie mają dostępu do ich składników prywatnych
Dostęp do składników prywatnych intmain() { punkt A; A.wpisz(); A.wypisz(); return 0; } class punkt { double x,y; public: void wpisz(); void wypisz(); }; voidpunkt::wpisz() { cin>>x>>y; } voidpunkt::wypisz() { cout<<"x = "<<x<<endl; cout<<"y = "<<y<<endl; }
Funkcja zaprzyjaźniona intmain() { punkt A; A.wpisz(); wypisz(A); return 0; } void wypisz(punkt &A) { cout<<"x = "<<A.x<<endl; cout<<"y = "<<A.y<<endl; } Funkcja zaprzyjaźniona z klasą to funkcja, która ma dostęp do wszystkich zmiennych składowych klasy. class punkt { double x,y; public: void wpisz(); friendvoid wypisz(punkt &A); }; voidpunkt::wpisz() { cin>>x>>y; } void wypisz(punkt &A);
Funkcja zaprzyjaźniona class kolo; class punkt { double x,y; public: void wpisz(); friendbool czy(punkt &,kolo &); }; class kolo { double x,y,r; public: void wpisz(); friendbool czy(punkt &,kolo &); }; voidpunkt::wpisz() { cin>>x>>y; } voidkolo::wpisz() { cin>>x>>y>>r; } bool czy(punkt &A, kolo &K) { if (sqrt(pow(A.x-K.x,2)+pow(A.y-K.y,2))<=K.r) return true; return false; } intmain() { punkt A; kolo K; A.wpisz(); K.wpisz(); cout << czy(A,K) <<endl; return 0; }
Klasy zaprzyjaźnione class punkt { double x,y; }; class kolo { punkt O; double r; public: void wpisz(); }; voidkolo::wpisz() { cin>>O.x>>O.y>>r; } class punkt { friendclass kolo; double x,y; }; class kolo { punkt O; double r; public: void wpisz(); }; voidkolo::wpisz() { cin>>O.x>>O.y>>r; }
Klasy zaprzyjaźnione • Przyjaźń jest jednostronna. Jeżeli klasa A deklaruje przyjaźń z klasą B to klasa A nie jest automatycznie traktowana przez klasę B jak przyjaciel. • Przyjaźń nie jest przechodnia. Jeżeli klasa A zadeklaruje przyjaźń z klasą B, klasa B zadeklaruje przyjaźń z klasą C to klasa C nie jest traktowana przez klasą A jak przyjaciel. 3. Przyjaźń nie jest dziedziczna. Jeżeli klasa A deklaruje przyjaźń z klasą B, a klasa C jest potomkiem klasy A to klasa C nie traktuje klasy B jako przyjaciela.
Zmienne statyczne Zmienna statyczna to zmienna wspólna dla wszystkich obiektów danej klasy. int main() { ulamekpierwszy,drugi; cout << pierwszy.mianownik << endl; cout << drugi.mianownik << endl; return 0; } #include<iostream> using namespace std; class ulamek { public: intlicznik; static intmianownik; }; intulamek::mianownik=5;
Funkcje statyczne #include<iostream> using namespace std; class punkt { double x,y; static intruch; public: punkt(); punkt(double, double, int); void wypisz(); void przesun(double, double); static void pozwolenie() {ruch=!ruch;}}; intpunkt::ruch=0; punkt::punkt(){ x=0;y=0;ruch=0;} punkt::punkt(double a, double b, int c) {x=a;y=b;ruch=c;} void punkt::wypisz() {cout<<"x = "<<x<<endl; cout<<"y = "<<y<<endl;} void punkt::przesun(double a, double b) {if (ruch) {x=x+a;y=y+b;}} int main() { punkt A(1,1,0); punkt B(2,2,0); A.przesun(1,2); A.wypisz(); punkt::pozwolenie(); //A.pozwolenie(); A.przesun(1,2); A.wypisz(); B.przesun(1,2); B.wypisz(); return 0; }
Funkcje statyczne • Do funkcji statycznej nie jest przekazywany wskaźnik this => (nie) można odwoływać się do zwykłych składników klasy. staticvoid pozwolenie(punkt &A) {ruch=!ruch;A.x=-A.x;}}; • Funkcje statyczne można wywoływać na rzecz klasy (nie konkretnego jej obiektu).
Zwykła klasa #include<iostream> usingnamespacestd; classcmpx { friendcmpx suma(cmpx,cmpx); double re,im; public: cmpx(double a=0, double b=0) :re(a),im(b){} voidwyp() {cout<<"re="<<re<<endl; cout<<"im="<<im<<endl;} }; cmpx suma(cmpxA,cmpx B) {cmpx C(A.re+B.re,A.im+B.im); return C;} intmain() { cmpx A(1,2),B(3,4),C; C=suma(A,B); C.wyp(); return 0; }
Przeładowanie operatorów #include<iostream> usingnamespacestd; classcmpx { friendcmpxoperator+(cmpx,cmpx); double re,im; public: cmpx(double a=0, double b=0) :re(a),im(b){} voidwyp() {cout<<"re="<<re<<endl; cout<<"im="<<im<<endl;} }; cmpxoperator+(cmpxA,cmpx B) {cmpx C(A.re+B.re,A.im+B.im); return C;} intmain() { cmpx A(1,2),B(3,4),C,D; C=A+B; C.wyp(); D=operator+(A,B); D.wyp(); return 0; }
Definicja operatora typ_zwracany operator@(argumenty) typ_zwracany – dowolny (z wyjątkami!!!) @ - przeładowywany operator (np. + * -) argumenty – przynajmniej jeden z argumentów musi być obiektem danej klasy lub referencją do obiektu danej klasy (nie może być wskaźnikiem)
Definicja operatora Funkcja operatorowa może być: • funkcją globalną (nie dla każdego operatora), • nie-statyczną funkcją składową klasy (dla niektórych operatorów musi być statyczna). Globalną funkcję operatorową obowiązują prawa dostępu tak jak każdą inną funkcję!
Operatory, które nie można / można przeładować • Operatory, które można przeładować: • Operatory, których nie można przeładować: • Operatory & * + - można przeładowywać w • wersji jedno- i dwuargumentowej
Uwagi dotyczące przeładowywania • Nie można tworzyć własnych operatorów. • Nie można przeładowywać operatorów dla typów wbudowanych. • Przeładowany operator może robić cokolwiek. • Może zwracać dowolny typ. Wyjątkiem są operatory: newnew[] deletedelete[] -> • Nie można zmieniać priorytetów operatorów. • Nie można zmieniać liczby argumentów, na których operatorów wykonuje działanie.
Uwagi dotyczące przeładowywania • Nie można zmieniać łączności operatorów. • Liczba argumentów przekazywanych do funkcji operatorowej jest zawsze równa: • liczbie argumentów na których pracuje operator – w przypadku gdy funkcja jest globalna, • o jeden mniej niż liczba argumentów na których pracuje operator – w przypadku gdy funkcja jest składową klasy. • WYJĄTEK OD POWYŻSZYCH: ()
Uwagi dotyczące przeładowywania • Co najmniej jeden z argumentów musi być obiektem (lub referencją do obiektu) klasy zdefiniowanej przez użytkownika – obojętnie który (nie zawsze obojętnie). • Argumenty funkcji operatorowej nie mogą być domniemane (WYJĄTEK: () ). • Operatory =&(wersja jednoargumentowa) ,newnew[] deletedelete[]są zawsze generowane automatycznie (operator = nie zawsze).
Funkcja składowa vs funkcja globalna • Jeżeli operator ma dopuszczać by po jego lewej stronie stał typ wbudowany to nie może być funkcją składową. • Dla typu enum funkcja operatorowa musi być funkcją globalną. • Przy użyciu operatora jako funkcji składowej w stosunku do pierwszego argumentu nie może zajść żadnej konwersji. • Operatory = [] () ->muszą być funkcjami składowymi klasy.
Operator przypisania Operator przypisania nie jest generowany: • Jeżeli klasa ma składnik const, • Jeżeli klasa ma składnik będący referencją. • Jeżeli w skład klasy wchodzi inna klasa, która ma operator przypisania zdefiniowany jako private. • Jeżeli klasa ma klasę podstawową, w której operator przypisania zdefiniowany jako private. Operatora przypisania się nie dziedziczy.
Operator przypisania Automatycznie wygenerowany operator przypisania wykonuje podstawienie składnik po składniku -> mogą wystąpić podobne problemy jak w przypadku automatycznie wygenerowanego konstruktora kopiującego!!!
Co powinien robić operator przypisania? • Sprawdzić czy nie kopiuje do samego siebie (A=A). • Zlikwidować dotychczasowe wnętrze. • Zbudować obiekt jeszcze raz na podobieństwo obiektu wzorcowego. • Zwracać skopiowany albo wzorcowy obiekt (są takie same).
Program z przeładowanymi operatorami << >> oraz = I/V #include<iostream> usingnamespacestd; class wektor { friend ostream &operator<<(ostream &ekran, constwektor &A); friend istream &operator>>(istream &klawiatura, wektor &A); int n; double *W; public: wektor(int a=1); wektor(int a, double *A); ~wektor(); wektor &operator=(const wektor &A); };
Program z przeładowanymi operatorami << >> oraz = II/V wektor::wektor(int a):n(a) { W=new double[n]; for (int i=0;i<n;i++) W[i]=0; } wektor::wektor(int a, double *A):n(a) { W=new double[n]; for (int i=0;i<n;i++) W[i]=A[i]; } wektor::~wektor() { delete W; }
Program z przeładowanymi operatorami << >> oraz = III/V wektor &wektor::operator=(const wektor &A) { if (&A==this) return *this; delete W; n=A.n; W=new double[n]; for (int i=0;i<n;i++) W[i]=A.W[i]; return *this; }
Program z przeładowanymi operatorami << >> oraz = V/V intmain() { double tab[]={1,2}; wektor X(2,tab),Y(3); cout<<"Wektor X:\n"<<X<<endl; cout<<"Podaj wektor Y\n"; cin>>Y; cout<<"Wektor Y:\n"<<Y<<endl; Y=X; cout<<"Wektor Y:\n"<<Y<<endl; return 0; }
Operator << Co to jest cout? – obiekt klasy ostream! Pytanie: Czy funkcja operator<< powinna być: • funkcją globalną • funkcją składową klasy (KTÓREJ)?
Operator << ODPOWIEDŹ: funkcją globalną, ponieważ: • nie może być funkcją składową klasy ostream– oczywiste • może być funkcją składową klasy wektor, ale wówczas trzeba stosować zapis: X << cout; który jest równoznaczny z zapisem: X.operator<<(cout);
Program z przeładowanymi operatorami << >> oraz = IV/V ostream &operator<<(ostream &ekran, const wektor &A) { for (int i=0;i<A.n;i++) ekran<<A.W[i]<<endl; return ekran; } istream &operator>>(istream &klawiatura, wektor &A) { for (int i=0;i<A.n;i++) klawiatura>>A.W[i]; return klawiatura; }
Dlaczego operatory << >> oraz = zwracają to co zwracają? Aby możliwe było wywołanie kaskadowe: a = b = c; cout << a << endl; cin >> a >> b;
Operator [] #include<iostream> usingnamespacestd; class wektor { … double &operator[](int a); ZWRACA REFERENCJĘ };
Operator [] double &wektor::operator[](int a) { return W[a]; } intmain() { … double tab[]={1,2,3}; wektor X(3,tab); cout<<"Wektor X:\n"<<X<<endl; cout<<X[1]<<endl; return 0; }
Dlaczego ta referencja jest taka ważna? Załóżmy, że funkcja zwraca wartość double wektor::operator[](int a); Wówczas zapis: X[1]=5; wywoła błąd (2=5 – kompilator zaprotestuje)
Operator () • Jako jedyny może przyjmować dowolną liczbę argumentów -> może być przeładowany. • Argumentu mogą być domniemane. • Jeżeli chcemy go umieścić po prawej stronie operatora przypisania to musi zwracać referencję (jak w przypadku operatora []). • Można go zastosować do indeksacji tablic wielowymiarowych.
Operator -> • Operator -> jest jednoargumentowy, pomimo że po jego obu stronach musi coś stać. • Działa na argumencie stojącym po jego lewej stronie. • Nie interesuje go co stoi po jego lewej stronie.
Operator -> Wywołanie: obiekt->skladnik; jest równoważne: (obiekt.operator->())->skladnik; Operator powinien zwracać: • wskaźnik, • obiekt (lub referencję) takiej klasy, która ma również przeładowany operator ->
Program z przeładowanymi operatorami newnew[] deletedelete[] I/III #include<iostream> usingnamespacestd; class wektor { … static void *operator new(size_trozmiar); static void *operator new[](size_trozmiar); staticvoid operator delete(void *wsk); staticvoid operator delete[](void *wsk); };
Program z przeładowanymi operatorami newnew[] deletedelete[] II/III void *wektor::operator new(size_trozmiar) { return new char[rozmiar]; } void *wektor::operator new[](size_trozmiar) { return new char[rozmiar]; } voidwektor::operatordelete(void *wsk) { deletewsk; return; } voidwektor::operatordelete[](void *wsk) { delete[] wsk; return; }
Program z przeładowanymi operatorami newnew[] deletedelete[] III/III intmain() { … double tab[]={1,2,3,4,5}; int n=3; wektor *X; X=new wektor(5,tab); cout<<*X<<endl; delete X; X=new wektor[n]; for (int i=0; i<n; i++) cin >> X[i]; for (int i=0; i<n; i++) cout << X[i]; delete[] X; return 0; }
Operatory ++A --A A++ A-- Operatory ++A oraz A++ działają na jednym argumencie i maja takie same nazwy. Jak je przeładować? Operator ++A jest definiowany jako jednoargumentowy. Operator A++ jest definiowany jako dwuargumentowy. Analogiczna sytuacja jest z operatorami --A oraz A--.
Program z przeładowanymi operatorami ++A oraz A++ #include<iostream> usingnamespacestd; class wektor { … wektor &operator++(); wektor operator++(int); }; wektor &wektor::operator++() { for (int i=0; i<n; i++) W[i]++; return *this; } wektor wektor::operator++(int) { wektor wek(*this); for (int i=0; i<n; i++) W[i]++; return wek; }
Program z przeładowanymi operatorami ++A oraz A++ intmain() { … double tab[]={1,2}; wektor X(2,tab),Y,Z; cout<<"\nWektor X:\n"; cout<<X<<endl; Y=X++; cout<<"\nWektor X:\n"; cout<<X<<endl; cout<<"\nWektor Y:\n"; cout<<Y<<endl; Z=++X; cout<<"\nWektor X:\n"; cout<<X<<endl; cout<<"\nWektor Z:\n"; cout<<Z<<endl; return 0; }
Cała klasa wektor class wektor { friend ostream &operator<<(ostream &ekran, const wektor &A); friend istream &operator>>(istream &klawiatura, wektor &A); int n; double *W; public: wektor(int a=1); wektor(int a, double *A); ~wektor(); wektor &operator=(const wektor &A); double &operator[](int a); static void *operator new(size_trozmiar); static void *operator new[](size_trozmiar); staticvoid operator delete(void *wsk); staticvoid operator delete[](void *wsk); wektor &operator++(); wektor operator++(int); };
Brakuje konstruktora kopiującego #include<iostream> usingnamespacestd; class wektor { … wektor(const wektor &A); … }; wektor::wektor(const wektor &A):n(A.n) { W=new double[n]; for (int i=0; i<n; i++) W[i]=A.W[i]; }
Prezentacja udostępniona na licencji CreativeCommons: Uznanie autorstwa, Na tych samych warunkach 3.0. Pewne prawa zastrzeżone na rzecz autorów. Zezwala się na dowolne wykorzystywanie treści pod warunkiem wskazania autorów jako właścicieli praw do prezentacji oraz zachowania niniejszej informacji licencyjnej tak długo, jak tylko na utwory zależne będzie udzielana taka sama licencja. Tekst licencji dostępny jest na stronie: http://creativecommons.org/licenses/by-sa/3.0/deed.pl