background image

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 ń  

background image

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 TCGolfKlasySpublic 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ą 
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 TCGolfKlasySpublic 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();

}

background image

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 protected)

– które składowe chcemy przekazać klasie 

dziedziczącej (public protected) a które nie 
(private)

• Tworząc klasę pochodną dodatkowo 

decydujemy poprzez specyfikator dostępu 
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;

}

background image

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.

background image

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

background image

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

background image

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

 

 

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

background image

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 

background image

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 ;

}

background image

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 ;

}
/*******************************************************/