Inżynieria 12 18, o


  • Wybór właściwych klas w modelu jest w znacznym stopniu subiektywny.

0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic

0x08 graphic
0x08 graphic

MODEL 1 - każde dana jest klasą

( klasy są jednopolowe )

MODEL 2 - model opcjonalny

MODEL 3 - model z jedną tylko klasą

= model strukturalny

Konstruktory

class DATA

{ int dzień, miesiąc, rok;

public:

DATA( int, int, int);

. . . . . . . . . . . . . . . . . . .

};

DATA dzisiaj = DATA(2, 3, 2001);

DATA moje_urodziny(12, 10, 1973);

class DATA

{ int dzień, miesiąc, rok;

public:

DATA( int, int, int);

DATA( int, int); // wystarczy podać dzień i miesiąc

DATA( int); // wystarczy podać dzień

DATA( ); // konstruktor domyślny

DATA( const char * ); // data w postaci napisu do

// rozpakowania

};

DATA dzisiaj, d; DATA data(4); DATA dzien(5, 2);

DATA boże_narodzenie( ”25 grudnia 2000”);

Zasady tworzenia i używania konstruktorów:

  1. W deklaracji konstruktora nie wolno określać typu zwracanej wartości, nawet void,

  2. Wybór właściwego konstruktora odbywa się tak jak funkcji przeciążonej,

  3. Jeśli zadeklarowano jakikolwiek konstruktor jawny, to należy zadeklarować i zdefiniować konstruktor domyślny,

  4. Konstruktory nie są dziedziczone,

  5. Identyfikator konstruktora jest identyczny z nazwa klasy,

  6. Nie można pobrać adresu konstruktora,

  7. Konstruktor nie może być funkcją wirtualną.

class PUNKT

{

int x, y;

unsigned kolor;

public:

PUNKT(void): x(0), y(0), kolor(0) { }

PUNKT ( int _x, int _y, unsigned _kolor = 0);

PUNKT ( unsigned _kolor);

. . . . . . . . . . . . . . . . . . . . . . .

void UstawWsp( int _x, int _y);

void UstawKolor( unsigned _kolor);

void Rysuj( void);

};

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

PUNKT:: PUNKT( int _x, int _y, unsigned _kolor = 0):

x( _x ), y( _y ), kolor( _kolor ) { }

PUNKT:: PUNKT( unsigned _kolor ):

x( 0 ), y( 0 ), kolor( _kolor ) { }

void PUNKT:: UstawWsp( int _x, int _y)

{ x = _x; y = _y; }

Przykłady poprawnych definicji obiektów:

PUNKT p1, p2( 1, 1 ), p3( 10, 10, 5 ), p4( 1 );

PUNKT tab[ 10 ], * ptr;

Niejednoznaczności

class X

{

public:

X( void );

X( int j = 1);

};

Deklaracja X obj; jest dla kompilatora niejednoznaczna.

Zasady używania list inicjacyjnych:

  1. Obiekty będące składowymi innych klas oraz klasy bazowe mogą inicjowane tylko w liście inicjacyjnej

  2. Składowe stałe i referencyjne mogą być inicjowane tylko w liście inicjacyjnej.

Uwaga: Jeżeli obiekt nie ma jawnego konstruktora, może być inicjowany wartościami innego obiektu tej samej klasy, np. DATA d = dzisiaj;

Konstrukcja „podobiektów” (obiektów składowych klasy)

class PUNKT_2D

{

int x, y;

public:

PUNKT_2D(void): x(0), y(0) { }

PUNKT_2D( int _x, int _y ): x( _x ), y( _y ) { }

int GetX( void ) {return x;}

int GetY( void ) {return y;}

};

class ODCINEK

{

PUNKT_2D A, B;

public:

ODCINEK(void) { }; // konstruktor 1

ODCINEK( int ax, int ay, int bx, int by):

A( ax, bx),

B( bx, by)

{ } // konstruktor 2

ODCINEK( PUNKT_2D &A, PUNKT_2D &B):

A( A ),

B( B )

{ } // konstruktor 3

};

Przykłady poprawnych definicji obiektów:

PUNKT_2D A, B( 10, 10 );

ODCINEK O1( 10, 20, 110, 120 ), O2, O3( A, B);

