Download as ppt, pdf, or txt
Download as ppt, pdf, or txt
You are on page 1of 24

Virtuelne funkcije i

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

Objekt nastao iz izvedene


klase

Ponaanje objekta izvedene klase odredjuju


pre svega metode (funkcije bazne klase
koje je on nasledio).
Objekat izvedene klase satoji se od atributa
i ponaanja bazne klase koji dopunjuju
atributi i ponaanja izvedene klase. Kada se
prosledjuje objekat izvedene klase (preko
pokazivaa ili reference) prosledjuje se
adresa baznog tipa to se naziva upcasting.
Prilikom nasledjivanja, stvoreni objekat se
sastoji iz bazne klase (na vrhu) I izvedene
klase ispod.

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.

Virtuelna lanica klase


Da bi se kreirala virtuelna funkcija
lanica klase, tada prilikom deklaracije
funkcije upotrebljavamo slubenu re
virtual.
Ako je funkcija deklarisana kao
virtuelna u baznoj klasi, tada je ona
virtuelna i u izvedenoj klasi.
Redefinicija virtuelne funkcije u
izvedenoj klasi se naziva overriding.
Sve izvedene klase koje imaju identian
potpis kao virtuelne funkcije bazne
klase koristie virtuelni mehanizam.

Late binding sa virtuelnom


funkcijom Av,Av,Av

#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);
}

Mehanizam late binding


Kljuna re late binding naznaava kompajleru da ne vri
rano uvrtavanje (early binding). Umesto toga on treba da
instalira sve mehanizme za kasno uvrtavanje (late
binding).
To znai da ako pozovemo funkciju glas() kroz adresu
objekta Ben izvedene klase Pas, ona treba da pozove
odgovarajuu funkciju oglasavanje().
Da bi se ovo postiglo tipian kompajler kreira tabelu koja
se naziva v-tabela za svaku klasu koja sadri virtuelne
funkcije.
Kompajler smeta adrese virtuelnih funkcija odredjene
klase u v-tabelu. U svaku klasu sa virtuelnim funkcijama
smeta se pokaziva nazvan vpointer (VPTR), koji pokazuje
na v-tabelu za taj objekat.
Kada se poziva virtuelna funkcija kroz pokaziva bazne

Klasa i virtuelne funkcije


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.

#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;}
};

17. class Virtuelna2


18. {

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";}
};

class Mastif : public Pas


{
public:
void Oglasavanje(Raspolozenje) const { cout << "Ja lajem kao mastif...\n";}
char* Sta_sam_ja() const {return "Mastif";}
};
class Vucjak : public Pas
{
public:
void Oglasavanje(Raspolozenje) const { cout << "Ja lajem kao vucjak...\n";}
char* Sta_sam_ja() const {return "Vucjak";}
void disati(int) { }
};
void glas(Sisari& i) {
i.Oglasavanje(Gladan);
}
void funkcija(Sisari& i) {i.disati(1);}
Sisari* A[] = {new Pas, new Macka, new Mastif, new Vucjak,};

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

Polje A[ ] pokazivaa na objekte


klase Sisari sadr`I adrese etiri
objekta.
Klasa Sisari ima tri virtuelne
funkcije, tako da se za svaki objekat
izvedene klase pravi v-tabela.
U tu tabelu se smetaju adrese svih
funkcija koje su deklarisane kao
virtuelne u toj klasi ili u baznoj klasi.

Kreiranje objekata rano i kasno


uvrtavanje (binding)

#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();
}

Apstraktne klase i potpuno


virtuelne funkcije
Kada elimo da neka klasa bude model na
osnovu kojeg izvodimo druge klase, tada tu
klasu moemo definisati kao apstraktnu
klasu.
Apstraktna klasa sadri barem jedan metod
(funkciju) koja je potpuno virtuelna.
Potpuno virtuelna funkcija sadri virtual
kljunu re i zavrava se sa =0.
Apstraktna klasa koja sadri potpuno
virtuelne funkcije, primorava izvedene klase,
da definiu implementaciju za te funkcije.

Sisari

virtual void Oglasavanje()


virtual char* Sta_sam_ja()
virtual void disati()

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

Definicija apstraktne klase


class Sisari

{
public:
virtual void Oglasavanje() const = 0;
virtual char* Sta_sam_ja() const = 0;
virtual void disati(int) = 0;
};

Potpuno virtuelne funkcije


Korisne su jer eksplicitno naznaavaju
apstraktnost klase kako korisniku tako i
kompajleru. Time je i njihova primena odredjena.
Potpuno virtuelne funkcije onemoguavaju
apstraktnu klasu da bude prosledjena funkciji po
vrednosti.
Potpuno virtuelne funkcije onemoguavaju i
object slicing.

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?

You might also like