PO wyk11 v2

background image

Dziedziczenie

Każdy pies dziedziczy z ssaka wszystkie jego cechy. Możemy powiedzieć, że
ponieważ jest ssakiem to umie się poruszać, oddycha powietrzem itp. Jednak
pojęcie pies dodaje do definicji ssaka możliwość szczekania, machania
ogonem itp. Pojęcie pies jest specjalistyczne natomiast ssak ogólne.
C++ pozwala na reprezentowanie takich relacji poprzez definiowanie klas
pochodzących od innych klas. Pochodzenie jest metodą wyrażania relacji
„jest..." .

Ssak

Gad

Kot

Pies

Terier

Myśliwski

Zwierz

ę

Można stworzyć klasę Pies jako pochodną klasy Ssak. Nie trzeba jawnie
określać, że Pies potrafi się poruszać, gdyż ta cecha zostanie odziedziczona z
klasy Ssak. Klasa Pies, poprzez dziedziczenie z klasy Ssak, automatycznie
posiada umiejętność "poruszania się".

background image

Klasa, która wprowadza nowe funkcje do już istniejącej klasy, nazywana
jest pochodną klasy oryginalnej. Klasa oryginalna nazywana jest klasą
bazową.

Jeżeli klasa Pies jest pochodną klasy Ssak, to klasa Ssak jest klasą bazową
klasy Pies. Klasy pochodne są nadzbiorami ich klas bazowych. Tak jak pies
posiada dodatkowe umiejętności w stosunku do statystycznego ssaka, tak i
klasa Pies dodaje nowe metody i dane do klasy Ssak.

Wyobraźmy sobie, że dostaliśmy zadanie zaprojektowania gry dla dzieci -
symulacji farmy. Trzeba napisać dla każdej klasy metody powodujące, że
każde zwierzę będzie się zachowywać zgodnie z oczekiwaniami odbiorcy
programu.

Zadanie

Tworzenie klasy pochodnej

Podczas deklaracji klasy trzeba zaznaczyć, że jest ona pochodną innej klasy
poprzez napisanie dwukropka po nazwie tworzonej klasy, typu pochodzenia
(public albo inny), a następnie nazwy klasy bazowej.

Przykład

class Pies: public Ssak

background image

Przykład

W świecie rzeczywistym ssaki są pochodną
zwierząt. W programie w C++ jesteśmy w
stanie

przedstawić

jedynie

część

posiadanych informacji o danym obiekcie.
Rzeczywistość jest zbyt kompleksowa i nie
można uwzględnić wszystkich jej aspektów.
Każda hierarchia w C++ jest jedynie
odzwierciedleniem fragmentu posiadanych
informacji. Sztuka dobrego projektowania
polega

na

przedstawieniu

ważnych

obszarów w taki sposób, aby całość
maksymalnie przystawała do rzeczywistości.

W poprzednich programach, zmienne wewnętrzne klasy
były deklarowane po słowie kluczowym private. Jednak
zmienne deklarowane jako private nie byłyby widoczne
w klasie pochodnej. Oczywiście można również
zadeklarować zmienne nJegoWiek i nJegoWaga jako
public, ale jest to niewskazane, gdyż umożliwiłoby
bezpośredni dostęp do nich innym klasom.

background image

Private czy Protected ?

Funkcje i zmienne zadeklarowane jako protected są dostępne we
wszystkich klasach pochodnych (i są w nich prywatne).

Zmienne

i

funkcje

zadeklarowane

jako

protected są widoczne
dla wszystkich funkcji
danej klasy i jej klas
pochodnych.

public

protected

private

Jeżeli funkcja ma dostęp
do obiektu danej klasy to
ma również bezpośredni
dostęp do wszystkich jej
zmiennych

i

funkcji

zadeklarowanych

jako

public.

Do zmiennych i funkcji
zadeklarowanych jako
private mają dostęp
tylko

funkcje

wewnętrzne

danej

klasy.

background image

Przykład

background image

Konstruktory i destruktory

Obiekty klasy Pies są również obiektami klasy Ssak. Jest to główna cecha
relacji „jest...".

Podczas usuwania obiektu Chacko z pamięci zachodzi proces odwrotny.
Najpierw jest wywoływany destruktor klasy Pies, a następnie destruktor
klasy Ssak. Każdy destruktor kasuje tę część obiektu Chacko, która należy
do jego klasy.

Kiedy tworzymy obiekt Chacko najpierw jest wywoływany jego konstruktor
bazowy, którego zadaniem jest stworzenie obiektu klasy Ssak. Następnie jest
wywoływany konstruktor klasy Pies, tworzący gotowy obiekt. Ponieważ, przy
deklaracji Chacko, nie podaliśmy żadnych parametrów, to wywoływany jest
domyślny konstruktor klasy Pies. Obiekt Chacko jest w pełni stworzony
dopiero wtedy, gdy zostaną wykonane oba konstruktory: jeden z klasy Ssak i
drugi z klasy Pies.

background image

background image

Przekazywanie argumentów do konstruktora bazowego

Istnieje możliwość przeciążenia konstruktora klasy Ssak tak, aby pobierał on
konkretny wiek. Podobnie można przeciążyć konstruktor klasy Pies, tak aby
pozwalał on na proste określenie rasy. Jak odczytać wartość parametru
przekazanego do konstruktora w klasie Ssak? Co się stanie, gdy klasa Pies
pozwala na inicjalizację wieku, natomiast klasa Ssak nie?

Inicjalizacja klasy bazowej może być przeprowadzona podczas inicjalizacji
klasy poprzez napisanie nazwy klasy bazowej i podanie w nawiasach
parametrów wymaganych przez klasę bazową.

background image