Komentarz:

Obiekt O2 zostanie skonstruowany przy pomocy konstruktora bezparametrowego konstruktor 1. Ponieważ lista inicjacyjna tego konstruktora jest pusta kompilator przyjmie przez domniemanie, że ma on postać

ODCINEK(void): A( ), B( ) { } // konstruktor 1

a co za tym idzie - do inicjacji obiektów składowych A i B zostanie użyty konstruktor domyślny klasy PUNKT_2D.

Konstruktory kopiujące

Ich zadaniem jest tworzenie obiektów z innych obiektów tej samej klasy poprzez kopiowanie tych obiektów łącznie ze strukturami związanymi z obiektem.

class X

{ . . . . . . . .

public:

X( const X& ); // konstruktor kopiujący

};

Przykład:

#include <string.h>

class STRING

{

char *str;

public:

STRING( void ): str( new char[10] ) { }

// konstruktor domyślny

STRING(char *s ): str( s ) { }

STRING( const STRING &s ): str( strdup( s.str) ) { }

~ STRING( void ) { delete [ ] str; } // destruktor

};

Przykłady:

STRING Imie( “Jan” ); // konstrukcja obiektu stałego

STRING Imie1( Imie ); // 1

// jawne użycie konstruktora kopiującego

STRING Imie1 = Imie; // 2

// niejawnie użycie konstruktora kopiującego

Gdyby nie zdefiniowano konstruktora kopiującego:

Uwaga: Znak = nie jest operatorem przypisania (sic !)

Destruktory

Destruktor jest funkcją bezparametrową.

Destruktor, oprócz usunięcia obiektu, powinien „posprzątać po sobie”, np. usunąć z ekranu okienko obsługujące obiekt, itp.

Przykład niejawnego użycia destruktora delete Imie;

Jeśli nie zdefiniowano destruktora jawnego kompilator wygeneruje destruktor domyślny (zwykle nie sprząta po sobie).

Jawne wywołanie destruktora: STRING:: ~STRING( );

Usuwanie obiektów automatycznych:

Przykład:

void f( char *s)

{ STRING obS( s );

. . . . . . . . . . . . . . .

}

Zakończenie wykonywania się funkcji spowoduje wywołanie destruktora (gdyby nie był zdefiniowany - domyślnego) i usunięcie obiektu automatycznego obS.

Tworzenie i usuwanie obiektów dynamicznych:

Przykład:

STRING *ptrS = new STRING( „Andrzej” );

delete ptrS; // wywołany zostanie destruktor

Przykład - obsługa stosu statycznego

(bez obsługi błędów)

0x08 graphic
rozmiar

0x08 graphic
0x08 graphic
class STOS

{

0x08 graphic
int rozmiar;

0x08 graphic
0x08 graphic
0x08 graphic
char * stos; // wskaźnik stosu

char * top; // wskaźnik wierzchołka pusty stos s1

public:

STOS( int r ) { top = stos = new char[ rozmiar = r ]; }

~ STOS( ) { delete [ ]stos; }

void push( char c ) { *top++ = c; }

char pop( ) { return *− −top; }

};

Przykłady użycia tak zdefiniowanej klasy:

