r12-06.pdf
(
464 KB
)
Pobierz
Szablon dla tlumaczy
Rozdział 12.
Dziedziczenie
W poprzednim rozdziale poznałeś wiele relacji związanych z projektowaniem obiektowym, m.in.
relację specjalizacji/generalizacji. Język C++ implementuje ją poprzez dziedziczenie.
Z tego rozdziału dowiesz się:
• czym jest dziedziczenie,
• w jaki sposób wyprowadzać klasę z innej klasy,
• czym jest dostęp chroniony i jak z niego korzystać,
• czym są funkcje wirtualne.
Czym jest dziedziczenie?
Czym jest pies? Co widzisz, gdy patrzysz na swoje zwierzę? Ja widzę cztery łapy i pysk. Biolodzy
widzą sieć interesujących organów, fizycy — atomy i różnorodne siły, zaś taksonom widzi
przedstawiciela gatunku
canine domesticus
.
Skupmy się na tym ostatnim przypadku. Pies jest przedstawicielem psowatych, psowate są
przedstawicielami ssaków, i tak dalej. Taksonomowie dzielą świat żywych stworzeń na królestwa,
typy, klasy, rzędy, rodziny, rodzaje i gatunki.
Hierarchia specjalizacji/generalizacji ustanawia relację typu
jest-czymś
. Homo sapiens jest
przedstawicielem naczelnych. Taką relację widzimy wszędzie: wóz kempingowy jest rodzajem
samochodu, który z kolei jest rodzajem pojazdu. Budyń jest rodzajem deseru, który jest rodzajem
pożywienia.
Gdy mówimy, że coś jest rodzajem czegoś innego, zakładamym że stanowi to specjalizację tej
rzeczy. Samochód jest zatemspecjalnym rodzajem pojazdu.
Dziedziczenie i wyprowadzanie
Pies dziedziczy — to jest, automatycznie otrzymuje — wszystkie cechy ssaka. Ponieważ jest
ssakiem, porusza się i oddycha powietrzem. Wszystkie ssaki, z definicji, poruszają się i oddychają.
Pies wzbogaca te elementy o cechy takie jak, szczekanie, machanie ogonem, zjadanie dopiero co
ukończonego rozdziału mojej książki, warczenie, gdy próbuję zasnąć... Przepraszam. Gdzie
skończyłem? A, wiem:
Psy możemy podzielić na psy przeznaczone do pracy, psy do sportów i teriery, zaś psy do sportów
możemy podzielić na psy myśliwskie, spaniele i tak dalej. Można także dokonywać dalszego
podziału, na przykład psy myśliwskie można podzielić na labradory czy goldeny.
Golden jest rodzajem psa myśliwskiego, który jest psem do sportów, należącego do rodzaju psów,
czyli będącego ssakiem, a więc zwierzęciem, czyli rzeczą żywą. Tę hierarchię przedstawia rysunek
12.1.
Rys. 12.1. Hierarchia zwierząt
C++ próbuje reprezentować te relacje, umożliwiając nam definiowanie klas, które są
wyprowadzane z innych klas. Wyprowadzanie jest sposobem wyrażenia relacji typu
jest-czymś
.
Nową klasę,
Dog
(pies), można wyprowadzić z klasy
Mammal
(ssak). Nie musimy wtedy wyraźnie
mówić, że pies porusza się, gdyż dziedziczy tę cechę od ssaków.
Klasa dodająca nowe właściwości do istniejącej już klasy jest
wyprowadzona
z klasy pierwotnej.
Ta pierwotna klasa jest nazywana klasą
bazową
.
Jeśli klasa
Dog
jest wyprowadzona z klasy
Mammal
, oznacza to, że klasa
Mammal
jest klasą bazową
(nadrzędną) klasy
Dog
. Klasy wyprowadzone (pochodne) stanową nadzbiór swoich klas
bazowych. Pies przejmuje swoje cechy od ssaków, tak klasa
Dog
przejmie pewne metody lub dane
klasy
Mammal
.
Zwykle klasa bazowa posiada więcej niż jedną klasę pochodną. Ponieważ zarówno psy, jak i koty
oraz konie są ssakami, więc ich zostały były wyprowadzone z klasy
Mammal
.
Królestwo zwierząt
Aby ułatwić przedstawienie procesu dziedziczenia i wyprowadzania, w tym rozdziale skupimy się
na związkach pomiędzy różnymi klasami reprezentującymi zwierzęta. Możesz sobie wyobrazić, że
bawimy się w dziecięcą grę — symulację farmy.
Opracujemy cały zestaw zwierząt, obejmujący konie, krowy, psy, koty, owce, itd. Stworzymy
metody dla tych klas, aby zwierzęta mogły funkcjonować tak, jak oczekiwałoby tego dziecko, ale
na razie każdą z tych metod zastąpimy zwykłą instrukcją wydruku.
Minimalizowanie funkcji (czyli pozostawienie tylko jej szkieletu) oznacza, że napiszemy tylko
tyle kodu, ile wystarczy do pokazania, że funkcja została wywołana. Szczegóły pozostawimy na
później, gdy będziemy mieć więcej czasu. Jeśli tylko masz ochotę, możesz wzbogacić minimalny
kod zaprezentowany w tym rozdziale i sprawić, by zwierzęta zachowywały się bardziej
realistycznie.
Składnia wyprowadzania
Gdy deklarujesz klasę, możesz wskazać klasę, od której pochodzi, zapisując po nazwie tworzonej
klasy dwukropek, rodzaj wyprowadzania (publiczny lub inny) oraz klasę bazową. Oto przykład:
class Dog : public Mammal
Rodzaj wyprowadzania opiszemy w dalszej części rozdziału. Na razie będziemy używać
wyprowadzania publicznego (oznaczonego słowem kluczowym
public
). Klasa bazowa musi być
zdefiniowana wcześniej, gdyż w przeciwnym razie kompilator zgłosi błąd. Listing 12.1 ilustruje
sposób deklarowania klasy
Dog
, wyprowadzonej z klasy
Mammal
.
Listing 12.1. Proste dziedziczenie
0: //Listing 12.1 Proste dziedziczenie
1:
2: #include <iostream>
3: using namespace std;
4:
5: enum BREED { GOLDEN, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB };
6:
7: class Mammal
8: {
9: public:
10: // konstruktory
11: Mammal();
12: ~Mammal();
13:
14: //akcesory
15: int GetAge() const;
16: void SetAge(int);
17: int GetWeight() const;
18: void SetWeight();
19:
20: //inne metody
21: void Speak() const;
22: void Sleep() const;
23:
24:
25: protected:
26: int itsAge;
27: int itsWeight;
28: };
29:
30: class Dog : public Mammal
31: {
32: public:
33:
34: // konstruktory
35: Dog();
36: ~Dog();
37:
38: // akcesory
39: BREED GetBreed() const;
40: void SetBreed(BREED);
41:
42: // inne metody
43: WagTail();
44: BegForFood();
45:
46: protected:
47: BREED itsBreed;
48: };
Ten kod nie wyświetla wyników, gdyż zawiera jedynie zestaw deklaracji klas (bez ich
implementacji). Mimo to można w nim wiele zobaczyć.
Analiza
W liniach od 7. do 28. deklarowana jest klasa
Mammal
(ssak). Zwróć uwagę, że w tym przykładzie
klasa
Mammal
nie jest wyprowadzana z żadnej innej klasy. W realnym świecie ssaki pochodzą od
(to znaczy, że są rodzajem) zwierząt. W programie C++ możesz zaprezentować jedynie część
informacji, które posiadasz na temat danego obiektu. Rzeczywistość jest zdecydowanie zbyt
skomplikowana, aby uchwycić ją w całości, więc każda hierarchia w C++ jest umowną
reprezentacją dostępnych danych. Sztuka dobrego projektowania polega na reprezentowaniu
interesujących nas obszarów tak, aby reprezentowały rzeczywistość w możliwie najlepszy sposób.
Hierarchia musi się gdzieś zaczynać; w tym programie rozpoczyna się od klasy
Mammal
. Z
powodu tego, pewne zmienne składowe, które mogły należeć do wyższej klasy, nie są tu
reprezentowane. Wszystkie zwierzęta z pewnością posiadają na przykład wagę i wiek, więc jeśli
klasa
Mammal
byłaby wyprowadzona z klasy
Animal
(zwierzę), moglibyśmy oczekiwać, że
dziedziczy te atrybuty. Jednak w naszym przykładzie atrybuty te występują w klasie
Mammal
.
Aby zachować niewielką i spójną postać programu, w klasie
Mammal
zostało umieszczonych
jedynie sześć metod — cztery akcesory oraz metody
Speak()
(mówienie) i
Sleep()
(spanie).
Klasa
Dog
(pies) dziedziczy po klasie
Mammal
, co wskazuje linia 30. Każdy obiekt typu
Dog
będzie posiadał trzy zmienne składowe:
itsAge
(wiek),
itsWeight
(waga) oraz
itsBread
(rasa). Zwróć uwagę, że deklaracja klasy
Dog
nie obejmuje zmiennych składowych
itsAge
oraz
itsWeight
. Obiekty klasy
Dog
dziedziczą te zmienne od klasy
Mammal
, razem z metodami tej
klasy (z wyjątkiem operatora kopiującego oraz destruktora i konstruktora).
Prywatne kontra chronione
Być może w liniach 25. i 46. listingu 12.1 zauważyłeś nowe słowo kluczowe dostępu,
protected
(chronione). Wcześniej dane klasy były deklarowane jako prywatne. Jednak prywatne składowe
nie są dostępne dla klas pochodnych. Moglibyśmy uczynić zmienne
itsAge
i
itsWeight
składowymi publicznymi, ale nie byłoby to pożądane. Nie chcemy, by inne klasy mogły
bezpośrednio odwoływać się do tych danych składowych.
UWAGA Istnieje powód, by wszystkie dane składowe klasy oznaczać jako prywatne, a nie jako
chronione. Powód ten przedstawił Stroustrup (twórca języka C++) w swojej książce
The Design
and Evolution of C++
, ISBN 0-201-543330-3, Addison Wesley, 1994. Metody chronione nie są
jednak uważane za kłopotliwe i mogą być bardzo użyteczne.
Potrzebujemy teraz oznaczenia, które mówi: „Uczyń te zmienne widocznymi dla tej klasy i dla
klas z niej wyprowadzonych”. Takim oznaczeniem jest właśnie słowo kluczowe
protected
—
chroniony. Chronione funkcje i dane składowe są w pełni widoczne dla klas pochodnych i nie są
dostępne dla innych klas.
Istnieją trzy specyfikatory dostępu: publiczny (
public
), chroniony (
protected
) oraz
private
(prywatny). Jeśli funkcja posiada obiekt twojej klasy, może odwoływać się do wszystkich jej
publicznych funkcji i danych składowych. Z kolei funkcje składowe klasy mogą odwoływać się do
wszystkich prywatnych funkcji i danych składowych swojej własnej klasy, oraz do wszystkich
chronionych funkcji i danych składowych wszystkich klas, z których ich klasa jest wyprowadzona.
Tak więc, funkcja
Dog::WagTail()
(macha ogonem) może odwoływać się do prywatnej danej
itsBreed
oraz do prywatnych danych klasy
Mammal
.
Nawet gdyby pomiędzy klasą
Mammal
, a klasą
Dog
występowały inne klasy (na przykład
DomesticAnimals
— zwierzęta domowe), klasa
Dog
w dalszym ciągu mogłaby się odwoływać
do prywatnych składowych klasy
Mammal
, zakładając że wszystkie inne klasy używałyby
dziedziczenia publicznego. Dziedziczenie prywatne zostanie omówione w rozdziale 16.,
„Dziedziczenie zaawansowane”.
Listing 12.2 przedstawia sposób tworzenia obiektów typu
Dog
oraz dostępu do danych i funkcji
zawartych w tym typie.
Listing 12.2. Użycie klasy wyprowadzonej
0: //Listing 12.2 Użycie klasy wyprowadzonej
1:
2: #include <iostream>
Plik z chomika:
pp555
Inne pliki z tego folderu:
r02-06.pdf
(277 KB)
r01-06.pdf
(378 KB)
rdodC-06.pdf
(111 KB)
rdodB-06.pdf
(82 KB)
rdodA-06.pdf
(319 KB)
Inne foldery tego chomika:
Adobe
ASP NET
C++ Builder 5
Zgłoś jeśli
naruszono regulamin