Zauważ, że domyślny konstruktor klasy Pies
wywołuje domyślny konstruktor klasy Ssak.
Takie rozwiązanie nie jest ściśle wymagane,
jest

to

"dokumentacja"

wywołania

domyślnego konstruktora klasy bazowej.
Bazowy konstruktor i tak zostanie wywołany.

Podobnie jak poprzednio, najpierw inicjalizuje on
klasę

bazową

poprzez

wywołanie

odpowiedniego

konstruktora

klasy

Ssak.

Dodatkowo

inicjalizowana

jest

zmienna

wewnętrzna nJegoWaga. Zauważmy, że nie ma
możliwości inicjalizacji zmiennej klasy bazowej
w części inicjalizacyjnej konstruktora. Ponieważ
klasa

Ssak

nie

posiada

konstruktora

pobierającego

wartość

dla

zmiennej

nJegoWaga,

dlatego

inicjalizację

trzeba

przeprowadzić w treści konstruktora.

background image

background image

Nadpisywanie funkcji

Obiekt klasy Pies ma dostęp do wszystkich funkcji wewnętrznych klasy Ssak
tak jak do własnych. Podobnie, jak dodaliśmy metodę MachajOgonem(),
możemy dodać inne funkcje. Istnieje również możliwość nadpisania funkcji
klasy bazowej. Nadpisanie oznacza zmianę implementacji funkcji z klasy
bazowej. Kiedy wywołuje się metodę z klasy pochodnej kompilator wywoła tę
właściwą - stworzoną w tej klasie.

Kiedy klasa pochodna tworzy funkcję z tym samym typem wartości
zwracanej, z tą samą nazwą i listą parametrów (sygnaturą) co jakaś
funkcja w klasie bazowej i definiuje jej nową implementację to mówimy o
nadpisaniu tej funkcji (metody).

UWAGA: Kiedy chce się napisać funkcję, trzeba zapewnić zgodność typu
wartości zwracanej, nazwy i listy parametrów z funkcją klasy bazowej.

Przeciążanie czy nadpisywanie ?

Obie metody dają podobne efekty. Kiedy przeciąża się metodę to tworzy się
kilka różnych metod o tej samej nazwie i o różnej sygnaturze (wartość
zwracana, lista parametrów). Kiedy nadpisuje się metodę to tworzy się w
klasie pochodnej metodę, która zastępuje metodę z klasy bazowej.

background image

W

klasa

Pies

nadpisuję

metodę Mow() tak, aby każdy
obiekt klasy Pies szczekał
(wypisywał na ekranie Hau!) w
momencie wywołania metody
Mow()

background image

Ukrywanie metod klasy bazowej

W ostatnim programie, metoda klasy Pies o nazwie Mow() ukryła metodę
klasy bazowej. O to nam chodziło, ale istnieje możliwość zaistnienia pewnego
efektu ubocznego. Jeżeli klasa Ssak miałaby przeciążoną metodę Ruch(),
którą byśmy nadpisali w klasie Pies, to zostałyby ukryte wszystkie
przeciążone metody Ruch() w klasie Ssak.
Jeżeli klasa Ssak miałaby trzy metody przeciążające funkcję Ruch() - jedną
nie pobierającą parametrów, druga pobierającą wartość całkowitą i trzecią
dwuargumentową i jeżeli klasa Pies nadpisałaby metodę Ruch() funkcją nie
pobierającą parametrów to dostęp do pozostałych dwóch metod z klasy Ssak
byłby bardzo utrudniony.

Linia została zakomentowana ponieważ
powoduje błąd kompilacji. Jeżeli klasa Pies
nie nadpisałyby metody Ruch() to
mogłaby wywołać metodę Ruch(int).
Teraz, jeżeli chcielibyśmy wykorzystać
metodę z parametrem, musielibyśmy
również ją nadpisać.

background image

Bardzo częstym błędem jest nieświadome ukrycie metod klasy bazowej przy
próbie nadpisania ich. Przyczyną jest pominięcie słowa kluczowego const.
const
jest częścią sygnatury funkcji i pominięcie go zmienia sygnaturę
powodując ukrycie funkcji zamiast jej nadpisania.

Wywołanie metody bazowej

Jeżeli nadpisało się już metodę bazową to nadal istnieje możliwość wywołania
nadpisanej funkcji. Należy w tym celu podać pełną nazwę metody łącznie z
nazwą klasy bazowej. Oto przykład:

Możliwa jest również pewna ciekawa modyfikacja wykomentowanej linii z
ostatniego programu:

Ssak: :Ruch()

oChcacko.Ssak::Ruch(10) ;

background image

Programista chce wywołać metodę Ruch(int) z
obiektu oChacko klasy Pies. Jest jednak pewien
problem, gdyż klasa Pies nadpisała metodę
Ruch(), ale nie przeciążyła jej czyli funkcja
Ruch(int) nie jest bezpośrednio dostępna.
Rozwiązaniem jest bezpośrednie odwołanie się do
klasy Ssak i wywołanie metody Ruch (int).


Document Outline


Wyszukiwarka

Podobne podstrony:
PO wyk09 v2
PO wyk10 v2
PO wyk07 v1
Rehabilitacja po endoprotezoplastyce stawu biodrowego
Systemy walutowe po II wojnie światowej
HTZ po 65 roku życia
Zaburzenia wodno elektrolitowe po przedawkowaniu alkoholu
Organy po TL 2
Metoda z wyboru usprawniania pacjentów po udarach mózgu
03Operacje bankowe po rednicz ce 1
Piramida zdrowia po niemiecku
przewoz drogowy po nowelizacji adr
Opieka nad pacjentem po znieczuleniu i operacji
Dzień po dniu

więcej podobnych podstron