Programowanie Obiektowe
Materiały pomocnicze do wykładu.
Wyłącznie do użytku wewnętrznego!!!!!
Dr inż. Zbigniew Świerczyński
Dziedziczenie
Istota dziedziczenia.
Dostęp do składników
Dziedziczenie wielopokoleniowe i wielobazowe
Dziedziczenie wirtualne
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
3
Czym jest klasa i obiekt w PO?
Niezmiernie ważne jest, aby wiedzieć, że:
• Klasa jest opisem cech i zachowań obiektów,
opisem tego, jak powinien wyglądać obiekt,
który będzie reprezentantem tej klasy (jej
instancją). Klasa jest pojęciem, którego
egzemplarzem jest obiekt.
• Klasa nie jest zbiorem istniejących obiektów
o podobnych własnościach (atrybutach) i
identycznych zachowaniach i nie jest też
definicją takiego zbioru.
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
4
Istota dziedziczenia
• Dziedziczenie to technika pozwalająca na
definiowane nowej klasy z wykorzystaniem
klasy już wcześniej istniejącej, bez zmiany tej
ostatniej.
Nowa Klasa
Stara Klasa
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
5
Istota dziedziczenia c.d.
s a m o c h ó d
o s o b o w y
V W
c ię ż a r o w y
J a g u a r
P o r s c h e
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
6
Istota dziedziczenia c.d.
• Pies domowy:
• Istota żywa
• Ograniczony wzrost
• Zdolny do samodzielnego poruszania się
• Stałocieplny
• Potrafi ssać
• Samice mają gruczoły mlekowe
• Psowaty wygląd
• Drapieżny
zw ierzę
ssa k
p ies
Pies domowy – drapieżny ssak z rodziny
psowatych, udomowiony przez człowieka
potomek wilka szarego.
• Pies domowy: ssak
• Psowaty wygląd
• Drapieżny
• Ssak: zwierzę
• Stałocieplny
• Potrafi ssać
• Samice mają gruczoły mlekowe
ko ń
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
7
Dziedziczenie - przykład
• Przykład:
– Golf i lepszy Golf
Golf
GolfKlasyS
GolfKlasyS
Golf
class TCGolf {
public:
int Zbiornik;
void WlaczSilnik() {} ;
};
class TCGolfKlasyS
: public TCGolf
{
public:
int Klimatyzacja;
void WlaczSilnik() {} ;
};
//////////////////////////////////////
int main(int argc, char *argv[])
{
TCGolf Golf;
TCGolfKlasyS GolfKlasyS;
Golf.Zbiornik=20;
GolfKlasyS.Zbiornik=30;//pole odz.
GolfKlasyS.Klimatyzacja=18;
return 0;
}
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
8
Składniki odziedziczone
• Do odziedziczonych
składników
zdefiniowanych w
klasie TCGolf
możemy odnosić się
tak, jak do składników
zdefiniowanych w
klasie
TCGolfKlasyS
class TCGolf {
public:
int Zbiornik;
void WlaczSilnik() {} ;
};
class TCGolfKlasyS: public TCGolf {
public:
int Klimatyzacja;
void WlaczSilnik() {} ;
};
//////////////////////////////////////
int main(int argc, char *argv[])
{
TCGolf Golf;
TCGolfKlasyS GolfKlasyS;
Golf.Zbiornik=20;
GolfKlasyS.Zbiornik
=30; //pole
// odziedziczone
GolfKlasyS.Klimatyzacja=18;
return 0;
}
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
9
• Nazewnictwo:
– Klasa podstawowa,
– Klasa pochodna,
Klasa pochodna i podstawowa -
nazewnictwo i oznaczenia
Golf
GolfKlasyS
GolfKlasyS
Golf
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
10
Klasa pochodna i podstawowa
• W klasie pochodnej
możemy:
– zdefiniować nowe,
dodatkowe składowe
– zdefiniować składowe,
które już istnieją w
klasie podstawowej
class TCGolf {
public:
int Zbiornik;
void WlaczSilnik() {};
};
class TCGolfKlasyS: public TCGolf {
public:
int Klimatyzacja;
void WlaczSilnik() {};
};
//////////////////////////////////////
int main(int argc, char *argv[])
{
TCGolf Golf;
TCGolfKlasyS GolfKlasyS;
Golf.Zbiornik=20;
GolfKlasyS.Zbiornik=30;//pole odz.
GolfKlasyS.Klimatyzacja=18;
return 0;
}
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
11
Przesłanianie
• W tym drugim przypadku mamy do czynienia z
zasłanianiem (przesłanianiem) składników
klasy podstawowej.
• Występuje ono jeśli oba składniki mają tą samą
nazwę i w przypadku funkcji taką samą listę
argumentów (to nie jest przeciążanie/
przeładowanie).
class TCGolf {
public:
void WlaczSilnik() {};
};
class TCGolfKlasyS: public TCGolf {
public:
void WlaczSilnik() {};
};
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
12
Przesłanianie c.d.
• Wskutek dziedziczenia mamy do czynienia
z jakby zagnieżdżaniem się zakresów.
• Poniższy zapis spowoduje wywołanie elementu
z klasy pochodnej
{ // zakres klasy podstawowej
int x;
//składnik przesłaniany
int k;
{// zakres klasy pochodnej
int x;
//składnik przesłaniający
int m;
}
}
int main(int argc, char *argv[])
{
...
GolfKlasyS.WlaczSilnik();
}
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
13
Dostęp do przesłoniętej składowej
• Jeśli zależy nam na
wywołaniu elementu
klasy podstawowej,
to musimy użyć
operatora zakresu
(kwalifikatora
zakresu) czyli
nazwy
kwalifikowanej
int main(int argc, char *argv[])
{
...
// Własna
GolfKlasyS.WlaczSilnik();
// Odziedziczona
GolfKlasyS.
TCGolf::
WlaczSilnik();
...
}
//-----------------------------------------
//wewnątrz klasy pochodnej
skladnik(); // z klasy pochodnej
przodek::
skladnik(); // z klasy podstawowej
// na zewnątrz
obiekt.skladnik(); // z klasy pochodnej
obiekt.
przodek::
skladnik();//z klasy podst.
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
14
Dostęp do składników klasy
• Tworząc klasę decydujemy o tym:
– które składowe dostępne są na zewnątrz klasy
(public) a które nie (private i protected)
– które składowe chcemy przekazać klasie
dziedziczącej (public i protected) a które nie
(private)
• Tworząc klasę pochodną dodatkowo
decydujemy poprzez specyfikator dostępu w
jakim obszarze umieścić dziedziczone składowe
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
15
Jak dziedziczone są składowe?
//Klasa podstawowa
class TCKlasaA {
public:
// dostępne dla wszystkich, nawet dla zewnętrznych
int PubA;
private:
// dostępne dla elementów klasy i zaprzyjaźnionych
int PrivA;
protected:
// dostępne dla elementów klasy i potomków
int ProtA;
};
//***********************************************************
//Klasa pochodna - dziedziczenie publiczne
class TCKlasaB:
public
TCKlasaA {
public:
int PubB;
// int PubA; // !!! To będzie odziedziczone z klasy TCKlasaA
private:
int PrivB;
protected:
int ProtB;
// int ProtA; // !!! To będzie odziedziczone z klasy TCKlasaA
};
//Klasa pochodna - dziedziczenie prywatne
class TCKlasaC:
private
TCKlasaA { .... };
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
16
Jak dziedziczone są składowe?
• Składowe prywatne też są dziedziczone, ale nie ma do
nich bezpośredniego dostępu w klasie pochodnej
.
Dziedziczenie
private
public
protected
private
public
protected
private
private
z klasy bazowej
Klasa podstawowa
Klasa pochodna
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
17
Domyślny specyfikator dostępu
• Do składowych prywatnych klasy podstawowej
można dostać się poprzez publiczną funkcję z
tej kasy. Funkcja ta będzie dostępna w klasie
pochodnej.
• Brak specyfikatora dostępu powoduje
przyjęcie domyślnego specyfikatora, którym
jest private
class przodek { ... };
class
dziedzic: przodek
{ ... }; //
Brak
class
dziedzic:
private
przodek
{ ... };
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
18
Udostępnianie wybiórcze
• Udostępnianie wybiórcze
można zastosować przy
dziedziczeniu
private
lub
protected
,
• Jest to zastosowanie
dziedziczenia publicznego
(public) w stosunku do
wybranych składowych.
• Robimy to przy pomocy
deklaracji dostępu:
KlasaPodst::Skladnik
class TCKlasaA {
public:
int poleAPubl;
protected:
int metodaAProt(int aArg);
private:
int poleAPriv;
};
class TCKlasaB: private TCKlasaA {
public:
int poleBPubl;
TCKlasaA::poleAPubl;
// deklaracja dostępu
protected:
int poleBProt;
TCKlasaA::metodaAProt;
// deklaracja dostępu
private:
int poleBPriv;
}
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
19
Deklaracja dostępu - uwagi
– Umieszczona jest w klasie pochodnej
– Nie pisze się typów a jedynie nazwę (tak samo z
funkcjami)
– Sposób ten ma pewne mankamenty np. brak
rozróżnienia nazw przeciążonych.
– Może ona tylko powtórzyć dostęp a nie zmienić
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
20
Co nie jest dziedziczone?
1. Konstruktory
2. Operator przypisania (operator=)
3. Destruktor
4. Przyjaźń
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
21
Dziedziczenie wielo-pokoleniowe
• Dla klasy C:
– Klasa B jest klasą podstawową lub
podstawową bezpośrednio
– Klasa A jest klasą podstawową
pośrednio lub bazową
• Lista pochodzenia jest listą klas
podstawowych bezpośrednich
• W takiej strukturze łatwo można
zauważyć co robi dziedziczenie
prywatne
Blok A
Blok B
Blok C
class A { ... };
class B: public A {...};
class C: public B {...};
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
22
Co nam daje dziedziczenie?
s a m o c h ó d
o s o b o w y
V W
c i ę ż a r o w y
J a g u a r
P o r s c h e
• oszczędność pracy (aby użyć klasy do stworzenia nowej, nawet
nie musimy wiedzieć jak ona dokładnie działa);
• budowanie hierarchii (odwzorowywanie struktury rzeczywistych
obiektów);
• klasy ogólne (takie, które służyć mają wyłącznie do
dziedziczenia);
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
23
Jak inicjalizować klasy pochodne?
• Wiemy, że konstruktory nie są dziedziczone;
• Jak powinniśmy inicjować składniki, które
odziedziczyliśmy, zwłaszcza jeśli jest ich bardzo dużo
i nie wszystkie nas interesują?
• Co jeśli jednym z elementów klasy jest obiekt jakiejś
innej klasy niezwiązanej ściśle z tą klasą?
• Odnośnie części dziedziczonej można powiedzieć, że
stanowi ona w pewnym sensie obiekt klasy
podstawowej i tak też jest traktowana.
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
24
W jakiej kolejności wywoływać się
będą konstruktory?
• Ogólnie:
Klasa wywoła najpierw swoich przodków, potem gości
a dopiero na samym końcu siebie.
• Konstruktory mogą inicjalizować komponenty swych
klas poprzez listę inicjalizacyjną.
• Konstruktory klas pochodnych mogą na tej liście
umieszczać również konstruktory klasy podstawowej
(tylko bezpośredniej).
– Jeśli nic nie wyspecyfikujemy na liście to:
• jeśli jest konstruktor domniemany (nie wymagający parametrów), to
on się wykona
• jeśli nie ma konstruktora domniemanego a jest inny, to kompilator
pokaże błąd.
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
25
W jakiej kolejności wywoływane są
konstruktory? Przykład
p o j a z d
s a m o c h ó d
s i l n i k
M e r c e d e s
k l i m a t y z a c j a
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
26
W jakiej kolejności wywoływane są
konstruktory? Przykład c.d.
class TCSilnik {
int typ;
TCSilnik(int n): typ(n) { };
~TCSilnik(){ };
};
class TCKlimatyzacja {
TCKlimatyzacja (){ };
~TCKlimatyzacja (){ };
};
class TCPojazd {
TCPojazd(){ } ;
~ TCPojazd(){ } ;
};
class TCSamochod: public TCPojazd {
TCSilnik Silnik;
TCSamochod (int typ): Silnik(typ), TCPojazd() { } ;
~ TCSamochod (){ } ;
};
class TCMercedes: public TCSamochod {
TCKlimatyzacja
Klimatyzacja;
TCMercedes(int typ): Klimatyzacja(), TCSamochod(typ) { };
~ TCMercedes(){ } ;
};
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
27
Zasada:
1. najpierw wywołany będzie konstruktor klasy
podstawowej
2. następnie konstruktory gości (w kolejności
występowania na liście)
3. w dalszej kolejności wszystkie pozostałe inicjalizacje
z listy
4. w końcu konstruktor bieżącej klasy
•
Destrukcja obiektu dokonuje się w odwrotnej
kolejności do jego konstrukcji.
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
28
Przypisanie i inicjalizacja obiektów
klas pochodnych
• Jest to trochę skomplikowane, ponieważ wiadomo, że
nie jest dziedziczony konstruktor kopiujący (żaden nie
jest dziedziczony) ani też operator przypisania.
• Praca polegająca na przypisaniu (lub inicjalizacji)
składa się z dwóch części:
– przypisanie (lub inicjalizacja) dla części odziedziczonej
– przypisanie (lub inicjalizacja) dla części nowej (pochodnej)
• Klasa pochodna może mieć zdefiniowane konstruktor
kopiujący i/lub operator przypisania, które prace te
wykonają.
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
29
Brak zdefiniowanego operatora
przypisania
• Jeśli jednak w klasie pochodnej nie będzie
zdefiniowanego operatora przypisania,
– wtedy kompilator automatycznie wygeneruje
operator:
klasa & klasa::operator=(klasa &);
przypisujący „składnik po składniku” korzystając
dla części dziedziczonej z operatora klasy
podstawowej
• jeśli operator klasy podstawowej jest prywatny, wtedy nie
będzie generował tego operatora dla klasy pochodnej
• jeśli klasa ma jakiś składnik const lub będący
referencją wtedy operator nie będzie wygenerowany
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
30
Brak zdefiniowanego konstruktora
kopiującego
• Jeśli w klasie pochodnej nie będzie
zdefiniowanego konstruktora kopiującego,
– wtedy kompilator automatycznie wygeneruje
konstruktor:
klasa::klasa(klasa &);
kopiujący „składnik po składniku” korzystając dla
części dziedziczonej z konstruktora kopiującego
klasy podstawowej
• jeśli konstruktor kopiujący klasy podstawowej jest
prywatny, wtedy nie będzie generował tego konstruktora
dla klasy pochodnej
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
31
Samodzielna inicjalizacja
• Napisanie samodzielne w klasie pochodnej
konstruktora kopiującego oraz operatora przypisania
nie powinno nastręczać trudności, ponieważ
wykorzystuje się przy tym poznaną już wiedzę. Pewien
drobny problem może stanowić wywołanie operatora
przypisania z klasy bazowej na rzecz obiektu klasy
pochodnej. Jeden ze sposobów polega na jawnym
wywołaniu czyli:
(*this).przodek::operator=(wzor);
gdzie wzor to zmienna zawierająca wzorcowe wartości
określona jako: potomek &wzor;
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
32
Dziedziczenie wielokrotne
(wielobazowe)
• Dziedziczenie wielokrotne uzyskuje się
umieszczając na liście dziedziczenia
(pochodzenia) więcej niż jedną klasę
podstawową.
samochód
łódka
amfibia
class samochod { ... };
class lodka { ... };
class amfibia
: public samochod, public lodka
{ ... };
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
33
O czym trzeba pamiętać?
• Przy dziedziczeniu wielobazowym należy
pamiętać, że:
– każda klasa może pojawić się na liście tylko raz
– definicja klasy umieszczonej na liście musi być już
wcześniej znana
– na liście przed każdą nazwą klasy musi wystąpić
określenie sposobu dziedziczenia
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
34
Przykład
class samochod
{
protected :
int a ;
public:
samochod(int arg) : a(arg) {
cout << "Konstruktor samochodu\n" ;
} ;
} ;
/////////////////////////////////////////////////////////
class lodka {
protected :
int b ;
public:
lodka(int x) : b(x) {
cout << "Konstruktor lodki \n" ;
}
} ;
/////////////////////////////////////////////////////////
class amfibia :
public samochod, public lodka
//
{
public :
amfibia() : samochod(1991) , lodka(4) {
cout << "Konstruktor amfibii \n" ;
}
void pisz_skladniki() {
cout << "Oto odziedziczone skladniki\na = "
<< a << "\t b = " << b << endl ;
}
} ;
/*******************************************************/
main()
{ amfibia aaa ;
aaa.pisz_skladniki();
}
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
35
Wieloznaczność
Jak temu zaradzić (2 sposoby):
• pisać nazwy kwalifikowane:
samochod::x lub lodka::x
• zdefiniować składnik (w amfibii) o tej samej nazwie
int amfibia ::x() { return samochod::x; }
samochód (a,
x
)
łódka
(b,
x
)
amfibia
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
36
Wieloznaczność - poszlaki
• Zapis: ojciec::x,
wskazuje gałąź poszukiwań.
ojciec
matka
(
x
)
dziecko
dziadek
(
x
)
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
37
Konwersje standardowe przy
dziedziczeniu
• Wskaźnik (referencja) do obiektu klasy
pochodnej może być niejawnie przekształcony
na wskaźnik (referencję) dostępnej
jednoznacznie klasy podstawowej.
• Dotyczy to wskaźników i referencji a nie
obiektów
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
38
Konwersje standardowe - Przykład
class samochod { // klasa podstawowa
public:
int zbiornik;
} ;
/////////////////////////////////////////////////////////
class VW: public samochod {// klasa pochodna
//...
} ;
/////////////////////////////////////////////////////////
void StacjaBenzynowa(samochod &klient)
{
klient.zbiornik = 50;
}
/*******************************************************/
main()
{
samochod jakisSamochod;
VW VWMoj
StacjaBenzynowa(jakisSamochod);
StacjaBenzynowa(VWMoj); // niejawna konwersja
StacjaBenzynowa((samochod &)VWMoj); // jawna konwersja
}
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
39
Co jest możliwe a co nie?
pochodna obj
podstawowa obj
pochodna *wsk
podstawowa *wsk
pochodna **wskwsk
podstawowa **wskwsk
pochodna *tabWsk[]
podstawowa *tabWsk[]
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
40
Wirtualne klasy podstawowe
• Często mówi się „wirtualna klasa podstawowa”,
ale to tylko dziedziczenie jest wirtualne
B
C
D
A
A
B
C
D
A
class A {...};
class B: public virtual A {...};
class C: public virtual A {...};
class D: public B, public C {...};
class B: private virtual A {...};
class C: public virtual A {...};
class A {...};
class B: public A {...};
class C: public A {...};
class D: public B, public C {...};
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
41
Wirtualne klasy podstawowe
• Wystarczy, że jedno dziedziczenie jest
publiczne aby klasa A była dostępna publicznie
• Ciekawe jest w jaki sposób wywoływane są
konstruktory klas dziedziczonych wirtualnie
class A {...};
class B: private virtual A {...};
class C: public virtual A {...};
class D: public B, public C {...};
Funkcje wirtualne
Polimorfizm
Funkcje czysto wirtualne. Klasy abstrakcyjne
Wirtualny destruktor
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
43
Wprowadzenie
• Kilka klas ma jednego przodka:
– Janek, Kasia i Fiona mają tego samego ojca
– Mercedes, Fiat i Volvo są to samochody
– Trąbka, bęben i fortepian to instrumenty
trąbka
fortepian
bęben
instrument
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
44
Przykład
class instrument { // Klasa podstawowa
public:
void virtual wydaj_dzwiek() {
cout << "Nieokreslony brzdek !\n" ;
}
};
/////////////////////////////////////////////////////////
class trabka : public instrument {
public:
void wydaj_dzwiek()
{
cout << "Tra-ta-ta !\n" ;
}
// ...
} ;
/////////////////////////////////////////////////////////
class beben: public instrument {
public:
void wydaj_dzwiek() {
cout << "Bum-bum-bum !\n" ;
}
// ...
} ;
/////////////////////////////////////////////////////////
class fortepian : public instrument {
public:
void wydaj_dzwiek() {
cout << "Pilm-plim-plim !\n" ;
}
// ...
} ;
/////////////////////////////////////////////////////////
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
45
Przykład c.d.
void muzyk(instrument & powierzony_instrument) {
powierzony_instrument.wydaj_dzwiek(); }
/*******************************************************/
main()
{ //
Obiekty
instrument jakis_instrument ;
trabka zlota_trabka ;
fortepian steinway_giseli ;
beben moj_werbel ;
jakis_instrument.wydaj_dzwiek();
zlota_trabka.wydaj_dzwiek();
steinway_giseli.wydaj_dzwiek();
moj_werbel.wydaj_dzwiek();
instrument *wskinstr ;
// ustawianie wskaźnika
wskinstr = & jakis_instrument ;
wskinstr-> wydaj_dzwiek() ;
wskinstr = &zlota_trabka ;
wskinstr-> wydaj_dzwiek() ;
wskinstr = &steinway_giseli ;
wskinstr-> wydaj_dzwiek() ;
wskinstr = &moj_werbel ;
wskinstr-> wydaj_dzwiek() ;
// poprzez referencję
muzyk(jakis_instrument);
muzyk(zlota_trabka);
muzyk(steinway_giseli) ;
muzyk(moj_werbel) ;
}
Tra-ta-ta
Tra-ta-ta
Bum-bum-bum
Pilm-plim-plim
Pilm-plim-plim
Bum-bum-bum
Nieokreślony brzdęk
Nieokreślony brzdęk
Tra-ta-ta
Bum-bum-bum
Pilm-plim-plim
Nieokreślony brzdęk
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
46
Wielopostaciowość
• Dzięki funkcji wirtualnej (modyfikacji w deklaracji
funkcji słowem virtual)
• instrukcja zapisana jako
Referencja.wydaj_dźwięk();
• interpretowana jest w trakcie działania programu jako:
Referencja.instrument::wydaj_dźwięk();
Referencja.trabka::wydaj_dźwięk();
Referencja.fortepian::wydaj_dźwięk();
• Identyczny efekt osiągamy dla wskaźników
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
47
Polimorfizm
• Taka wielość form określana jest mianem
polimorfizm czyli inaczej wielopostaciowość.
Ale to nie funkcja wirtualna jest polimorficzna
tylko takie jej wywołanie wykazuje
polimorfizm lub zachowuje się polimorficznie.
• Dzieje się to kosztem rozmiaru wynikowego
programu oraz jego szybkości.
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
48
Polimorfizm c.d.
• Wczesne i późne wiązanie
– Wczesne wiązanie to wiązanie na etapie kompilacji
– Późne wiązanie to wiązanie na etapie wykonywania
• Polimorfizm nie jest
przeciążeniem/przeładowaniem
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
49
Funkcja wirtualna
• Funkcja składowa jest wirtualna wtedy, gdy w
definicji klasy przy jej deklaracji stoi słowo virtual,
lub gdy w jednej z klas podstawowych tej klasy
identyczna funkcja zadeklarowana jest jako virtual.
• Klasa pochodna nie musi definiować swojej wersji
funkcji wirtualnej.
• Funkcja wirtualna
– nie może być static.
– Może być zaprzyjaźniona z jakąś inną klasą, ale nie będzie
dla niej wirtualną
– Może być inline (ale czasem będzie to ignorowane)
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
50
Klasy abstrakcyjne
• To klasa, która nie reprezentuje żadnego
konkretnego obiektu i służy tylko do
dziedziczenia
• Skoro klasa abstrakcyjna nie reprezentuje
żadnego obiektu, więc nie będziemy tworzyć na
jej podstawie żadnego obiektu. Jeśli klasa ta
zawiera funkcję wirtualną to funkcja ta nie
będzie realizowana i można ją zdefiniować
następująco
Typ virtual funkcja() = 0 ;
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
51
Funkcja czysto wirtualna
• Funkcję taką nazywamy czysto wirtualną (ang. pure
virtual). Oznacza to, że tej wersji funkcji wirtualnej nie
ma się nigdy wykonywać.
• Zdefiniowanie w jakieś klasie funkcji czysto wirtualnej
powoduje, że kompilator nie pozwoli na stworzenie
obiektu tej klasy. Klasa ta jest abstrakcyjna nie tylko
dla nas ale i dla kompilatora.
• Brak definicji funkcji w klasie pochodnej powoduje
odziedziczenie funkcji czysto wirtualnej ze wszystkimi
tego skutkami (brak możliwości tworzenia obiektów).
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
52
Wirtualny destruktor
• Mimo iż wyda się to dziwne, to jednak
wirtualność destruktora jest możliwa
• Można uzasadnić to tym, że
– w klasie jest tylko jeden
– nigdy nie ma parametrów
– można przyjąć, że nazwa się zawsze „destruktor”
• Wirtualność destruktora jest wyjątkiem.
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
53
Po co wirtualny destruktor?
• Wprowadzenie funkcji wirtualnych oznacza, że chce się
korzystać polimorfizmu
• Polimorfizm to możliwość odnoszenia się do obiektów klas
pochodnych poprzez wskaźnik (lub referencję) do klasy
bazowej.
• Poza wywołaniem metody wirtualnej często zachodzi też
potrzeba zniszczenia takiego obiektu (wskazywanego przez
wskaźnik do klasy bazowej).
• Jeśli mówimy „umyj ten pojazd” mając na myśli konkretny
obiekt (np. Volvo), to możemy też powiedzieć „zniszcz ten
pojazd” i to też odnosi się do tego konkretnego, który
wskazujemy.
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
54
Po co wirtualny destruktor? Przykład
• Przy tworzeniu różnych
obiektów wywoływane
są różne konstruktory
• Różne obiekty
wskazywane są przez
taki sam wskaźnik
• Wywołanie różnych
funkcji przez taki sam
wskaźnik
• Wywołanie różnych
destruktorów przez taki
sam wskaźnik
main()
{
//Jednakowe wskaźniki ale
// różne konstruktory
instrum *pierwszy = new skrzypce;
instrum *drugi = new gitara; //
instrum *trzeci = new gwizdek ;
//Różne funkcje
pierwszy->wydaj_dzwiek() ;
drugi ->wydaj_dzwiek() ;
trzeci ->wydaj_dzwiek() ;
//Jednakowe wskaźniki ale
//muszą być różne destruktory
delete pierwszy ;
delete drugi ;
delete trzeci ;
}
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
55
Jak uzyskać wirtualny destruktor?
• Tę sprawę załatwia postawienie słowa virtual przed
nazwą destruktora
• Dzięki temu destruktor będzie wywoływany
inteligentnie, co spowoduje, że niszczony będzie
właściwy obiekt w całości (a nie tylko jego część
odpowiadająca klasie podstawowej).
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
56
Zasada
•
Jeśli klasa deklaruje jedną ze swych
funkcji jako virtual,
wówczas jej destruktor deklarujemy
również jako virtual.
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
57
Wirtualny destruktor - przykład
class instrum {
public :
void virtual wydaj_dzwiek() {
cout << "cisza" ;
}
virtual ~instrum() { // ----wirtualny destruktor
cout << "Destruktor instrumentu \n" ;
}
} ;
/////////////////////////////////////////////////////////
class skrzypce : public instrum { //
char *nazwa ;
public :
skrzypce(char *firma) {// - konstruktor------------
nazwa = new char[strlen(firma) + 1] ; //
strcpy(nazwa, firma) ;
}
~skrzypce() { //- destruktor (wirtualny) -------------
cout << "Destruktor skrzypiec + " ;
delete nazwa ; //
}
// ----------------------------
void wydaj_dzwiek()
{
cout << "tirli-tirli ("
<< nazwa << ")\n" ;
}
} ;
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
58
Wirtualny destruktor - przykład
class gwizdek :public instrum { //
public :
void wydaj_dzwiek() {
cout << "fiu-fiu \n" ;
}
} ;
/////////////////////////////////////////////////////////
class gitara : public instrum {
char *nazwa ;
public :
gitara(char *firma) {// - konstruktor-----------------
nazwa = new char[strlen(firma) + 1] ; //
strcpy(nazwa, firma) ;
}
~gitara() { //- destruktor (wirtualny) ---------------
cout << "Destruktor gitary + " ;
delete nazwa ; //
}
// --------------------------
void wydaj_dzwiek()
{
cout << "brzdek-brzdek ("
<< nazwa << ")\n" ;
}
} ;
K
ATEDRA
M
ETROLOGII
E
LEKTRONICZNEJ
I F
OTONICZNEJ
opracowanie Zbigniew Świerczyński
59
Wirtualny destruktor - przykład
main()
{
cout << "Definiujemy w zapasie pamięci\n"
"trzy instrumenty orkiestry\n " ;
instrum *pierwszy = new skrzypce("Stradivarius") ;
instrum *drugi = new gitara("Ramirez") ;
instrum *trzeci = new gwizdek ; //
cout << "Gramy polimorficznie ! \n" ;
pierwszy->wydaj_dzwiek() ; //
drugi ->wydaj_dzwiek() ;
trzeci ->wydaj_dzwiek() ;
cout << "\nKoncert sie skonczyl, "
"likwidujemy instrumenty\n\n" ;
delete pierwszy ; //
delete drugi ;
delete trzeci ;
}
/*******************************************************/