Download as pdf or txt
Download as pdf or txt
You are on page 1of 15

Objektno orijentisano Pojam specijalizacije opšteg

programiranje 1  Jedna klasa objekata (B) može da bude podvrsta neke druge klase (A)
 Klasa B je specijalni slučaj (a-kind-of) klase A
 Objekat klase B je (is-a) i objekat klase A
 Primeri:
– Sisari su klasa koja je okarakterisana načinom ishrane mladunaca
 Mesožderi su vrsta sisara koja se hrani mesom
 Biljojedi su vrsta sisara koja se hrani biljkama

Izvođenje klasa – Geometrijske figure u ravni su klasa koja ima koordinate težišta
 Krug je vrsta figure u ravni koja je okarakterisana dužinom poluprečnika
 Kvadrat je vrsta figure u ravni koja je okarakterisana dužinom stranice
– Vozila su klasa predmeta koji služe za prevoz
 Teretna vozila su vrsta vozila namenja prevozu stvari i životinja
 Putnička vozila su vrsta vozila namenjena prevozu ljudi

2 Izvođenje klasa 27.11.2023.

Izvođenje i nasleđivanje (1) Izvođenje i nasleđivanje (2)


 Objekti klase B imaju sve osobine klase A i još neke specifične  Ako je klasa B specijalni slučaj (izvedena iz) klase A:
 Specijalnija klasa B se izvodi iz generalnije (opštije) klase A – A je osnovna (base), a B izvedena (derived)
 Klasa B nasleđuje karakteristike klase A: – A je natklasa (superclass), a B potklasa (subclass)
– strukturne karakteristike (atribute) – A je roditelj (parent), a B dete (child)
– karakteristike ponašanja (metode)  Objektni jezici koji podržavaju nasleđivanje
 Pored nasleđenih ima i sopstvene (specifične) članove – objektno-orijentisani (engl. Object-Oriented)
 Relacija između klasa B i A se naziva:  Objektni jezici koji ne podržavaju nasleđivanje
– nasleđivanje (engl. inheritance) – objektno-zasnovani (engl. Object-Based)
– generalizacija/specijalizacija  Pri izvođenju nije potrebno vršiti
 Relacija nasleđivanja se prikazuje (usmerenim acikličnim) grafom – nikakve izmene postojeće klase
(UML notacija): – čak ni njeno ponovno prevođenje
A B

3 Izvođenje klasa 27.11.2023. 4 Izvođenje klasa 27.11.2023.


Definisanje izvedene klase Konačna klasa
 Izvedena klasa se definiše na sledeći način:  Modifikator final – konačna klasa
class ime a zatim pre znaka {: – iz klase ne može da se dalje izvodi
public – list u stablu izvođenja
ime osnovne
modifikator : virtual
klase  Reč nije rezervisana, može da se pojavi i kao identifikator
protected – ne preporučuje se
private  Primer:
class A { ... };
, class B { ... };
 Ako postoji samo jedna osnovna klasa – jednostruko izvođenje class C: A { ... };
 Ako postoji više osnovnih klasa u listi – višestruko izvođenje class D: A, B { ... };
class E final: D { ... };
 Izvođenje može da bude i u više koraka:
class F: E {...}; // ! GRESKA: klasa E je konacna
– izvedena klasa može da bude osnovna klasa za sledeće izvođenje
int final = 55; // final kao ime globalne promenljive
– takvo izvođenje se ne naziva višestrukim

5 Izvođenje klasa 27.11.2023. 6 Izvođenje klasa 27.11.2023.

Primer izvođenja Pristup nasleđenim članovima