STOS s1( 100 ); STOS s2( 200 ); s1.push( `a' );

s2.push( s1.pop( ) ); char c = s2.pop( );

cout<< ch << `\n';

Dziedziczenie proste

Umożliwia uszczegóławianie już zdefiniowanych klas bez potrzeby ponownej kompilacji klas bazowych

0x08 graphic

rozbudowywalność

Przykład:

class PRACOWNIK

{

char * Nazwisko, * Dział;

int Uposażenie;

. . . . . . . . . . . . . . . . . . . . .

};

class KIEROWNIK: public PRACOWNIK

{

int Uprawnienia[ 8 ];

. . . . . . . . . . . . . . . . . . . . .

};

Przykłady poprawnych definicji obiektów i wskazań do obiektów w warunkach dziedziczenia prostego:

KIEROWNIK kk;

PRACOWNIK *p = & kk;

// każdy kierownik jest jednocześnie pracownikiem,

PRACOWNIK pp;

0x08 graphic
0x08 graphic
KIEROWNIK *k = &pp;

// . . . ale nie każdy pracownik kierownikiem

  • Obiekty klasy pochodnej będą traktowane jak obiekty klasy bazowej, jeśli sięgnie się do nich za pomocą wskaźnika. Odwrotnie - NIE !

Dziedziczenie i polimorfizm

W poniższym przykładzie zaprezentowane zostanie użycie: klas abstrakcyjnych, funkcji wirtualnych i czysto wirtualnych (abstrakcyjnych) oraz omówiony mechanizm polimorfizmu.

Problem:

Zdefiniować klasę, mogącą przechowywać wskaźniki do obiektów różnych typów i zapewniającą ich wizualizacje za pomocą jednej funkcji Out( ).

0x08 graphic

class ELEMENT

{

public:

void Out( );

};

0x08 graphic

0x08 graphic

0x08 graphic
0x08 graphic
rozmiar = 5

0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic

0x08 graphic
0x08 graphic
0x08 graphic

0x08 graphic
0x08 graphic
0x08 graphic

0x08 graphic
0x08 graphic
0x08 graphic

0x08 graphic
0x08 graphic

typedef BOOL;

class KOSZ

{

ELEMENT **tab; // tablica wskaźników do obiektów

unsigned rozmiar, ostatni;

public:

KOSZ( unsigned rozm = 10 );

~ KOSZ( void );

BOOL DoKosza( ELEMENT * );

// weźmie do kosza wskazanie obiektu

dowolnego typu

void Out( void );

// wysyła na wyjście wszystkie obiekty znajdu-

jące się w koszu niezależnie od ich typu

};

KOSZ:: KOSZ( unsigned rozm ):

tab( new ELEMENT *[ rozm ],

rozmiar( rozm), ostatni( 0 ) { }

KOSZ:: ~KOSZ( void )

{

for(int i = 0; i < ostatni; ++i) delete tab[ i ];

delete tab;

};

BOOL KOSZ:: Do Kosza( ELEMENT *pE1)

{ if( ostatni < rozmiar )

{ tab[ ostatni ] = pE1; ++ostatni;

return 1; }

else return 0;

}

void KOSZ:: Out( void )

{

for( int i=0; i < ostatni; i++) tab[ i ] Out( );

}

  • Umieszczenie w Koszu wskaźnika obiektu klasy pochodnej od ELEMENT będzie równoważne umieszczeniu tam wskaźnika klasy ELEMENT

Teraz możemy definiować klasy zupełnie dowolnych obiektów dziedziczących z klasy ELEMENT

0x08 graphic

0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic

0x08 graphic

0x08 graphic

0x08 graphic
0x08 graphic

0x08 graphic
0x08 graphic
class STUDENT: public ELEMENT

{

char *nazwisko;

int wiek;

public:

STUDENT( char *_nazwisko, int _wiek);

~STUDENT( );

void Out( );

};

STUDENT:: STUDENT( char *_nazwisko, int _wiek):

nazwisko( strdup( _nazwisko ), wiek( _wiek) { }

STUDENT:: Out( void )

{ cout<< “\nNazwisko “<<nazwisko

<<”\nWiek “<<wiek; }

  • Funkcja składowa Out( ) klasy STUDENT ma tę samą nazwę, co funkcja składowa klasy bazowej ELEMENT, więc ją przesłania (dziedziczenie nie wystąpi),

  • Wywołanie tab[ i ] Out( ); odwołuje się do funkcji ELEMENT:: Out( ). Jeśli chcemy, aby odnosiło się do konkretnej funkcji Out( ) w klasie pochodnej musimy funkcję Out( ) w klasie bazowej uczynić funkcją wirtualną.

Przedefiniujmy więc klasę ELEMENT

0x08 graphic

class ELEMENT

{

public:

virtual ~ELEMENT( );

virtual void Out( ) = 0;

// deklaracja funkcji abstrakcyjnej (czysto wirtualnej)

};

Klasę KOSZ tez można wyprowadzić z klasy ELEMENT. Wtedy będzie można UMIESZCZAĆ W Koszu inne Kosze wraz z zawartością (sic ! ).

Oto poprawiona definicja klasy KOSZ:

class KOSZ: public ELEMENT

{

ELEMENT **tab;

unsigned rozmiar, ostatni;

public:

KOSZ( unsigned rozm = 10 );

virtual ~ KOSZ( void ); // wirtualny destruktor

BOOL DoKosza( ELEMENT * );

virtual void Out( void ); // wirtualna metoda

};

Również klasy STUDENT i LITERA, dziedziczące z klasy ELEMENT będą miały wirtualne destruktory i metody Out( ).

Funkcja testująca zdefiniowaną wyżej strukturę klas:

void main( )

{

KOSZ *k1= new KOSZ(3), *k2=new KOSZ(2);

// oba obiekty są dynamiczne

k1→DoKosza( new STUDENT(„Jan Nowak”, 20);

k1→DoKosza( new LITERA(`B');

k1→DoKosza( new STUDENT(„Anna Jopek”, 25);

k2→DoKosza( new LITERA(`Q');

k2→DoKosza(k1);

k2→Out( ); // polimorfizm

delete k2; // polimorfizm

return 0;

};

  • Wywołanie metody Out( ) dla obiektu wskazywa-nego przez k2 klasy KOSZ powoduje poprzez tab[i] Out( ) wywołanie odpowiedniej funkcji klasy pochodnej, odpowiedniej do klasy obiektu, chociaż tab[i] jest wskazaniem klasy bazowej ELEMENT *,

  • Podobnie delete k2 jest równoważne delete tab[i], co odpowiada usunięciu obiektu wskazywanego przez tab[i] klasy ELEMENT. Destruktor w klasie ELEMENT jest czysto wirtualny a jego definicje znajdują się w klasach pochodnych.

Zasady używania funkcji wirtualnych:

  1. Można tworzyć obiekty klas zawierających funkcje wirtualne, ale jeśli chociaż jedną z tych funkcji uczynimy abstrakcyjną, klasa staje się abstrakcyjną.

  2. Typ funkcji wirtualnej jest deklarowany w klasie bazowej i nie może ulec zmianie w klasach pochodnych.

  3. Funkcja wirtualna musi być zdefiniowana w klasie, w której po raz pierwszy została zadeklarowana.

  4. Klasy pochodne nie muszą korzystać z funkcji wirtualnych klas bazowych.

  5. Jeśli wywołuje się funkcję wirtualną z kwalifikatorem zakresu, np. KOSZ:: Out( ), mechanizm wirtualny nie działa.

Dziedziczenie wielobazowe

Postać ogólna nagłówka definicji klasy pochodnej

class oznacznik : lista_klas_bazowych

Element listy klas bazowych ma postać:

< public | protected | private > ozn_klasy_bazowej

Dostępność klas bazowych w klasach pochodnych:

Rodzaj dziedziczenia

Składowe klasy bazowej ...

w klasach pochodnych są ...

public

private

niedostępne

protected

protected

public

public

protected

private

niedostępne

protected

protected

public

protected

private

private

niedostępne

protected

prywatnymi klasy pochodnej

public

prywatnymi klasy pochodnej

29

MODEL 1

MODEL 2

MODEL 3

top

stos

Ta klasa mogłaby być klasą abstrak-cyjną dla takiej klasy

Zdefiniujmy wobec tego klasę podstawową

KOSZ zawierającą wszystko co niezbędne

tab

student

litera

ostatni

ELEMENT

void Out( void );

LITERA

char ch;

void Out( void );

STUDENT

char * nazwisko;

int wiek;

void Out( void );

Klasa abstrakcyjna - nie można tworzyć obiektów



Wyszukiwarka

Podobne podstrony:
DGP 2014 12 18 ubezpieczenia i swiadczenia
12 18 88
PATOMORFOLOGIA wykład 12, PATOMORFOLOGIA wykład 12 (18 XII 00)
18.12, 18
12 (18)
12 18 IIIid 13376 ppt
Fizyka K2 5,12,18 id 176846
wykład 12- 18.01.2010
grafika inzynierska 12 11 2009 c2
2002 12 18
Matma zdania 2010 12 18
2001 12 18
labirynt 12 7 18
KPC - Wykład (12), 18.12.2012
12 z 18, materiały do egzaminu
2003 12 18
labirynt 7 12 18

więcej podobnych podstron