Wykład 8 Polimorfizm 1.Funkcje polimorficzne 2.Czyste funkcje wirtualne i klasy abstrakcyjne PO8-1 / 38
Funkcje polimorficzne Funkcja polimorficzna, a funkcja zwykła Przykłady funkcji polimorficznych PO8-2 / 38
Funkcje polimorficzne Funkcje polimorficzne – wielopostaciowe to zadeklarowane ze specyfikatorem virtual oraz wszystkie funkcje tego samego typu zawarte w dowolnej klasie pochodnej w ciągu klas pochodnych. Funkcją polimorficzną mogą być destruktory pomimo różnych nazw w różnych klasach. Wywołanie funkcji polimorficznej do referencji lub wskaźnika na obiekt dokonuje się z klasy tego obiektu, nie zaś z klasy zmiennej referencyjnej ani klasy zmiennej wskaźnikowej. W konstruktorach i destruktorach funkcje polimorficzne zachowują się tak jak zwykłe funkcje. PO8-3 / 38
Przykład 1 cz. 1 class BAZOWA { public: fun1(...); // kropki symbolizują parametry funkcji virtual fun2(...); // funkcja polimorficzna virtual ~ BAZOWA( ); // polimorficzny destruktor //... }; class POCHODNA: public BAZOWA { public: fun1(...); fun2(...); // funkcja polimorficzna //... }; PO8-4 / 38
Przykład 1 cz. 2 Funkcja(BAZOWA &v) { //... v.fun1(...); // funkcja fun1 z klasy Bazowa v.fun2(...); // funkcja fun2 z klasy obiektu //...// tożsamego ze zmienną v } POCHODNA S; BAZOWA *p=&S, T; p->fun1(...); // funkcja fun1 z klasy Bazowa p->fun2(...); // funkcja fun2 z klasy Pochodna Funkcja(T); // v. fun2 - funkcja z klasy Bazowa Funkcja(S); // v. fun2 - funkcja z klasy Pochodna PO8-5 / 38
Przykład 2 cz. 1 class Punkt {//... public: virtual double Pole( ) const {return 0;} //... }; class Kolo: public Punkt {//... public: double Pole( ) const{return *R*R;} //... }; class Walec: public Kolo {//... public: double Pole( ) const{return *R*(R+h);} //... }; PO8-6 / 38
Przykład 2 cz. 2 void DrukujPole( ostream &wy, const Punkt &v ) { wy << "Pole= " << v.Pole( ) << endl; } void main( ) { Punkt P; Kolo K; Walec W; //... DrukujPole(cout, W);// wypisze pole walca DrukujPole(cout, K);// wypisze pole koła DrukujPole(cout, P);// wypisze pole = 0 }; PO8-7 / 38
Destruktor klasy Vec class Vec { //... ~Vec( ); //... }; Vec *p=new Mac("B"); //... delete p;// Przed usunięciem obiektu klasy Mac jest // aktywowany tylko destruktor klasy Vec //... } Aby aktywować destruktor z klasy obiektu wskazywanego a nie destruktor z klasy zmiennej p, destruktory klas Vec i Mac muszą być polimorficzne. PO8-8 / 38
Destruktor wirtualny class Vec { //... virtual ~Vec( ); //... }; Vec *p=new Mac("B"); //... delete p; //... } Przed usunięciem obiektu klasy Mac jest aktywowany destruktor klasy Mac. Usunięcie podobiektu bazowego jest poprzedzone wywołaniem destruktora z klasy Vec. PO8-9 / 38
Wprowadzanie tablic A N= 3 A[1]= 2.75 A[2]= 2.15 A[3]= 4.5 B W= 2 B K= 3 B[1,1]= 4.75 B[1,2]= 5.25 B[1,3]= 1.75 B[2,1]= 7. 5 B[2,2]= 2.1 B[2,3]= 5.0 void main( ) { Vec Z ("A"); Mac B ("B"); cin >> Z >> B; //... }; Przykład wprowadzania Wygląd ekranu (przykład) PO8-10 / 38
Operator >>, cz. 1 class Vec { protected: virtual double *Alokacja( istream&, int ); virtual void Napis1(int i) const { cerr<<naz<<'['<<(i+1)<<"]= "; } //... friend istream &operator>>( istream&, Vec&); }; class Mac: public Vec { double *Alokacja( istream&, int ); void Napis1(int i) const { cerr<<naz<<'['<<( i/K+1)<<", "<<( i%K+1)<<"]= "; } //... }; PO8-11 / 38
Operator >>, cz. 2 double *Vec::Alokacja( istream &we, int k ) { if( k ) cerr > n; return new(nothrow) double[n]; } double *Mac::Alokacja( istream &we, int k ) { if( k ) cerr > W; if( k ) cerr > K; n=W*K; double *B= new(nothrow) double[n]; if( B==0 ) W=K=0 ; return B; } PO8-12 / 38
Operator >>, cz. 3 istream &operator>>( istream &we, Vec &v) { int k = we = = cin; delete [ ] v.A; v.A = v.Alokacja(we, k); if(v.A==0) v.n=0; if( int i=0; i<v.n; i++) {if( k ) v.Napis1( i ); we >> v.A[i]; } return we; } PO8-13 / 38
Wyprowadzanie tablic Z = [ 2.75, 2.15, 4.5 ] B[1]= [ 4.75, 5.25, 1.75 ] B[2]= [ 7.50, 2.10, 5.00 ] void main( ) { Vec Z ("Z"); Mac B ("B"); cin >> Z >> B; //... Vec::Precision(2); cout << Z << endl; cout << B << endl; //... }; Przykład wprowadzania Wygląd ekranu (przykład) PO8-14 / 38
Operator <<, cz. 1 class Vec { protected: virtual void Napis2( ostream &wy, int i) const { if( i ) wy << ", "; else wy << naz << "=[ " } //... friend ostream &operator<<( ostream&, const Vec&); }; class Mac: public Vec { void Napis2( ostream &wy, int i ) const {if( i= =0 ) wy << '\n' << naz << "[1]=[ "; else if( i%K ) wy << ", "; else wy << " ]\n" << naz << '[' << ( i / K+1 ) << "]=[ "; } //... }; PO8-15 / 38
Operator <<, cz. 2 ostream &operator<<( ostream &wy, const Vec &v) { int k=wy.precision( v.Pr ); v.Napis2( wy, 0 ); if( v.A ) wy << v.A[0]; if( int i=1; i<v.n; i++) { v.Napis2( wy, i ); wy << setw( 5+v.Pr ) << v.A[i]; } return wy << " ]" << endl << setprecision( k ); } PO8-16 / 38
Klasy abstrakcyjne Czysta funkcja polimorficzna Klasa abstrakcyjna Przykład klasy abstrakcyjnej Pilka Przykład klasy abstrakcyjnej Zbior PO8-17 / 38
Czyste funkcje wirtualne class Abstrakcyjna { //... virtual typ Funkcja(... ) = 0; //... }; Czysta funkcja wirtualna ma zdefiniowany swój interfejs, nie ma natomiast zdefiniowanego algorytmu. Klasa zawierająca choćby jedną funkcję czysto wirtualną jest klasą abstrakcyjną. Czysta funkcja wirtualna niezdefiniowana w klasie pochodnej pozostaje czystą funkcją wirtualną, a klasa pochodna jest też klasą abstrakcyjną. PO8-18 / 38
Klasy abstrakcyjne Nie wolno tworzyć obiektów klasy abstrakcyjnej. Klasy abstrakcyjnej można użyć tylko jako interfejsu i jako klasy bazowej dla innych klas. Klasa abstrakcyjna, która ma służyć tylko jako interfejs nie powinna zawierać żadnych danych. Klasy abstrakcyjna będąca interfejsem nie wymaga definiowania konstruktora. Klasa z funkcją wirtualną (w tym klasa abstrakcyjna oraz interfejs) powinna mieć destruktor wirtualny. PO8-19 / 38
Klasa Pilka class Pilka {// klasa abstrakcyjna protected: int x, y, dx, dy, r; // położenie, wektor prędkości, promień // czyste funkcje wirtualne virtual void Wstaw( ) const =0; // pokazanie piłki virtual void Ukryj( ) const =0; // ukrycie piłki virtual int MaxX( ) const =0; // rozmiar X obszaru virtual int MaxY( ) const =0; // rozmiar Y obszaru // funkcja wirtualna virtual void Odbij( ); // ruch lub odbicie piłki public:Pilka( int =1, int =1, int =1, int =1, int =1 ); Pilka &operator ++( ); virtual ~Pilka( ) { } friend ostream &operator <<(ostream&, const Pilka&); friend istream &operator >>(istream&, Pilka&); }; PO8-20 / 38
Funkcje klasy Pilka Pilka::Pilka( int x, int y, int dx, int dy, int r ): x( x ), y( y ), dx(dx ), dy(dy ), r( r ) { } void Pilka::Odbij( ) // ruch lub odbicie piłki { if( x < r ) { x = r + r x; dx = dx; } else if( x > MaxX( ) r ) { x = 2*( MaxX( ) r ) x; dx = dx; } if( y < r ) { y = r + r y; dy = dy; } else if( y > MaxY( ) r ) { y = 2*( MaxY( ) r ) y; dy = dy; } } PO8-21 / 38 r x r-x y r+r-x
Funkcje klasy Pilka Pilka &Pilka::operator ++( ) { Ukryj( ); x += dx; y += dy; Odbij( ); Wstaw( ); return *this; } PO8-22 / 38
Zbiory- teoria 1 Przestrzeń elementów (zbiór pełny) Zbiory rozłączne A B Dopełnienie zbioru A A AA PO8-23 / 38
Zbiory- teoria 2 Iloczyn zbiorów A*BSuma zbiorów A+B A B A B Różnica symetryczna A^B Różnica zbiorów A B ABAB A B A B PO8-24 / 38
Reprezentacja zbiorów Zbiór A Zbiór B A=Zbiór 5 elementów o numerach: 1, 2, 5, 7, 8 B=Zbiór 4 elementów o numerach: 0, 2, 7, 9 A+B A+B=Zbiór 7 elementów o numerach: 0, 1, 2, 5, 7, 8, 9 A*B A*B=Zbiór 2 elementów o numerach: 2, 7 Element nr 2 należy do zbioru Element nr 9 nie należy do zbioru PO8-25 / 38
Reprezentacja arytmetyki Niech przynależność kolejnych elementów do zbiorów A i B reprezentują kolejne bity zmiennych Za i Zb typu unsigned long. Kolejnym operacjom na zbiorach A i B odpowiadają następujące operacje bitowe na zmiennych Za i Zb: Operacje na zbiorachOperacje bitowe –A–A~Za A+BZa | Zb A*BZa & Zb A^BZa ^ Zb A–BA–BZa & ~Zb PO8-26 / 38
Klasa Zbior class Zbior {// klasa abstrakcyjna public:Zbior &operator ~( ); Zbior &operator ^=(const Zbior&); Zbior &operator – =(const Zbior&); Zbior &operator *=(const Zbior&); Zbior &operator +=(const Zbior&); Zbior &operator =(const Zbior&); int Pelny( ); int Pusty( ); int Moc( ) const ; virtual int MaxN( ) const =0; // czysta funkcja wirtualna Zbior(int); Zbior(const Zbior&); virtual ~Zbior( ); friend ostream &operator <<(ostream&, const Zbior&); friend istream &operator >>(istream&, Zbior&); protected: unsigned long *Z; static int Rozmiar(int); virtual void PutEl(ostream&, int) const =0; virtual int GetEl(istream&) =0; // czyste funkcje wirtualne }; PO8-27 / 38
Konstruktory i destruktor Zbior::Zbior(int n) { Z=new(nothrow) unsigned long[n]; if(Z) for(int i=n–1; i>=0; – – i) Z[i]=0; } Zbior::Zbior(const Zbior &v) // konstruktor kopiujący { int n=v.Rozmiar( v.MaxN( ) ); Z=new(nothrow) unsigned long[n]; if(Z==0) return; if(Z) for(int i=n–1; i>=0; – – i) Z[i]=v.Z[i]; } Zbior::~Zbior( ) { delete [ ]Z; Z=0; } PO8-28 / 38
Metody klasy Zbior int Zbior::Moc( ) const// liczba elementów w zbiorze {unsigned long el; if(Z==0) return 0; int i, m=0, n=Rozmiar( MaxN( ) ); for( i=n – 1; i >= 0; – – i) for(el=Z[ i ]; el; el>>=1) if(el & 1) ++m; return m; } int Zbior::Rozmiar(int MaxEl ) // rozmiar tablicy bitowej { return ( MaxEl – 1 )/( sizeof(long) << 3 )+1; } int Zbior::Pelny( )// 1- zbiór pełny, 0-zbiór niepełny { return Moc( )==MaxN( ); } int Zbior::Pusty( ) // 1-zbiór pusty, 0-zbiór niepusty { if(Z==0) return 1; int i, n=Rozmiar( MaxN( ) ); for( i=n – 1; i >= 0; – – i) if( Z[ i ]!=0 ) return 0; return 1; } PO8-29 / 38
Operatory = i >> Zbior& Zbior::operator =(const Zbior &v) {// operator przypisania if(&v==this) return *this; if((Z==0) || (v. Z==0)) return *this; int i, n=Rozmiar( MaxN( ) ); for( i=n – 1; i>=0; – – i) Z[ i ]=v.Z[ i ]; return *this; } istream &operator >>(istream &we, Zbior &v) {// wprowadzanie zawartości zbioru if(v.Z==0) return we; int i, k, n=v. Rozmiar( v. MaxN( ) ); unsigned m=sizeof( long ) << 3; for( i=n – 1; i>=0; – – i) ) v. Z[ i ]=0; while( ( k=v. GetEl(we) )>=0 ) v. Z[ k/m ] |= 1<<( k%m ); return we; } PO8-30 / 38
Operator << ostream &operator <<(ostream &wy, const Zbior &v) {// wyprowadzanie zawartości zbioru wy << "{ "; int i, j, k, n=v. Rozmiar( v. MaxN( ) ), m=v. Moc( ); unsigned long el; for(i=0, k=0; i<n; ++i, k+=sizeof(long)<<3) { if( m==0 ) break; for( j=k, el=v.Z[ i ]; el; el>>=1, ++j) if(el & 1) { v. PutEl( wy, j ); – – m; if(m) wy << ", "; } return wy << " }"; } PO8-31 / 38
Operatory += i *= Zbior &Zbior::operator+=(const Zbior &v) {// suma zbiorów if(&v==this) return *this; if( (Z==0) || (v. Z==0) ) return *this; int i, n=Rozmiar( MaxN( ) ); for( i=n – 1; i>=0; – – i ) Z[ i ] |= v. Z[ i ]; return *this; } Zbior &Zbior::operator*=(const Zbior &v) {// przekrój zbiorów if(&v==this) return *this; if( (Z==0) || (v. Z==0) ) return *this; int i, n=Rozmiar( MaxN( ) ); for( i=n – 1; i>=0; – – i ) Z[ i ] &= v. Z[ i ]; return *this; } PO8-32 / 38
Operatory – = i ^= Zbior &Zbior::operator – =(const Zbior &v) {// różnica zbiorów if( (Z==0) || (v.Z==0) ) return *this; int i, n=Rozmiar( MaxN( ) ); for( i=n – 1; i>=0; – – i ) Z[ i ] &= ~v. Z[ i ]; return *this; } Zbior &Zbior::operator^=(const Zbior &v) {// różnica symetryczna zbiorów if( (Z==0) || (v.Z==0) ) return *this; int i, n=Rozmiar( MaxN( ) ); for( i=n – 1; i>=0; – – i ) Z[ i ] ^=v. Z[ i ]; return *this; } PO8-33 / 38
Operator ~ Zbior &Zbior::operator ~( ) {// dopełnienie zbioru if( Z==0 ) return *this; int i, n=Rozmiar( MaxN( ) ); for( i=n – 1; i>=0; – – i ) Z[ i ] = ~Z[ i ]; i=MaxN( )%(sizeof(*Z) << 3); if( i ) Z[n – 1] &= ~( ~ 0ul << i ); return *this; } PO8-34 / 38
Klasa ZbioryL class ZbioryL : public Zbior {// klasa zbiorów liczb całkowitych public: int MaxN( ) const; ZbioryL( ); virtual ~ZbioryL( ); ZbioryL operator + (ZbioryL w) const; ZbioryL operator *(ZbioryL w) const; ZbioryL operator – (ZbioryL w) const; ZbioryL operator ^ (ZbioryL w) const; ZbioryL operator – ( ) const; protected: static int Min; // element minimalny virtual void PutEl(ostream&, int) const; // wyprowadzenie elementu virtual int GetEl(istream&); // wczytanie elementu static int MaxEl;// liczba wszystkich elementów }; // Elementami zbiorów są tu liczby od Min do Min+Maxel – 1 PO8-35 / 38
Metody klasy ZbioryL ZbioryL::ZbioryL( ):Zbior( MaxEl ) // konstruktor { } int ZbioryL::MaxN( ) const// liczba wszystkich elementów { return MaxEl; } int ZbioryL::GetEl(istream& we) {// numer wprowadzanego elementu (od 0) lub –1 gdy koniec int i; cerr << "Element = "; we >> i; if( i =Min+MaxEl ) return –1; return i –Min; } void ZbioryL::PutEl(ostream& wy, int i) const { wy << ( i+Min ); }// wyprowadzenie elementu o numerze i PO8-36 / 38
Operatory klasy ZbioryL ZbioryL ZbioryL::operator + ( ZbioryL w ) const {// suma zbiorów w += *this; return w; } ZbioryL ZbioryL::operator * (ZbioryL w) const {// przekrój zbiorów w *= *this; return w; } ZbioryL ZbioryL::operator – ( ZbioryL w ) const {// różnica zbiorów ZbioryL v( *this ); v – = w; return v; } PO8-37 / 38
Operatory klasy ZbioryL ZbioryL ZbioryL::operator ^ ( ZbioryL w ) const {// różnica symetryczna zbiorów w ^= *this; return w; } ZbioryL ZbioryL::operator – ( ) const {// dopełnienie zbioru ZbioryL w( *this ); ~ w; return w; } PO8-38 / 38