Professional Documents
Culture Documents
Poli Morfi Zam
Poli Morfi Zam
polimorfizam
Polimorfizam implementiran u programu C++
pomou virtuelnih funkcija je trei kamen temeljac
objektno orijentisanog programiranja.
On obezbedjuje drugu dimenziju odvajanja
interfejsa od implementacije.
Enkapsulacija omoguava kreiranje novih tipova
podataka kombinujui karakteristike i ponaanja
klase.
Kontrola pristupa odvaja interfejs klase od njene
implementacije, ime detalji klase mogu biti privatni
i nedostupni za okolne klase.
Virtuelne funkcije omoguavaju stvaranje mnotva
razliitih tipova na osnovu definicije jednog tipa.
Nasledjivanje i ponaanje
Videli smo da mehanizmom nasledjivanja stvoreni
objekt poseduje ponaanje bazne klase kao i
izvedene klase kojoj i pripada.
Izvedeni objekat pripada novom tipu, odnosno
novoj klasi koja je izvedena iz bazne klase. To znai
da je izvedeni objekat vrsta baznog objekta.
Svi objekti izvedene klase ponaaju se na isti
nain, na osnovu definicija funkcija lanica bazne I
izvedene klase.
Virtuelne funkcije definisane u baznoj klasi,
omoguavaju da se objekti nastali iz izvedenih
klasa iste bazne klase (slini objekti) ponaaju
razliito.
Razlike se ispoljavaju u ponaanju opisanom u
funkcijama bazne klase kroz virtuelne funkcije.
Upcasting
Zvuk sisara
#include <iostream.h>
enum Raspolozenje {Gladan, Besan, Ljut, Zadovoljan};
class Sisari
{
public:
void Oglasavanje(Raspolozenje) const { cout << "Zvuk sisara...\n";}
};
class Pas : public Sisari
{
public:
void Oglasavanje(Raspolozenje) const { cout << "Av, Av, Av...\n";}
};
void glas(Sisari& i) {
i.Oglasavanje(Gladan);
}
void main()
{
Pas Ben;
glas(Ben);
Problem
Ogleda se u injenici da prosledjeni
objekat Ben izvedene klase Pas se
ne oglasava kao Pas ve kao i
svaki objekat bazne klase Sisari.
Tu nastaje problem nazvan
upcasting.
Kako obavestiti C++ kompajler da
je funkciji glas() prosledjen objekat
izvedene klase Pas (po referenci) a
ne objekat bazne klase Sisari.
Virtuelne funkcije
Problem prethodnog primera nastaje
zbog injenice da kompajler nije u stanju
da pozove pravu funkciju onda kada
poziva funkciju klase Sisari. Da li je to
funkcija koja pripada baznoj klasi ili
izvedenoj klasi.
To se naziva problemom kompajlera
early binding ili rano uvrtavanje.
Problem se reava kasnim uvrtavanjem
late binding to znai da binding
nastaje za vreme izvrenja programa na
osnovu tipa objekta a ne klase kojoj taj
objekat pripada ili iz koje je izveden.
Dynamic binding
Kada jezik poseduje osobinu late binding, mora
da postoji mehanizam odrdjivanja tipa objekta
koji za vreme izvravanja programa (runtime)
poziva odgovarajuu funkciju lanicu.
Mehanizam late binding varira od jezika do
jezika, ali mora da postoji neka vrsta informacije
ubaena u objekt koja to obezbedjuje.
Da bi se postigo mehanizam late binding tada
C++ zahteva slubenu re virtual kada se
deklarie funkcija u baznoj klasi.
Mehanizam late binding nastaje samo kod
virtuelnih funkcija, i samo kada se koristi adresa
bazne klase kod koje virtuelna funkcija postoji.
#include <iostream.h>
enum Raspolozenje {Gladan, Besan, Ljut, Zadovoljan};
class Sisari
{
public:
virtual void Oglasavanje(Raspolozenje) const { cout << "Zvuk sisara...\n";}
};
class Pas : public Sisari
{
public:
void Oglasavanje(Raspolozenje) const { cout << "Av, Av, Av...\n";}
};
void glas(Sisari& i) {
i.Oglasavanje(Gladan);
}
void main()
{
Pas Ben;
glas(Ben);
}
#include <iostream.h>
enum Rasa {Terijer,Labrador,Ovcar,Snaucer,Bul_Mastif};
class Obicna
{
int a;
public:
void x() const {}
int i() const {return 1;}
};
class Virtuelna1
{
int a;
public:
virtual void x() const {}
int i() const {return 1;}
};
int a;
20. public:
21.
virtual void x() const {}
22.
virtual int i() const {return 1;}
19.
int: 4
Obicna: 4
Virtuelna1: 8
Virtuelna2: 8
23. };
17. void main()
18. {
19. cout << "int: " << sizeof(int) << endl;
20. cout << "Obicna: " << sizeof(Obicna) << endl;
21. cout << "Virtuelna1: " << sizeof(Virtuelna1) << endl;
22. cout << "Virtuelna2: " << sizeof(Virtuelna2) << endl;
23. }
Veliina objekata
Kao to vidimo, veliina objekta klase Obicna je
veliina jednog celobrojnog podatka int.
Sa jednom virtuelnom funkcijom, imamo
prethodnu veliinu objekta (4 bajta za int) plus
4 bajta za veliinu void pointera. Na objekat
klase Virtuelna1 ima veliinu 8 bajta.
Sa dve virtuelne funkcije objekat klase
Virtuelna2 ima takodje istu veliinu od 8 bajta.
To je zbog toga to VPTR ukazuje na tabelu sa
adresama funkcija. Potrebna je samo jedna
adresa za jednu klasu, jer tabela na koju pointer
ukazuje sadri sve adrese virtuelnih funkcija.
#include <iostream.h>
enum Raspolozenje {Gladan, Besan, Ljut, Zadovoljan};
class Sisari
{
public:
virtual void Oglasavanje(Raspolozenje) const { cout << "Zvuk sisara...\n";}
virtual char* Sta_sam_ja() const {return "Sisar";}
virtual void disati(int) { }
};
class Pas : public Sisari
{
public:
void Oglasavanje(Raspolozenje) const { cout << "Av, Av, Av...\n";}
char* Sta_sam_ja() const {return "Pas";}
void disati(int) { }
};
class Macka : public Sisari
{
public:
void Oglasavanje(Raspolozenje) const { cout << "Mjau, Mjau, Mjau...\n";}
char* Sta_sam_ja() const {return "Macka";}
};
void main()
{
Pas Petronije;
Macka Beki;
Mastif Ben;
Vucjak Laki;
glas(Petronije);
glas(Beki);
glas(Ben);
glas(Laki);
funkcija(Ben);
Objekti:
Pas objekt
Niz pokazivaca
A[ ] na objekte
klase Sisari
vptr
&Pas::Oglasavanje
&Pas::Sta_sam_ja
&Pas::disati
&Macka::Oglasavanje
Macka objekt
vptr
&Macka::Sta_sam_ja
&Macka::disati
&Mastif::Oglasavanje
Mastif objekt
vptr
Vucjak objekt
vptr
&Mastif::Sta_sam_ja
&Mastif::disati
&Vucjak::Oglasavanje
&Vucjak::Sta_sam_ja
&Vucjak::disati
Virtuelne funkcije i
pokazivai
#include <iostream.h>
class Sisari
{
public:
virtual void Oglasavanje() const { cout << "Zvuk sisara...\n";}
};
class Pas : public Sisari
{
public:
void Oglasavanje() const { cout << "Av, Av, Av...\n";}
};
void main() {
Pas Ben;
Sisari *p1= &Ben;
Sisari &p2=Ben;
Sisari p3;
cout << "p1->Oglasavanje() = "; p1->Oglasavanje();
cout << "p2.Oglasavanje() = "; p2.Oglasavanje();
cout << "p3.Oglasavanje() = "; p3.Oglasavanje();
}
Sisari
Pas
Macka
void Oglasavanje()
char* Sta_sam_ja()
void disati()
void Oglasavanje()
char* Sta_sam_ja()
void disati()
Mastif
Vucjak
void Oglasavanje()
char* Sta_sam_ja()
void Oglasavanje()
char* Sta_sam_ja()
{
public:
virtual void Oglasavanje() const = 0;
virtual char* Sta_sam_ja() const = 0;
virtual void disati(int) = 0;
};
Nasledjivanje i v-table
Nasledjivanjem mo`emo da predefini{emo
neke virtuelne funkcije, odnosno da na njima
primenimo mehnizam override.
Kompajler kreira novu v-table za novu klasu, I
insertuje nove adrese funkcija koriste}I adrese
funkcija bazne klase za svaku funkciju kod koje
nije primenjen mehanizam overrride.
Za svaki kreirani objekt postoji pun set adresa
funkcija u v-table, tako da nije mogu}e pozvati
se na adresu koja nije u tabeli.
[ta se de{ava kada nasledimo u izvedenoj
klasi virtuelne funkcije I dodamo jo{ neke?