 Objekti izvedene klase sadrže:  Preko objekta izvedene klase, javnim članovima osnovne klase
– bezimeni podobjekat osnovne klase koji sadrži nasleđene članove se pristupa isto kao i članovima izvedene klase
– specifične članove navedene u definiciji izvedene klase  Primer:
class Osnovna {
int i; // atribut osnovne klase int main () {
public: Osnovna b;
void f(); // metod osnovne klase Izvedena d;
};
b.f();
b.g(); // ! GRESKA: g() je metod izvedene klase,
class Izvedena : public Osnovna { // a b je objekat osnovne
int j; // atribut izvedene klase
d.f(); // objekat izvedene klase d ima i metod f(),
public: // nasledjen je: int i; d.g(); // i metod g()
void g(); // metod izvedene klase
}; // nasledjen je: void f(); }

7 Izvođenje klasa 27.11.2023. 8 Izvođenje klasa 27.11.2023.


Konstruktor, destruktor, operator= Prava pristupa
 Izvedena klasa ne nasleđuje iz osnovne klase:  Članovi izvedene klase
– imaju prava pristupa javnim članovima osnovne klase
– konstruktore
– nemaju prava pristupa privatnim članovima osnovne klase
– destruktor
 Posebno pravo pristupa - zaštićeno
– operator=  Labela protected: (u osnovnoj klasi)
 Kao i za osnovne klase, postoje ugrađeni (implicitno definisani): – označava deo klase kojem imaju pravo pristupa metodi te i izvedenih klasa
– podrazumevani konstruktor koji ima prazno telo – članovi u ovoj sekciji se nazivaju zaštićenim (protected)
– kopirajući konstruktor koji kopira član po član (plitka kopija)  Zaštićenim članovima može da se pristupi iz metoda izvedene klase
– ali samo kao nasleđenim članovima, a ne preko objekta osnovne klase
– premeštajući konstruktor koji premešta član po član
– destruktor koji ima prazno telo
– kopirajući operator= koji vrši dodelu kopiranjem, član po član
– premeštajući operator= koji vrši dodelu premeštanjem, član po član

9 Izvođenje klasa 27.11.2023. 10 Izvođenje klasa 27.11.2023.

Primer prava pristupa Načini izvođenja


class Osnovna {  Način izvođenja određuje modifikator ispred imena osnovne klase
int pb;
protected: int zb;  Izvođenje može da bude:
public: int jb;
}; – javno (public)
class Izvedena : public Osnovna { – zaštićeno (protected)
public:
void m(int x) { – privatno (private)
jb=zb=x;// moze da pristupi javnom i zasticenom clanu,  Kaže se i za osnovnu klasu da je javna, zaštićena ili privatna
pb=x; // ! GRESKA: privatnom clanu ne moze da pristupi
Osnovna o;  Podrazumevano (bez modifikatora) izvođenje je privatno
o.zb=x; // ! GRESKA: ne moze preko objekta osnovne klase
}  Način izvođenja određuje nasleđivanje prava pristupa:
}; – određuje se stepen kontrole pristupa članovima osnovne klase
void f() {
Osnovna b; preko objekta izvedene klase
b.pb=1; // ! GRESKA: privatan clan – objekat izvedene klase - može da bude podobjekat u narednom izvođenju
b.zb=1; // ! GRESKA: zasticeni clan
b.jb=1; // u redu – pristup javnom clanu – ne utiče na pravo pristupa iz metoda dotične izvedene klase
}

11 Izvođenje klasa 27.11.2023. 12 Izvođenje klasa 27.11.2023.


Implicitna promena prava pristupa Eksplicitna promena prava pristupa
 Može i eksplicitno da se promeni pravo pristupa nasleđenom članu
– može da se uveća u odnosu na originalno (zaštićeni postaje javni)
– može da se restaurira na originalno koje je umanjeno načinom izvođenja
Član osnovne klase – može delimično da se restaurira
Izvođenje javni zaštićen privatan – može i da se umanji u odnosu na originalno
 Ne može da se promeni pravo pristupa privatnom članu osnovne klase
javno javni zaštićen privatan – jer privatnom članu ne može da se pristupi iz izvedene klase
 Postiže se uvozom člana u odgovarajuću sekciju izvedene klase
– koristi se deklaracija using klasa::clan
zaštićeno zaštićen zaštićen privatan
– može samo za članove za koje postoji pravo pristupa (ne za privatne u osnovnoj)
– na taj način može da se napravi izuzetak za pojedini član
od promene prava pristupa usled načina izvođenja
privatno privatan privatan privatan
 Može (uz upozorenje) i bez reči using, samo klasa::clan
– da bi se obezbedila kompatibilnost sa ranijm standardom
– zastarelo, izbegavati u novim programima

13 Izvođenje klasa 27.11.2023. 14 Izvođenje klasa 27.11.2023.

Primer Razlika privatnog i javnog izvođenja


class O { class IPI: public PI{  Između privatnog i javnog izvođenja – suštinska razlika
int pb; public:
protected: IPI(){  Javno izvođenje realizuje koncept nasleđivanja
int z1b,z2b; j1b=0; // OK – relacija "B je vrsta A" (a-kind-of)
public: j2b=0; // OK – izvedena klasa B zadržava ugovor (interfejs) roditeljske klase A
int j1b,j2b; z1b=0; // OK – objekat izvedene klase može da zameni objekat osnovne
}; z2b=0; //! GRESKA
class PI : O { } – to je jedino pravo izvođenje
public: }  Privatno izvođenje realizuje koncept sadržanja
using O::j1b; // vracanje originalnog prava pristupa – relacija "A je deo B" (a-part-of)
using O::z1b; // povecanje prava pristupa – semantički slično sa slučajem kada B sadrži atribut tipa A
PI () { z2b=1; } // moze da se pristupi iz izvedene klase
protected: – nije nasleđen ugovor roditelja - javni (i zaštićeni) članovi postali su privatni
using O::j2b; // delimicno vracanje prava pristupa – nasleđena je samo implementacija roditeljske klase A
}; – objekat izvedene klase ne može da zameni objekat osnovne
int main () {
PI pi;  Zaštićeno izvođenje
pi.j1b=0; // javni clan klase PI – unutar izvedene klase iz zaštićeno izvedene – koncept nasleđivanja
pi.j2b=0; // ! GRESKA: zasticen clan klase PI – izvan izvedene klase – koncept sadržanja
pi.z1b=0; // javni clan klase PI
pi.z2b=0; // ! GRESKA: privatni clan klase PI
15 } Izvođenje klasa 27.11.2023. 16 Izvođenje klasa 27.11.2023.
Sakrivanje članova Uvoz članova
 Redefinisanje identifikatora člana osnovne klase u izvedenoj klasi  U izvedenoj klasi svi sakriveni metodi treba da budu
– sakriva identifikator iz osnovne – redefinisani ili
 U metodima izvedene klase sakrivenom članu iz osnovne klase se pristupa: – restaurirani (otkriveni)
ime_osnovne_klase::clan  Nije dobro da izvedena klasa sadrži samo neke metode iz osnovne klase
 U izvedenoj klasi ne može da se (podrazumevano) računa – ne radi se o pravom nasleđivanju
sa uobičajenim preklapanjem imena metoda iz osnovne klase – korisnik izvedene klase očekuje isti (ili prošireni) ugovor osnovne klase
 Ako se u izvedenoj klasi napiše neki metod  Uvoz – otkrivaju se svi sakriveni metodi sa datim imenom
koji ima isto ime kao metod iz osnovne klase using osnovna_klasa::ime_metoda
– takav metod sakriva sve nasleđene metode sa istim imenom – neće biti vidljivi samo metodi osnovne sa istim potpisom kao u izvedenoj klasi
– iz metoda izvedene klase ne može da se direktno pristupi sakrivenim metodima  Ne može da se restaurira vidljivost pojedinačnog metoda osnovne klase
 Nasleđeni sakriveni metodi
– ne mogu da se pozivaju za objekat izvedene klase
– osim preko pokazivača na osnovnu klasu koji pokazuje na objekat izvedene klase

17 Izvođenje klasa 27.11.2023. 18 Izvođenje klasa 27.11.2023.

Sakrivanje i uvoz članova – primer Uvoz konstruktora


class O { public: m2(); // O::m2();  Konstruktori osnovne klase se ne nasleđuju
int a=1; m2(x); // I::m2(int)
void m1();
O::m2(y);// O::m2(int);
 Može da se izvrši uvoz (svih) konstruktora osnovne klase
void m1(int);
void m2(); } using ime_klase::ime_klase
void m2(int); }; – na taj način se generišu konstruktori izvedene sa istim potpisima
}; void f(){
class I: public O { – generisani konstruktori izvedene klase imaju prazno telo
public: I i;
– oni samo implicitno pozivaju konstruktore osnovne sa istim potpisom
using O::m2; int p=i.a; // I::a
int a=2; int q=i.O::a; // O::a  Primer:
void m1(int); i.m1(); //! GRESKA
void m2(int); class A { public: A(int i){…} };
void m(){ i.O::m1(); // O::m1()
class B: public A {public: using A::A; }
int x=a; // x=I::a i.m1(p); // I::m1(int)
int y=O::a;// x=O::a i.O::m1(q); // O::m1(int) B b(1); // poziva se A::A(1)
m1(); // ! GRESKA i.m2(); // O::m2()
m1(x); // I::m1(int)
O::m1(); // O::m1(); i.m2(p); // I::m2(int)
O::m1(y); // O::m1(int); i.O::m2(q); // O::m2(int)
}
19 Izvođenje klasa 27.11.2023. 20 Izvođenje klasa 27.11.2023.
Izvođenje struktura Konstruktori i destruktori
 Strukture su ravnopravne sa klasama  Prilikom kreiranja objekta izvedene klase,
– moguće je izvođenje strukture iz klase ili iz strukture
 podrazumeva se javno poziva se konstruktor te klase, ali i konstruktor osnovne klase
– moguće je i izvođenje klase iz strukture  Prilikom uništavanja objekta izvedene klase,
 podrazumeva se privatno
poziva se destruktor te klase, ali i destruktor osnovne
 Primer:
struct OS { };  Prenos parametara konstruktoru osnovne klase:
class OK { }; – u listi inicijalizatora konstruktora izvedene klase
class IKS: OS{ }; // privatno izvodjenje
može da se navede i inicijalizator osnovne klase
class IKK: OK{ }; // privatno izvodjenje
struct ISK: OK{ }; // javno izvodjenje – inicijalizator osnovne klase se sastoji od imena osnovne klase
struct ISS: OS{ }; // javno izvodjenje i argumenata poziva konstruktora osnovne klase
 Nije moguće eksplicitna inicijalizacija nasleđenih atributa
 Unije (union) ne mogu da se izvode, niti iz unije može da se izvodi

21 Izvođenje klasa 27.11.2023. 22 Izvođenje klasa 27.11.2023.

Primer inicijalizacije osnovne klase Redosled konstrukcije


class Osnovna {  Pri kreiranju objekta izvedene klase redosled akcija je sledeći:
int bi; – inicijalizuje se podobjekat osnovne klase
public:  pozivom odgovarajućeg konstruktora osnovne klase
Osnovna(int); // konstruktor osnovne klase  za višestruko izvođenje - po redosledu navođenja osnovnih klasa
}; u definiciji izvedene, ne u listi inicijalizatora
Osnovna::Osnovna (int i) : bi(i) – inicijalizuju se specifični atributi
{/*...*/}  po redosledu navođenja u definiciji klase, ne u listi inicijalizatora
 klasni tipovi pozivom njihovih odgovarajućih konstruktora
class Izvedena : public Osnovna {  ugrađeni tipovi na osnovu inicijalizatora
int di;  nizovi objekata po rastućim vrednostima indeksa
public: podrazumevanim konstruktorom
Izvedena(int); – izvršava se telo konstruktora izvedene klase
};  Pri uništavanju objekta izvedene klase
Izvedena::Izvedena (int i) : Osnovna(i),di(i+1) redosled poziva destruktora je uvek obratan
{/*...*/}

23 Izvođenje klasa 27.11.2023. 24 Izvođenje klasa 27.11.2023.


Primer redosleda Konverzija
class A {  Objekat javno izvedene klase je i objekat osnovne klase
public:
A() {cout<<"Konstruktor A."<<endl;}  Posledice su:
~A() {cout<<"Destruktor A."<<endl;}
}; – pokazivač na objekat izvedene klase može da se implicitno konvertuje
u pokazivač na objekat osnovne klase
class O {
public: – pokazivač na objekat osnovne klase može samo eksplicitno da se konvertuje
O() {cout<<"Konstruktor O."<<endl;} u pokazivač na objekat izvedene klase
~O() {cout<<"Destruktor O."<<endl;}
};
– isto važi i za reference
Izlaz:
Konstruktor O. – objekat osnovne klase može da se inicijalizuje objektom izvedene klase
class I : public O {
A a; Konstruktor A. – objektu osnovne klase može da se dodeli objekat izvedene klase
public: Konstruktor I.
I() {cout<<"Konstruktor I."<<endl;} Destruktor I.
 Objekat privatno/zaštićeno izvedene klase nije i objekat osnovne klase
~I() {cout<<"Destruktor I."<<endl;} Destruktor A. – pokazivač na objekat takve klase može da se implicitno konvertuje
}; Destruktor O. u pokazivač na objekat osnovne klase samo unutar izvedene klase
int main () { I d; }

25 Izvođenje klasa 27.11.2023. 26 Izvođenje klasa 27.11.2023.

Pojam polimorfizma Decentralizacija odgovornosti


 Potrebno je projektovati klasu geometrijskih figura Figura  Bitno u prethodnom primeru: decentralizacija odgovornosti
– sve figure treba da imaju metod crtaj() – program koji crta sve figure ne zna kako se crta pojedina figura
 Iz klase geometrijske figure se izvode klase kruga, kvadrata, trougla itd.  Svaki objekat će "prepoznati" kojoj izvedenoj klasi pripada,
 Izvedena klasa treba da realizuje metod crtanja na sebi svojstven način iako mu se obraćamo kao objektu osnovne klase
 U nekom delu programa sve figure na crtežu treba da budu nacrtane  Polimorfizam: svaki objekat izvedene klase izvršava metod
 C++ omogućava da figure jednostavno iscrtamo na sledeći način: onako kako je to definisano u njegovoj izvedenoj klasi,
iako mu se pristupa kao objektu osnovne klase
void crtanje (int br_figura, Figura** niz_figura){
for (int i=0; i<br_figura; i++)  Polymorphism = Poly (više) + Morph (oblik)
niz_figura[i]->crtaj(); – isti metod ima više "oblika"
} – tip objekta za koji se zove metod određuje koji metod se poziva
 Napomena: figurama se pristupa preko niza pokazivača na figure – mehanizam je potpuno dinamički – klasa objekta se određuje
pri izvršavanju programa, a ne pri prevođenju

27 Izvođenje klasa 27.11.2023. 28 Izvođenje klasa 27.11.2023.


Virtuelni (polimorfni) metodi Nadjačavanje virtuelnih metoda
 Virtuelni (polimorfni) metodi:  Deklaracija virtuelnog metoda u izvedenoj klasi
– metodi osnovne klase koji u izvedenim klasama mogu da se redefinišu, – mora da se potpuno slaže sa deklaracijom istog u osnovnoj klasi
a ponašaju se polimorfno – potpuno slaganje znači:
– metod u izvedenoj nadjačava (eng. override) metod osnovne klase  da se podudaraju broj i tipovi argumenata, kao i tip rezultata
– izuzetno, ako je tip rezultata referenca/pokaz. na osnovnu klasu
 Polimorfna klasa:  redefinisan metod može da vraća referencu/pokazivač
– klasa koja sadrži barem jedan virtuelni (polimorfni) metod na javno izvedenu klasu iz date osnovne
 Virtuelni metod se u osnovnoj klasi deklariše:  Neslaganje potpisa istoimenog virtuelnog metoda – sakrivanje met.
– pomoću modifikatora (ključne reči) virtual na početku deklaracije  Samo razlika u tipu rezultata (osim navedene) – greška
 Prilikom deklarisanja virtuelnih metoda u izvedenim klasama:  Virtuelni metod osnovne klase
– ne mora da se piše modifikator virtual
ne mora da se redefiniše u svakoj izvedenoj klasi
– u izvedenoj klasi u kojoj nije redefinisan,
 Pozivom preko pokazivača/reference na osnovnu klasu važi nasleđen virtuelni metod iz osnovne klase
izvršava se onaj metod koji pripada klasi pokazanog objekta

29 Izvođenje klasa 27.11.2023. 30 Izvođenje klasa 27.11.2023.

Primer virtuelnog metoda Pozivanje virtuelnih metoda


class ClanBiblioteke {  Virtuelni mehanizam se aktivira samo ako se objektu pristupa indirektno
protected: (preko reference ili pokazivača):
Racun r;
public: class Osnovna { public: virtual void f(); };
virtual Racun platiClanarinu () // virtuelni metod osnovne klase class Izvedena : public Osnovna { public: void f(); };
{ return r-=CLANARINA; }
}; void g1(Osnovna b) { b.f(); }
void g2(Osnovna *pb) { pb->f(); }
class PocasniClan : public ClanBiblioteke { void g3(Osnovna &rb) { rb.f(); }
public: int main () {
Racun platiClanarinu () // virtuelni metod izvedene klase Izvedena d;
{ return r; } g1(d); // poziva se Osnovna::f
}; g2(&d); // poziva se Izvedena::f
g3(d); // poziva se Izvedena::f
int main () { Osnovna *pb=new Izvedena; pb->f(); // poziva se Izvedena::f
ClanBiblioteke *clanovi[100]; Osnovna &rb=d; rb.f(); // poziva se Izvedena::f
//... Osnovna b=d; b.f(); // poziva se Osnovna::f
for (int i=0; i<brojClanova; i++)cout<<clanovi[i]->platiClanarinu(); delete pb; pb=&b; pb->f(); // poziva se Osnovna::f
} }

31 Izvođenje klasa 27.11.2023. 32 Izvođenje klasa 27.11.2023.


Modifikatori override i final Modifikatori - primer
 Implicitna virtuelnost metoda u izvedenoj klasi nije robusna class A { public:
virtual void vm1();
 Kao modifikator (iza liste parametara) može da se navede override virtual void vm1(int);
– eksplicitno se iskazuje da metod nadjačava odgovarajući virtuelni metod void m1();
};
– prevodilac prijavljuje grešku ako nije moguće nadjačavanje
class B: public A {public:
 ako u osnovnoj klasi ne postoji virtuelni metod void vm1() override;
sa odgovarajućim potpisom i tipom rezultata void vm1(int) override final;
 Modifikator final sprečava nadjačavanje metoda u izvedenoj klasi void vm1(float) override; // ! GRESKA – nema A::vm1(float)
– dozvoljen samo za (eksplicitno ili implicitno) virtuelni metod void vm2() override; // ! GRESKA – nema A::vm2()
void m1() override; // ! GRESKA – A::m1() nije virtuelna
 Modifikatori override i final nisu rezervisane reči void m2() final; // ! GRESKA – B::m2() nije virtuelna
 Modifikatori override i final nisu deo potpisa metoda };
class C: public B { public:
 Moguća je kombinacija oba modifikatora override i final void vm1(int); // ! GRESKA – B::vm1(int) je konacna
};
int override=1; int final=2; // U redu, nije preporucljivo

33 Izvođenje klasa 27.11.2023. 34 Izvođenje klasa 27.11.2023.

Dinamičko vezivanje Implementacija virtuelnih poziva


 Dinamičko vezivanje (engl. dynamic binding):  Za jednostruko izvođenje mehanizam je sledeći:
– mehanizam koji obezbeđuje da se metod koji se poziva određuje: – za virtuelne (kao i za nevirtuelne) metode svake nove klase
 prema tipu objekta,
 jedinstven kod metoda
 ne prema tipu pokazivača ili reference na taj objekat
– za svaku polimorfnu klasu:
 Odlučivanje koji će se virtuelni metod (iz koje klase) pozvati
 tabela pokazivača na virtuelne metode te klase
– obavlja se u toku izvršavanja programa - dinamički
(tabela virtuelnih funkcija - TVF)
– bitna je razlika u odnosu na mehanizam preklapanja imena funkcija
– svaki objekat polimorfne klase sadrži pokazivač na TVF
 mehanizam preklapanja imena funkcija je statički

35 Izvođenje klasa 27.11.2023. 36 Izvođenje klasa 27.11.2023.


Primer – virtuelni mehanizam Osobine polimorfnih metoda
class O{ int main(){  Virtuelni (polimorfni) metodi ne mogu da budu statički
public: I i;  Ako je potreban statički metod sa polimorfnim ponašanjem
int a; O *po=&i; – ovaj metod treba da poziva nestatički virtuelni metod za neki objekat date klase
– pokazivač ili referenca na objekat čiji se metod poziva
virtual void f(); po->g(); // (*(po->TVFp[1]))(po) može da se prenese kao argument poziva statičkog metoda
virtual void g(); // ...  Primer:
} } class X{
TVF za O
public:
class I:public O{ O::f(){...}
&O::f() virtual void m()const{};
int b; po: i:
static void s(const X&);
public: &O::g() O::g(){...} };
TVFp
void g(); void X::s(const X& x){ x.m();}
a TVF za I
}  Globalne prijateljske funkcije ne mogu da budu polimorfne
b 0 &O::f() – ako je potrebno polimorfno ponašanje – sličan recept kao za statičke metode
I::g(){...}  Virtuelni metodi mogu da budu prijatelji drugih klasa
1 &I::g()

37 Izvođenje klasa 27.11.2023. 38 Izvođenje klasa 27.11.2023.

Virtuelni destruktor Primer virtuelnog destruktora


 Konstruktor ne može da bude virtuelna funkcija class OVD { public: virtual ~OVD(); };
– jer se poziva pre nego što se objekat kreira class IVD: public OVD { public: ~IVD(); };
 Destruktor može da bude virtuelna funkcija class OnVD{ public: ~OnVD(); };
– ako je destruktor virtuelna funkcija, class InVD: public OnVD { public: ~InVD(); };
tek u vreme izvršenja se odlučuje koji destruktor se poziva
void oslobodi (OVD *pb) { delete pb; }
 Kada se uništava dinamički kreirani objekat (pomoću delete p) void oslobodi (OnVD *pb) { delete pb; }
– destruktor osnovne klase se uvek izvršava
int main () {
– ili kao jedini ili posle destruktora izvedene klase OVD *pb=new OVD; oslobodi(pb); // ~OVD()
 Kada neka klasa ima neku virtuelnu funkciju, IVD *pd=new IVD; oslobodi(pd); // ~IVD(), ~OVD()
verovatno i njen destruktor (ako ga ima) treba da bude virtuelan OnVD *pbn=new OnVD; oslobodi(pbn); // ~OnVD()
 Unutar destruktora izvedene klase InVD *pdn=new InVD; oslobodi(pdn); // ~OnVD()
}
– ne treba da se poziva destruktor osnovne klase (implicitno se poziva)

39 Izvođenje klasa 27.11.2023. 40 Izvođenje klasa 27.11.2023.


Nizovi i izvođenje Prenos zbirki objekata funkcijama
 U jeziku C++ niz objekata nije objekat  Ako se računa sa nasleđivanjem, pravilo:
 Niz objekata izvedene klase nije jedna vrsta niza objekata osnovne klase – u programu ne treba da se koriste nizovi objekata, već nizovi pokazivača na objekte

 Ako se niz objekata izvedene klase prenese funkciji void f(Osnovna **b, int i) { cout<<b[i]->bi; }
kao niz objekata osnovne klase, može da dođe do greške int main () {
Osnovna b1; Izvedena d1,d2;
 Primer: Osnovna *b[5]; // b se moze konvertovati u Osnovna**
class Osnovna { public: int bi; }; b[0]=&d1; b[1]=&b1; b[2]=&d2; d2.bi=77;
class Izvedena : public Osnovna { public: int di; }; f(b,2); // ispisace se 77
void f(Osnovna *b, int i) { cout<<b[i].bi; } }
int main () {  Nije dozvoljena konverzija Izvedena** u Osnovna**
Izvedena d[5];
d[2].bi=77;  Za prethodni primer nije dozvoljeno:
f(d,2); // nece se ispisati 77 int main () {
} Izvedena *d[5]; // d je tipa Izvedena**
– objekti osnovne klase su manji od objekata izvedene klase f(d,2); // ! GRESKA: pokusana konverzija
// Izvedena** u Osnovna**
– funkcija f smatra da je dobila niz objekata osnovne klase }
– kada joj se prosledi niz objekata izvedene klase, nema načina da to odredi

41 Izvođenje klasa 27.11.2023. 42 Izvođenje klasa 27.11.2023.

Apstraktni metodi i klase Primer apstraktne klase


 Apstraktan (čist virtuelan) metod: class Osnovna {
public:
– virtuelan metod koji nije definisan za osnovnu klasu virtual void cvf () =0; // apstraktni metod
 Deklaracija apstraktnog metoda u osnovnoj klasi: virtual void vf (); // virtuelni metod
};
– umesto tela stoji =0
class Izvedena : public Osnovna {
 Klasa koja sadrži barem jedan apstraktni metod public:
naziva se apstraktnom klasom (engl. abstract class) void cvf();
 Apstraktna klasa ne može da ima primerke (objekte), };
već iz nje samo mogu da se izvode druge klase int main () {
Izvedena izv, *pi=&izv;
 Mogu da se definišu pokazivači i reference na apstraktnu klasu Osnovna osn; // ! GRESKA: Osnovna je apstraktna klasa
 Pokazivači i reference na apstraktnu klasu Osnovna *po=&izv;
mogu da ukazuju samo na objekte izvedenih konkretnih klasa po->cvf(); // poziva se Izvedena::cvf()
po->vf(); // poziva se Osnovna::vf()
}

43 Izvođenje klasa 27.11.2023. 44 Izvođenje klasa 27.11.2023.


Apstraktni destruktor Generalizacija i konkretizacija
 Kada treba da se spreči stvaranje objekata klase čiji su metodi konkretni  Apstraktna klasa predstavlja generalizaciju izvedenih klasa
– deklariše se apstraktni destruktor: virtual ~Klasa() = 0  Primer:
 Apstraktni destruktor mora da bude definisan i za osnovnu klasu – sve geometrijske figure treba da mogu da odrede svoju površinu
– definicija mora da bude izvan tela klase – u osnovnoj klasi Figura se ne zna kako se uopšteno izračunava površina
 Primer: – svaka izvedena klasa iz klase Figura će znati kako se izračunava površina
class O {
public:  Klasa koja se izvodi iz apstraktne je jedna konkretizacija te klase
virtual ~О()=0;  Ako se u izvedenoj klasi iz apstraktne ne navede definicija nekog
}; apstraktnog metoda iz osnovne klase i izvedena klasa je apstraktna
O::~O(){}
class I:public O {};  Apstraktna klasa može da ima konstruktor
O o; // ! GRESKA – iako ne mogu da se stvaraju objekti apstraktne klase
I i; // U redu – on će da bude pozivan kao konstruktor osnovne klase
pri konstrukciji objekata konkretnih klasa koje se izvode iz apstraktne klase

45 Izvođenje klasa 27.11.2023. 46 Izvođenje klasa 27.11.2023.

Problem konverzije naniže Operator dinamičke konverzije


 Problem:  Izrazi za dinamičku konverziju tipa:
– preko pokazivača/reference na osnovnu klasu dynamic_cast<izvedena klasa*>(pokazivač)
nije moguće da se pristupi članovima izvedene klase dynamic_cast<izvedena klasa&>(referenca)
– samo ako se zna tačno kog tipa je objekat na koji ukazuje – konvertuju pokazivač/referencu na polimorfnu osnovnu klasu
pokazivač/referenca na osnovnu klasu u pokazivač/referencu na izvedenu klasu
 može da se eksplicitno konvertuje pokazivač na osnovnu – ako klasa nije polimorfna – greška pri prevođenju
u pokazivač na izvedenu klasu
– ovakva konverzija se naziva konverzijom naniže (downcast)  Ako pokazivač ne pokazuje na objekat izvedene klase
u koju se vrši konverzija, ili njene potklase:
– konverzija naniže nije bezbedna
– rezultat je nullptr
 Rešenje:
 Ako referenca ne upućuje na objekat izvedene klase
– da se uradi konverzija pokazivača/reference naniže
ali da se pri tome proveri njena ispravnost u koju se vrši konverzija, ili njene potklase:
 postiže se operatorom za dinamičku konverziju tipa – baca se izuzetak bad_cast (definisan u <typeinfo>)

47 Izvođenje klasa 27.11.2023. 48 Izvođenje klasa 27.11.2023.


Primer dinamičke konverzije Dinamičko određivanje tipa
class A {public: virtual void vm(){}}; // polimorfna klasa  Jezik C++ omogućava da se u vreme izvršavanja odredi tip izraza
class B: public A{/*...*/};  Dva oblika: (1) typeid(izraz) (2) typeid(tip)
class C: public A{/*...*/}; – izraz:
int main(){  može da bude proizvoljnog tipa:
– standardnog/ korisničkog kao i jednostavnog/složenog
A *pa=new B();
 vrednost se ne izračunava, nema ni bočnih efekata
B *pb=dynamic_cast<B*>(pa);  ako ukazuje na objekat polimorfne klase,
C *pc=dynamic_cast<C*>(pa); //pc==nullptr rezultat se odnosi na dinamički tip operanda, inače na statički
 ako je u izrazu typeid(*p) pokazivač p==nullptr
B &rb=dynamic_cast<B&>(*pa);
– ako je tip p pokazivač na polimorfnu klasu  izuzetak bad_typeid
C &rc=dynamic_cast<C&>(*pa); // bad_cast – inače se rez. odnosi na statički tip pokazivača (za X *p  info za X)
} – tip: proizvoljan tip
– rezultat: const type_info&
 Introspekcija ili refleksija: način da program saznaje o sebi
49 Izvođenje klasa 27.11.2023. 50 Izvođenje klasa 27.11.2023.

Klasa type_info Primer određivanja tipa


 type_info je tip iz <typeinfo> i sadrži podatke o tipu class O {}; // nepolimorfna klasa
class I: public O{public: virtual void f(){}};
 Metodi klase type_info: class II: public I{};
bool operator==(const type_info&) const; int main(){
bool operator!=(const type_info&) const; O *po=new I;
I *pi=new II; //Izlaz (Borland):
bool before(const type_info&) const; cout<<(typeid(*po)==typeid(I))<<endl; //0
// uređuje dva objekta tipa type_info (zavisi od prev.) cout<<(typeid(*pi)==typeid(II))<<endl; //1
const char* name()const; cout<<typeid(O).name()<<endl; //O
// vraća neko logično ime tipa (zavisi od prevodioca) cout<<typeid(*po).name()<<endl; //O
cout<<typeid(*pi).name()<<endl; //II
 Tip type_info nema raspoložive javne konstruktore cout<<typeid(po).name()<<endl; //O *
– ne mogu da se stvaraju podaci ovog tpa cout<<typeid(pi).name()<<endl; //I *
– razlog zašto ne mogu da se prenose podaci ovog tipa po vrednosti int m[100][20];
 Operator dodele za tip type_info je takođe privatan cout<<typeid(m).name()<<endl; //int[100][20]
}

51 Izvođenje klasa 27.11.2023. 52 Izvođenje klasa 27.11.2023.


Zloupotreba određivanja tipa Višestruko nasleđivanje

 Objektno-orijentisane programe treba koncipirati tako:  Višestruko nasleđivanje (engl. multiple inheritance):
– da se pišu polimorfni metodi koji decentralizuju odgovornosti – kada klasa direktno nasleđuje osobine više osnovnih klasa
 odgovornosti se delegiraju pojedinim apstrakcijama – roditeljske klase nisu jedna drugoj vrsta
 Primer:
 Određivanje tipa krije zamku:
– konj je životinja, ali je i prevozno sredstvo
– kada se odredi tip objekta on može da se poredi sa klasama
– pri tome ni životinja nije vrsta prevoznog sredstva, ni obrnuto
 za dati tip objekta se poziva nepolimorfni metod date klase
 Klasa se deklariše kao naslednik više klasa
– polimorfizam se zamenjuje selekcijom tako što se u zaglavlju navode osnovne klase
– program ponovo postaje sa centralizovanom odgovornošću – ispred svake osnovne klase treba da stoji reč public,
– smanjuje se mogućnost jednostavnog održavanja koda da bi izvedena klasa nasleđivala prava pristupa članovima
class I:public O1, public O2, public O3 {/*...*/};

53 Izvođenje klasa 27.11.2023. 54 Izvođenje klasa 27.11.2023.

Konstrukcija i destrukcija Problem "dijamant strukture"


 Pravila o nasleđenim članovima važe i ovde  Problem:
 Konstruktori svih osnovnih klasa se izvršavaju pre – kada su osnovne klase pri višestrukom izvođenju,
izvedene iz iste roditeljske klase
– konstruktora članova izvedene klase i
 Primer:
– konstruktora izvedene klase class B {int i; /*...*/};
 Konstruktori osnovnih klasa se pozivaju po redosledu deklarisanja class X : public B {/*...*/};
class Y : public B {/*...*/};
 Destruktori svih osnovnih klasa se izvršavaju posle class Z : public X, public Y {/*...*/};
– destruktora izvedene klase i  Svaka od klasa X i Y ima po jedan primerak članova klase B
– destruktora članova izvedene klase – objekat Z z; će imati dva skupa članova klase B
 Destruktori osnovnih klasa se pozivaju obrnutim redom navođenja – moguće je da se napravi razlika među njima pomoću operatora ::
z.X::i ili z.Y::i
 Konstruktor osnovne klase B se izvršava dva puta

55 Izvođenje klasa 27.11.2023. 56 Izvođenje klasa 27.11.2023.


Virtuelne osnovne klase Redosled konstrukcije
 Ako ovo nije potrebno, pri izvođenju iz klase B,  Konstruktori virtuelnih osnovnih klasa
ona treba da bude deklarisana kao virtuelna osnovna klasa:
class B {int i; /*...*/}; se pozivaju pre konstruktora nevirtuelnih osnovnih klasa
class X : virtual public B {/*...*/};  Precizan redosled izvršavanja konstruktora:
class Y : virtual public B {/*...*/};
class Z : public X, public Y {/*...*/}; – konstruktori virtuelnih osnovnih klasa
 Sada klasa Z nasleđuje samo jedan skup članova klase B  po dubini grafa izvođenja (najpre najviših u hijerahiji klasa)
– nema dvoznačnosti u pristupu članovima nasleđenim iz B
 sleva-udesno na istom nivou grafa
 Konstruktor klase B se poziva samo jednom
 Klasa B mora da bude virtuelna osnovna i za X i za Y – konstruktori nevirtuelnih osnovnih klasa
– ako je samo za jednu virtuelna - ostaju dva skupa članova i 2x konstruktor – konstruktori atributa
 Za metod m() iz osnovne klase B koji nije redefinisan ni u X ni u Y – konstruktor izvedene klase
– z.m() poziva metod osnovne kalse B
 Konstruktori virtuelnih osnovnih klasa se pozivaju samo jednom
 Redefinicija metoda m() definisanog u klasi B
– ili u klasi X ili u klasi Y – z.m() poziva se data redefinicija (nije problem)
– i u klasi X i u klasi Y – z.m() je dvoznačno (problem)

57 Izvođenje klasa 27.11.2023. 58 Izvođenje klasa 27.11.2023.

Primer redosleda konstrukcije


#include <iostream.h>
class B { // Originalni redosled: // B X Y Z
public: B(){cout<<" B";}
};
// Da je stajalo (jedna po jedna zamena):
class X : virtual public B { class X: public B {… // B B X Y Z
public: X(){cout<<" X";}
};

class Y : virtual public B { class Y: public B {… // B X B Y Z


public: Y(){cout<<" Y";}
};

class Z : public X, public Y { class Z: public X,


public: Z(){cout<<" Z"<<endl;} virtual public Y {… // B Y X Z
};
class Z: virtual public X,
int main(){Z z;} virtual public Y {… // B X Y Z

59 Izvođenje klasa 27.11.2023.

You might also like