Wstęp
do programowania
Wykład 10 – klasy, dziedziczenie
- Nowy typ klasy tworzony jest na bazie istniejącego typu
- Nowo definiowany typ klasy dziedziczy wszystkie pola i
metody bez konieczności ich definiowania.
- Typ, od którego dziedziczona jest struktura nazywa się
typem rodzicem (ancestor type) lub typem bazowym.
- Typ tworzony od typu bazowego to typ potomny (descendant
type) lub typ pochodny.
Dziedziczenie może być pośrednie lub bezpośrednie.
KLasy - dziedziczenie
- Dziedziczenie metod może być dwojakiego rodzaju:
1) bez przesłaniania - deklaracje metod w typie przodka i potomka
zawierają inne identyfikatory;
2) z przesłanianiem metod - deklaracja metody w typie potomnym
ma taki sam identyfikator jak metoda w typie
bazowym - wówczas metoda w typie potomnym
przesłania, redefiniuje metodę odziedziczoną
od typu przodka;
- Pola klasy nie mogą być przesłaniane - żaden typ potomny
nie może posiadać pól o identyfikatorach występujących w typie bazowym.
- Przesłonięcie metody statycznej dopuszcza zmiany nagłówka w dowolny
sposób; nowa metoda może mieć zarówno inne parametry jak i różną treść.
- Procedura nadająca wartości polom w typie pochodnym może
wykorzystywać procedurę nadającą wartości polom typu bazowego i
określać tylko dodatkowe pola typu pochodnego.
Obiekty - dziedziczenie
•Klasa potomna dziedziczy metody i pola przodka.
•Czasem musimy zmienić dziedziczoną metodę przodka, tak
aby wykonywała w całości metodę przodka ale dodatkowo
wykonywała jeszcze inne dodatkowe operacje.
•Realizuje się to przez wywołanie wewnątrz metody potomka
metody przodka poprzedzonej słowem kluczowym inherited
(tzn. dziedziczony)
•Jest to potrzebne, wygodne i zwalnia z pamiętania hierarchii
obiektów.
Dziedziczenie
program dziedicz0;
Type TRodzic = class
procedure napisz;
end;
procedure TRodzic.napisz;
begin writeln('to napisał >> Rodzic');
end;
Type TPotomek = class(TRodzic)
procedure napisz;
end;
procedure TPotomek.napisz;
begin
//Inherited napisz;
writeln('A to dopisalo zycie (Potomek)' );
end;
Var potomek :TPotomek;
begin
Potomek:= TPotomek.Create;
Potomek .napisz;
Readln;
Potomek.Free;
end.
Rodzic
Potomek
Aby program działał prawidłowo
Należy klasę potomka uzupełnić
o metodę wykonaj o identycznej
Treści jak w klasie Rodzic
• Polimorfizm (wielopostaciowość) jest mechanizmem dzięki
któremu ta sama metoda (o tej samej nazwie (i parametrach))
może mieć różne skutki działania (różne znaczenie) w
zależności od tego jaki obiekt ją wywołuje.
•Dziedziczenie homomorficzne – połączenia miedzy metodami
obiektów są wykonywane w czasie kompilacji programu.
(przykład dziedzicz_homomorf.dpr)
Dziedziczenie polimorficzne – korzystanie z tablic metod
wirtualnych (VMT) dla każdej klasy w której są adresy metod w
danej klasie. Wzajemne wywoływanie metod odbywa się za
pośrednictwem adresów zawartych w tablicach VMT a te są
różne dla klasy przodka i potomka. Jednak metoda w klasie
przodka prawidłowo rozpozna wywoływaną metodę również w
klasie potomka.
Dziedziczenie, Polimorfizm
W dziedziczeniu polimorficznym należy używać metod
wirtualnych.
Tj. W nagłówku metody przodka w należy użyć dyerktywy
virtual
np. procedure metoda; virtual;
Ale w deklaracji nagłówka metody potomka należy użyć
dyrektywy override
np. procedure metoda; override;
Przykład: dziedzicz_polimorf.dpr
Polimorfizm, metody wirtualne
program dziedzicz_homomorf;
Type TRodzic = class
procedure napisz;
procedure wykonaj; end;
procedure TRodzic.napisz;
begin
writeln('To napisał >> Rodzic');
end;
procedure TRodzic.wykonaj;
begin napisz; end;
Type TPotomek = class(TRodzic)
procedure napisz;
end;
procedure TPotomek.napisz;
begin writeln('to napisał > Potomek' );
end;
Var potomek :TPotomek;
begin
Potomek:= TPotomek.Create;
Potomek.wykonaj; Readln;
Potomek.Free; End.
program dziedzicz_polimorf;
Type TRodzic = class
procedure napisz;
virtual;
procedure wykonaj; end;
procedure TRodzic.napisz;
begin
writeln('To napisał >> Rodzic');
end;
procedure TRodzic.wykonaj;
begin napisz; end;
Type TPotomek = class(TRodzic)
procedure napisz;
override;
end;
procedure TPotomek.napisz;
begin writeln('to napisał > Potomek' );
end;
Var potomek :TPotomek;
begin
Potomek:= TPotomek.Create;
Potomek.wykonaj; Readln;
Potomek.Free; End.
W Lazarusie metody wirtualne mogą być również pokrywane
metodami statycznymi albo innymi metodami wirtualnymi.
Dlatego przy dziedziczeniu polimorficznym koniecznie trzeba
użyć dyrektywy override w klasie potomka.
Jeżeli metodę wiertualną zakrywamy metodą statyczna (brak
override) rezygnujemy z polimorfizmu, i kompilator wyświetla
ostrzeżenie, dlatego zamiast dyrektywy ovwrride należy
zastąpić dyrektywą reintroduce.
dziedziczeniu polimorficznym należy używać metod
wirtualnych.
Tj. W nagłówku metody przodka w należy użyć dyerktywy
virtual
np. procedure metoda; virtual;
Polimorfizm, metody wirtualne
program dziedzicz_reintro;
Type TRodzic = class
procedure dodaj(x:byte); virtual;
end;
procedure TRodzic.dodaj;
begin
writeln(x+10); end;
Type TPotomek = class(TRodzic)
procedure dodaj(x:byte); reintroduce;
end;
procedure TPotomek.dodaj;
begin writeln(x,' + dziesiec'); end;
Var potomek :TPotomek;
begin
Potomek:= TPotomek.Create;
Potomek.dodaj(1);
Readln;
Potomek.Free; end.
Rodzic
Potomek
program dziedzicz_wnuk;
Type TRodzic = class
procedure dodaj(x:byte); virtual;
procedure wykonaj(b:byte);
end;
procedure TRodzic.dodaj(x:byte);
begin
writeln(x+10); end;
procedure TRodzic.wykonaj(b:byte);
begin
dodaj(b); end;
Type TPotomek = class(TRodzic)
procedure dodaj(x:byte); override;
end;
procedure TPotomek.dodaj(x:byte);
begin writeln(x,' + sto'); end;
Type TWnuk = class(TPotomek)
procedure dodaj(x:byte); override;
end;
procedure TWnuk.dodaj(x:byte);
begin writeln(x,' plus 1000'); end;
Var Pot :TRodzic;
Rodzic
Potomek
Wnuk
Var Pot :TRodzic;
begin
Pot:= TRodzic.Create;
Pot.wykonaj(1);
Pot.Free;
Pot:= TPotomek.Create;
Pot.wykonaj(1);
Pot.Free;
Pot:= TWnuk.Create;
Pot.wykonaj(1);
Pot.Free; Readln; end.
Dziedziczenie polimorficzne zapewniają
- metody wirtualne deklarowane słowem virtual
- metody dynamiczne deklarowane słowem dynamic
Działają tak samo, Różnią się sposobem optymalizacji.
Metoda dynamiczna daje mniejszy rozmiar kodu wynikowego
programu po kompilacji.
Metoda wirtualne powoduje że program po kompilacji działa
szybciej niż dla metody dynamicznej.
Z polimorfizmem mamy do czynienia również w przypadku
wywołania metod z parametrem aktualnym typu potomnego
względem typu parametru formalnego.
Polimorfizm, metody wirtualne
Metody dynamiczne
program dziedzicz_poli2;
Type TRodzic = class
procedure napisz; virtual;
end;
procedure TRodzic.napisz;
begin
writeln('To napisal Rodzic');
end;
Type TPotomek = class(TRodzic)
procedure napisz; //override;
end;
procedure TPotomek.napisz;
begin writeln('To napisał Potomek' );
end;
procedure wykonaj(x:Trodzic);
begin x.napisz;
end;
Fig (Figura)
Pr
(prostokąt)
Var Rodzic:TRodzic;
potomek :TPotomek;
begin
Rodzic:= TRodzic.Create;
Potomek:= TPotomek.Create;
wykonaj(Potomek);
Readln;
Rodzic.Free;
Potomek.Free;
End.
program dziedziczenie_1;
Type TFig = class //class
x, y :Integer;
nazwa: string[20];
constructor Create;
destructor Destroy;
procedure narysuj;
procedure zmaz;
end;
constructor TFig.Create;
begin
inherited Create;
x := 1; y := 2;
end;
destructor TFig.Destroy;
begin zmaz;
inherited Destroy;
end;
Fig (Figura)
Pr
(prostokąt)
procedure TFig.narysuj;
begin
writeln('+++ Narysowano
figure x=',x,' y=',y);
end;
procedure TFig.zmaz;
begin
writeln('+++ Zmazano
figure x=',x,' y=',y, ' ',nazwa);
end;
Przykład - dziedziczenie
//program dziedziczenie1;
/// klasy dziedziczące z Tfig
Type TProst = class(TFig)
a, b :Integer;
constructor Create(a_,
b_:Integer;name:string);
procedure narysuj;
procedure zmaz_np;
procedure przesun(x1,y1:integer);
end;
constructor TProst.Create(a_, b_
:Integer; name:string);
begin inherited Create;
a := a_; b := b_;
nazwa:=name; end;
procedure TProst.narysuj;
begin
write('+ Rys prostokat x=',x,' y=',y);
writeln('+ o rozmiarach a=',a,' b=',b);
end;
Fig (Figura)
Pr
(prostokąt)
procedure TProst.zmaz_np;
begin
writeln('+ Zmazano
prostokat x=',x,' y=',y, ' ',nazwa);
end;
procedure
TProst.przesun(x1,y1:integer);
begin
zmaz;
//zmaz_np;
x:=x1; y:=y1;
narysuj;
//program dziedziczen_1;
Var Fig :TFig;
Pr1:TProst;
begin
Fig := TFig.Create;
Fig.narysuj;
Fig.Destroy; //Fig.Free;
Fig := TProst.Create(2,2,'Kwadrat');
Fig.narysuj;
//Fig.przesun(10, 10);
Fig.Destroy;
Pr1 := Tprost.Create(10,15,'Prost 1');
Pr1.narysuj;
Pr1.przesun(100, 100);
Pr1.zmaz_np;
Pr1.zmaz;
Pr1.Free;
readln; end.
Fig (Figura)
Pr
(prostokąt)
program dziedzicz2;
//dziedziczenie, polimorfizm, met
virtualane
uses SysUtils;
type
TFig = class
x, y :Integer;
constructor Create(x_, y_:Integer);
destructor Destroy;override;
procedure narysuj; virtual;
procedure przesun(dx, dy :Integer);
procedure zmaz; virtual;
end;
procedure TFig.zmaz;
begin
writeln('mazanie figury');
end;
procedure TFig.narysuj;
Fig (Figura)
Pr
(prostokąt)
Okr
(okrąg)
Przykład –
dziedziczenie polimorficzne
procedure TFig.narysuj;
begin
writeln('rysowanie figury');
end;
constructor TFig.Create(x_, y_ :Integer);
begin
inherited Create;
x := x_;
y := y_;
end;
destructor TFig.Destroy;//procedure
begin
zmaz;
inherited Destroy; //lub inherited;
end;
procedure TFig.przesun(dx, dy :Integer);
begin
zmaz;
x := x+dx;
y := y+dy;
narysuj;
end;
//program Dziedzicz2;
//program Dziedzicz2
- klasy dziedziczące z TFig
type
TProst = class(TFig)
a, b :Integer;
constructor Create(x_, y_, a_, b_:Integer);
procedure narysuj; override;
procedure zmaz; override;
end;
constructor TProst.Create(x_, y_, a_, b_ :Integer);
begin
inherited Create(x_, y_);
a := a_; b := b_;
end;
procedure TProst.narysuj;
begin
writeln('+++ Narysowano prostokat x=',x,' y=',y);
end;
procedure TProst.zmaz;
begin
writeln('+++ Zmazano prostokat x=',x,' y=',y);
end;
Fig (Figura)
Pr
(prostokąt)
Okr
(okrąg)
//program Dziedzicz2; /// klasy dziedziczące z TFig
type
TOkr = class(TFig)
r :Integer;
constructor Create(x_, y_, r_:Integer);
procedure narysuj; override;
procedure zmaz; override;
end;
constructor TOkr.Create(x_, y_, r_ :Integer);
begin
inherited Create(x_, y_);
r := r_;
end;
procedure TOkr.narysuj;
begin
writeln('+++ Narysowano Okrag x=',x,' y=',y);
end;
procedure TOkr.zmaz;
begin
writeln('+++ Zmazano Okrag x=',x,' y=',y);
end;
Fig (Figura)
Pr
(prostokąt)
Okr
(okrąg)
// Dziedzicz2 polimorficzne
{program główny}
Var Fig :TFig;
begin
Fig := TProst.Create(0,1,2,3);
Fig.narysuj;
Fig.przesun(10, 10);
Fig.Free; // Fig.Destroy;
Fig := TOkr.Create(0,1,20);
Fig.narysuj;
Fig.przesun(100, 100);
Fig.Free;
readln;
end.
Metody abstrakcyjne – to takie metody które nie
zawierają żadnych treści (np. klasa Bazowa, Rodzic)
i deklarowane są w danej klasie tylko po to, aby można
było zdefiniować odpowiednie metody w klasach
potomnych (tzn. nie są definiowane).
Metody abstrakcyjne deklaruje się za pomocą
dyrektywy
abstract
umieszczonej w nagłówku metody po słowie
virtual lub dynamic .
Metody abstrakcyjne stosuje się zatem tylko do metod
polimorficznych.
Polimorfizm, metody ABSTRAKCYJNE
program dziedzicz5;
//dziedziczenie, polimorfizm metoda abstrakcyjna,
uses SysUtils;
type
TFig = class(TObject) //class
x, y :Integer;
constructor Create(x_, y_:Integer);
destructor Destroy; override;
procedure przesun(dx, dy :Integer);
procedure narysuj;
virtual; abstract;
procedure zmaz;
virtual; abstract;
end;
procedure TFig.przesun(dx, dy :Integer);
begin
zmaz;
x := x+dx;
y := y+dy;
narysuj;
end;
constructor TFig.Create(x_, y_ :Integer); //procedure
//program dziedzicz5;
constructor TFig.Create(x_, y_ :Integer);
begin inherited Create;
x := x_; y := y_; end;
destructor TFig.Destroy;//procedure
begin zmaz; inherited Destroy; end;
Type TProst = class(TFig)
a, b :Integer;
constructor Create(x_, y_, a_, b_:Integer);
procedure narysuj; override;
procedure zmaz; override;
end;
constructor TProst.Create(x_, y_, a_, b_ :Integer);
begin
inherited Create(x_, y_);
a := a_; b := b_; end;
procedure TProst.narysuj;
begin
writeln(‘+ Narysowano prostokat x=',x,' y=',y); end;
procedure TProst.zmaz;
begin writeln(‘+ Zmazano prostokat x=',x,' y=',y); end;
//program dziedzicz5;
Type TOkr = class(TFig)
r :Integer;
constructor Create(x_, y_, r_:Integer);
procedure narysuj; override;
procedure zmaz; override;
end;
constructor TOkr.Create(x_, y_, r_ :Integer);
begin inherited Create(x_, y_);
r := r_; end;
procedure TOkr.narysuj;
begin writeln('+++ Narysowano Okrag x=',x,' y=',y); end;
procedure TOkr.zmaz;
begin writeln('+++ Zmazano Okrag x=',x,' y=',y); end;
Var Fig :TFig;
begin
Fig := TProst.Create(0,1,2,3);
Fig.narysuj; Fig.przesun(10, 10); Fig.Free;
Fig := TOkr.Create(0,1,7);
Fig.narysuj; Fig.przesun(200, 200); Fig.Free;
readln; end.
Przykład 3
Lista_klas.dpr
W tym programie mamy dynamicznie tworzoną listę
Obiektów geometrycznych będących potomkami obiektu
abstrakcyjnego Figura
program ListaClass_p3;
{$APPTYPE CONSOLE} {$O-,Q+,R+}
uses
SysUtils;
type real=INTEGER;
type
TFig=class
x, y :reaL;
procedure przesun(dx, dy :real);
procedure zmaz; virtual; abstract;
procedure narysuj;virtual; abstract;
constructor Create(_x, _y :real);
destructor Destroy; override;
end;
procedure TFig.przesun(dx, dy :real);
begin
zmaz;
x := x+dx; y := y+dy;
narysuj;
end;
constructor TFig.Create(_x, _y :real);
begin
inherited Create;
x := _x; y := _y;
end;
Przykład
W tym programie mamy dynamicznie
tworzoną listę
Obiektów geometrycznych będących
potomkami klasy abstrakcyjnej TFig
procedure TFig.przesun(dx, dy :real);
begin
zmaz;
x := x+dx;
y := y+dy;
narysuj;
end;
constructor TFig.Create(_x, _y :real);
begin
inherited Create;
x := _x; y := _y;
end;
destructor TFig.Destroy;
begin
zmaz;
inherited Destroy; //lub inherited
end;
////////////////////////////////////////////////////
//Element listy
Type TElem=class
dane :TFig;
nast :TElem;
constructor Create(d :TFig);
destructor Destroy; override;
end;
constructor TElem.Create(d :TFig);
begin
inherited Create;
dane := d;
nast := nil;
end;
destructor TElem.Destroy;
begin
dane.Free; //dispose(dane, done);
end;
////////////
type
TLista = class
pocz :TElem;
destructor Destroy; override;
procedure dopisz(D :TFig);
procedure przesunWszystko(dx, dy :real);
end;
//constructor TLista.init; //dawniej procedure
//inicjuj(var Li :Tlista);
//Create z TObject zeruje wszelkie pola
destructor TLista.Destroy; //dawniej
procedure usunWszystko(var pocz :Tlista);
var
w :TElem;
begin
while pocz <> nil do
begin
w := pocz;
pocz := pocz.nast;
w.Free;
end;
end;
procedure TLista.dopisz(D :TFig);
var
w :TElem;
begin
w := pocz;
pocz := TElem.Create(D);
pocz.nast := w;
end;
procedure TLista.przesunWszystko(dx, dy :real);
var
w :TElem;
begin
w := pocz;
while w <> nil do
begin
w.dane.przesun(dx,dy);
w := w.nast;
end;
end;
/// klasa pochodna z rodzicem TFig
type
TProst=class(TFig)
a, b :real;
constructor Create(_x, _y, _a, _b :real);
procedure zmaz; override;
procedure narysuj; override;
end;
constructor TProst.Create(_x, _y, _a, _b
:real);
begin
inherited Create(_x, _y);
a := _a; b := _b;
end;
procedure TProst.narysuj;
begin
writeln('+++ narysowano prost. x=',x,'
y=',y);
end;
procedure TProst.zmaz;
begin
writeln('--- zmazano prost. x=',x,' y=',y);
end;
type
TOkr=class(TFig)
r :real;
constructor create(_x, _y, _r :real);
procedure zmaz; override;
procedure narysuj; override;
end;
constructor TOkr.Create(_x, _y, _r :real);
begin
// x := _x; y := _y;
inherited Create(_x, _y);
r := _r;
end;
procedure TOkr.narysuj;
begin
writeln('+++ narysowano okr. x=',x,' y=',y);
end;
procedure TOkr.zmaz;
begin
writeln('--- zmazano okr. x=',x,' y=',y);
end;
//program główny ListaClass_p3
var
Li :TLista;
begin
Li := TLista.Create;
Li.dopisz( TProst.Create(-1,-2,3,4) );
Li.dopisz( TProst.Create(11,22,3,4) );
Li.dopisz( TOkr.create(-0.3,-0.2,3) );
Li.przesunWszystko(0.5,0.5);
Li.Free;
readln;
end.
program ListaClassProperty_p4;
//wersja rozszerzona
{$APPTYPE CONSOLE} {$O-,Q+,R+}
Uses SysUtils;
type
TFig=class //==== class(TObject)
private
Fx :real;
Fy :real;
function GetX :real;
procedure SetX(Value :real);
public
property x :real read GetX write SetX;
property y :real read Fy write Fy;
public
procedure przesun(dx, dy :real);
procedure zmaz; virtual; abstract;
procedure narysuj;virtual; abstract;
constructor Create(_x, _y :real);
destructor Destroy; override;
end;
function TFig.GetX :real;
begin
Result := abs(Fx);
end;
procedure TFig.SetX(Value :real);
begin
Fx := 100*Value;
end;
procedure TFig.przesun(dx, dy :real);
begin
zmaz;
x := x+dx;
y := y+dy;
narysuj;
end;
constructor TFig.Create(_x, _y :real);
begin
inherited Create;
x := _x; y := _y;
end;
destructor TFig.Destroy;
begin
zmaz;
inherited Destroy; //lub inherited
end;
///////////////////////////////////////////////
//Element listy
type
TElem=class
dane :TFig;
nast :TElem;
constructor Create(d :TFig);
destructor Destroy; override;
end;
////////////
constructor TElem.Create(d :TFig);
begin
inherited Create;
dane := d;
nast := nil;
end;
destructor TElem.Destroy;
begin
//dispose(dane, done);
dane.Free;
end;
Type
TLista = class
pocz :TElem;
destructor Destroy; override;
procedure dopisz(D :TFig);
procedure przesunWszystko(dx, dy :real);
end;
//constructor TLista.init; //dawniej
procedure inicjuj(var Li :Tlista);
//Create z TObject zeruje wszelkie pola
destructor TLista.Destroy; //dawniej
procedure usunWszystko(var pocz :Tlista);
Var w :TElem;
begin
while pocz <> nil do
begin
w := pocz;
pocz := pocz.nast;
w.Free;
end;
end;
procedure TLista.dopisz(D
:TFig);//dawniej procedure dopisz(var
pocz :Tlista; D :PFig);
Var w :TElem;
begin
w := pocz;
pocz := TElem.Create(D);
pocz.nast := w;
end;
procedure TLista.przesunWszystko(dx,
dy :real);
Var w :TElem;
begin
w := pocz;
while w <> nil do
begin
w.dane.przesun(dx,dy);
w := w.nast;
end;
end;
//////////////////////////////////////////
//////////////
/// klasa pochodna z rodzicem TFig
type
TProst=class(TFig)
a, b :real;
constructor Create(_x, _y, _a, _b :real);
procedure zmaz; override;
procedure narysuj; override;
end;
constructor TProst.Create(_x, _y, _a, _b
:real);
begin
// x := _x; y := _y;
inherited Create(_x, _y);
a := _a; b := _b;
end;
procedure TProst.narysuj;
begin
writeln(‘+narysowan prost. x=',x,' y=',y);
end;
procedure TProst.zmaz;
begin
writeln('--- zmazano prost. x=',x,'
y=',y);
end;
//////
type
TOkr=class(TFig)
r :real;
constructor create(_x, _y, _r :real);
procedure zmaz; override;
procedure narysuj; override;
end;
constructor TOkr.Create(_x, _y, _r
:real);
begin
// x := _x; y := _y;
inherited Create(_x, _y);
r := _r;
end;
procedure TOkr.narysuj;
begin
writeln(‘+narysowano okr. x=',x,' y=',y);
end;
procedure TOkr.zmaz;
begin
writeln('--- zmazano okr. x=',x,' y=',y);
end;
Var Li :TLista;
begin
Li := TLista.Create;
Li.dopisz( TProst.Create(-1,-2,3,4) );
Li.dopisz( TProst.Create(11,22,3,4) );
Li.dopisz( TOkr.create(-0.3,-0.2,3) );
Li.przesunWszystko(0.5,0.5);
Li.Free;
readln;
end.
/////////////