You are on page 1of 24

Klase II

1. Konstantne funkcije članice


Funkcije članice klase generalno se mogu podeliti na dve grupe:
na funkcije članice koje menjaju i one koje ne menjaju sadržaj
objekata za koje su pozvane. U prethodnom delu ove glave
objašnjeno je na koji način funkcija članica pristupa članovima
objekta za koji je pozvana. Reč je o thispokazivaču, koji predstavlja
pokazivač na objekat klase za koji je funkcija članica pozvana, a koji
se implicitno prosleđuje prilikom svakog poziva. Za
"obične" funkcije članice, taj pokazivač deklarisan je kao konstantni
pokazivač na objekat tipa klase kojoj i funkcija članica pripada, tj.
za klasu X važi:
X * const this ;
Ovako deklarisan pokazivač ne garantuje da sadržaj na koji
pokazuje neće biti menjan. Dakle, čak i kada obična funkcija članica
ne menja sadržaj objekta za koji je pozvana, unutar nje članovima
objekta pristupa preko "običnog" (nekonstantnog) pokazivača. Pošto
običan pokazivač ne može da pokazuje na konstantne podatke,
obične funkcije članice ne mogu biti pozvane za konstantne objekte
neke klase.
Da bi se omogućilo pozivanje funkcija članica koje ne menjaju
sadržaj objekata, u programski jezik C++ uvedene su tzv.
"konstantne" funkcije članice. Ove funkcije članice deklarišu se tako
što se na kraj zaglavlja doda ključna reč const. Ovako
deklarisane funkcije članice, za razliku od običnih, kao skriveni
argument imaju this pokazivač deklarisan na sledeći način:
const X* const this ;
pod pretpostavkom da se razmatra konstantna funkcija članica
neke klase X. Sada je očigledno da se konstantne funkcije članice
mogu pozivati kako za konstantne, tako i za nekonstantne objekte.
Zbog ove činjenice, sve funkcije članice koje ne menjaju sadržaj
objekata za koji su pozvane poželjno je deklarisati kao
konstantne funkcije članice.
Potrebno je naglasiti da u slučaju da se konstantna funkcija članica
definiše izvan klase, ključna rečconst mora se navesti kako u
zaglavlju deklaracije (unutar klase), tako i u zaglavlju definicije
(izvanklase). Dve funkcije članice koje imaju isto ime i listu
argumenata, a razlikuju se po tome što jedna ima na kraju zaglavlja
ključnu reč const, a druga nema, tretiraju se kao
preklopljene funkcije, tj. program prevodilac ih tretira kao dve
različite funkcije članice. Često se konstantne funkcije članice
nazivaju "inspektori", dok se obične funkcije članicenazivaju
"mutatori".
Primer :
class hido
{
int b ;
public:
int get ( ) const
{ return b ; }
void set ( int x )
{ b = x ; }
void inc ( ) const
{ ++ b ; } //GRESKA: const fukcija ne moze menjati
objekat!
} ;

int main ( )
{
hido h ;
h.b = 12 ; //GRESKA: b je privatni clan klase!
h.set ( 3 ) ;
int a = h.get ( ) ;
const hido hc ; //LOSE: const objekat inicijalizovan
//slucajnim automatskigenerisanim konstruktorom!
hc.set ( 2 ) ; //GRESKA: za const objekat ne mogu se
// pozivati obicne funkcije clanice!
int b = hc.get ( ) ;
return 0 ;
}
2. Statički članovi klase
Članovi podaci i funkcije članice neke klase, navođenjem ključne
reči static ispred deklaracije ili definicije, mogu da budu
deklarisani kao statički članovi klase.

U nastavku, prvo sledi detaljan opis statičkih članova podataka, a


zatim i statičkih funkcija članica.
2.1. Statički članovi podaci
Kada se neki član podatak klase deklariše kao statički, tada on
postaje zajednički za sve objekte teklase. Drugim rečima, za razliku
od nestatičkog člana podatka, za koji važi da svaki objekat klaseima
svoju realizaciju (primerak u memoriji) ovog člana, za statički član
važi da postoji samo jedna realizacija statičkog člana podatka za
sve objekte klase, tj. svaki pristup ovakvom članu predstavlja
pristup istom delu memorije.
Ovakvi podaci smeštaju se u deo memorije predviđen za statičke
podatke, što znači da njihov životni vek ne zavisi od životnog veka
bilo kog objekta date klase, već se oni kreiraju na početku
izvršavanja, a ukidaju na kraju izvršavanja programa. Može se reći
da statički član podatak nije deo objekta klase i da predstavlja
"centralizovanu" informaciju u vezi sa klasom.
Praktično, ovi podaci imaju osobine koje su slične ostalim statičkim
podacima (globalnim podacima i statičkim podacima unutar
funkcija). Razlike u odnosu na globalne podatke sastoje se u tome
što imena statičkih članova podataka pripadaju imenskom
prostoru klase unutar koje su deklarisani i što projektant klase,
specifikatorima pristupa, može ograničiti pristup njihovim imenima
izvan klase.
Potrebno je naglasiti da deklaracija statičkog člana podatka
unutar klase nije i njegova definicija, pa se on mora posebno
definisati izvan definicije klase.Tom prilikom nije potrebno ponovo
navoditi ključnu reč static.
Pošto statički član podatak nije deo objekta klase, moguće mu je
pristupiti i bez navođenja konkretnog objekta i to na sledeći način:
ime_klase :: ime_statičkog_člana
Važno je istaknuti razliku između statičkih članova podataka klase i
lokalnih statičkih podataka unutar funkcija članica klase. Lokalni
statički podaci unutar funkcija članica ponašaju se na isti način kao i
lokalni statički podaci unutar globalnih funkcija. Dakle, oni se
inicijalizuju prvi put kada se funkcija pozove i čuvaju vrednost
između dva uzastopna poziva funkcije. Oblast važenja njihovog
imena ograničena je telom funkcije u kojoj se nalaze. Za lokalne
statičke podatke unutar funkciječlanice klase može se reći da su
zajednički za sve pozive te funkcije članice bez obzira na objekte
teklase.
2.2. Statičke funkcije članice
Funkcija članica čije zaglavlje počinje ključnom rečju static, naziva
se statička funkcija članica. Ovakva funkcija ima sva svojstva
globalnih funkcija osim činjenica da njeno ime spada u imenski
prostor klase u kojoj je deklarisana/definisana i da
projektant klase može specifikatorima pristupa da ograniči pristup
njenom imenu izvan klase.
Suštinska razlika u odnosu na nestatičke funkcije članice sastoji se u
tome što statičke funkciječlanice nemaju this pokazivač, tj.
prilikom poziva ovakvih funkcija, ne prosleđuje se pokazivač na
objekat klase kojoj pripada i statička funkcija. To znači da se u telu
ovakve funkcije može nesmetano pristupiti samo statičkim
podacima članovima, dok se običnim (nestatičkim) podacima
članovima ne može se pristupiti neposredno, kao što se to čini iz
nestatičkih funkcija članica. Da bi se pristupilo nestatičkim
članovima klase, neophodno je statičkoj funkciji proslediti pokazivač
ili referencu na objekat čijim podacima se nastoji pristupiti.
Slično statičkim članovima podacima i statičke funkcije članice
moguće je pozivati bez navođenja konkretnog objekta i to nasledeći
način:
ime_klase :: ime_statičke_funkcije_članice
(lista_argumenata)
S obzirom na ovu činjenicu, kao i činjenicu da se implicitno ne
prosleđuje pokazivač na objekat dateklase, statičke funkcije članice
moguće je pozivati i pre kreiranja bilo kog objekta date klase.
Statičke funkcije članice mogu da se definišu kako unutar
definicije klase (inline), tako i izvan definicije klase. Kada se definišu
izvan klase, ispred zaglavlja definicije nije dozvoljeno ponavljati
ključnu reč static.
Važno je naglasiti da se dve funkcije članice, koje imaju isto ime i
listu argumenata, ne mogu razlikovati samo po ključnoj
reči static ispred zaglavlja!
Primer:
#include <iostream>
using namespace std ;
class test
{
static int c ;
double d ;
public:
test( ):d(0), c(0) { } //GRESKA: ne moze staticka
// promenljiva uinicijalnu listu!
test( ):d(0) { ++ c ; }
~test( ) { -- test::c ; }
void how_many ( )
{ cout << c << endl ; }
static void how_much ( )
{ cout << c << endl ; }
static void error ( double f )
{ d *= f ; } //GRESKA: ne moze se nestatickom clanu
// podatku pristupati direktno unutar
// staticke funkcije clanice!
int callings ( )
{
static int count = 0 ;
return ++ count ;
}
} ;

int test::c = 0 ; //Definicijastatickog clana podatka.


int main ( )
{
how_much ( ) ; //GRESKA: “how_much”nije globalna
funkcija!
test::how_much ( ) ; //Ovako moze.
test t1 ;
t1.how_many ( ) ;
{
test t2 ;
t1.how_many ( ) ;
}
t2.how_many ( ) ; //GRESKA: “t2”lokalna za prethodni
blok!
t1.how_many ( ) ;
test::callings ( ) ; //GRESKA: “callings” nije
staticka
// funkcija clanica!
t1.callings ( ) ; //Ovako moze.
return 0 ;
}
3. Prijatelji klasa
Do sada je pokazano da je unutar funkcija članica klase moguće
pristupati svim članovima klase, bez obzira na specifikatore
pristupa. U programskom jeziku C++ omogućava se
projektantu klase da isti pristup članovima klase omogući i unutar
funkcija koje nisu članice klase. Takve funkcije nazivaju se
"prijateljske" funkcije.
Da bi funkcija bila prijateljska za neku klasu, potrebno je istu
deklarisati u definiciji te klase i to na način da se ispred deklaracije
navedeključna reč friend. Pri tom, treba naglasiti da prijateljska
funkcija može biti kako globalna funkcija, tako i funkcija članica
neke druge klase.
Ime prijateljske funkcije ne spada u oblast važenja klase u kojoj je
proglašena za prijatelja, pa se ovakva funkcija ne poziva preko
objekta date klase (pomoću operatora pristupa članu), već se
globalna prijateljska funkcija poziva kao i svaka druga globalna
funkcija, dok se prijateljska funkcija članica neke druge klase,
poziva preko objekta klase kojoj pripada (pomoću operatora
pristupa članu).
Prijateljske funkcije moguće je definisati unutar klase u kojoj se želi
da one budu prijatelji. Dakle, moguće je tela ovih funkcija navesti u
definiciji klase i tom prilikom ništa se ne menja po pitanju njihovog
korišćenja.
Primer:
class first ;
class second
{
int y ;
public:
void set ( first & ) ;
void set2 ( first &, int ) ;
} ;
class first
{
int x ;
friend void test ( first & c ) { ++ c.x ; } //Inline
friend class second ; //Sve funkcije clanice klase
// “second” suprijatelji klase first.
} ;

void second::set ( first & f)


{ f.x = 12 ; }

void second::set2(first & f,int x )


{ f.x = x ; }

int main ( )
{
first f ;
second s ;
s.set ( f ) ;
s.set2 ( f, 23 ) ;
test ( f ) ;
return 0 ;
}
4. Prijatelji klasa (nastavak)
Kada postoji potreba da sve funkcije članice jedne klase budu
prijatelji druge klase, moguće je čitavu prvu klasu proglasiti
prijateljskom u drugoj i na taj način omogućiti da se sva imena i
članovi iz druge klase mogu koristiti unutar prve. Jedna klasa se u
drugoj proglašava prijateljskom tako što se u definiciji druge navodi
sledeće:
friend class ime_prijateljske_klase ;
Ukoliko je prijateljska klasa prethodno definisana ili samo
deklarisana, moguće je upotrebiti i kraću formu:
friend ime_prijateljske_klase ;
Važno je naglasiti da prijateljstvo nije simetrična relacija, tj. ako je
klasa Y deklarisana kao prijatelj u klasi X, to ne znači da je
istovremeno klasa X prijatelj klase Y. Osim toga, prijateljstvo nije ni
tranzitivna relacija, tj. ako je klasa B prijatelj klase A , a klasa C
prijatelj klase B, to ne znači da je klasa C automatski
prijatelj klase A. Takođe, treba istaknuti da se prijateljska relacija
ne nasleđuje. Nameće se pitanje da li koncept "prijatelja" narušava
princip enkapsulacije kao jedan od postulata objektno orijentisanog
programiranja? S obzirom da sam projektant unutar
definicije klase deklariše prijateljske funkcije, a da prijateljska
relacija nije ni tranzitivna ni nasledna, može se reći da se princip
enkapsulacije ne narušava.
Ako globalne funkcije prijatelji neke klase imaju pristup svim
članovima klase, kao i funkcije članice, nameće se dilema o tome u
kojim situacijama je bolje koristiti globalne prijateljske funkcije od
funkcija članica klase. Ne postoje stroga pravila koja bi dala odgovor
na ovu dilemu, već preporuke koje kažu da su globalne
prijateljske funkcije bolje u sledećim situacijama:
1. kada funkcija treba da pristupa članovima više klasa,
2. ako funkcija ne zahteva argument kao lvrednost,
3. kada postoji potreba da se omogući implicitna (automatska)
konverzija tipa argumenta,
4. prilikom preklapanja nekih operatora.
Praktično, može se reći da globalne prijateljske funkcije ne
predstavljaju ponašanje objekta klase, već posebne usluge u vezi sa
datom klasom. Zbog toga ih sečesto naziva "uslugama klase" ( class
utilities ).
5. Konstruktor kopije
Kada se u programskom jeziku C++ jedan objekat
neke klase inicijalizuje drugim objektom isteklase, tada program
prevodilac članove podatke prvog objekta inicijalizuje redom
vrednostima koje sadrže članovi podaci drugog objekta.U određenim
situacijama to ne odgovara potrebama programera, pa je
uprogramskom jeziku C++ uvedena mogućnost da programer
kontroliše proces inicijalizacije jednog objekta drugim istog tipa. U
pitanju je poseban konstruktor koji se poziva svaki put kada se
jedan objekat inicijalizuje drugim objektom iste klase. To je
konstruktor koji može da se poziva sa jednim argumentom tipa
svoje klase, a naziva se konstruktor kopije (copy-constructor).
Za klasu X , konstruktor kopije deklariše se nasledeći način:
X (const X & ) ;
Važno je istaknuti da se konstruktor kopije ne može deklarisati tako
da kao argument prima objekat vlastite klase po vrednosti, već
isključivo po referenci.
Ako se ne definiše konstruktor kopije za neku klasu, program
prevodilac automatski generiše podrazumevani javni konstruktor
kopije. Ovaj konstruktor vrši kopiranje članova podataka na način
opisan na samom početku dela glave posvećene konstruktoru
kopije, tj. članovi podaci se redom kopiraju. Ovakav način kopiranja
naziva se i "plitkim" kopiranjem (shallow copy).
Primer:
class test
{
int x, y ;
public:
test ( int a, int b ) : x( a ), y( b ) { }
} ;

int main ( )
{
test t1 ( 3, -1 ) ; test t2 ( t1 ) ;
//Poziva se automatski kreiran
// konstruktor kopije.Clanovi objekta
// “t2” imaju iste vrednostikao clanovi objekta “t1”.
return 0 ;
}
Ovakav način kopiranja u većini slučajeva odgovara potrebama,
međutim u određenim situacijama može da izazove probleme.
Takav slučaj prikazan je u sledećem primeru:
class test2
{
int * px ;
public:
//Konstruktor dinamicki alocira memoriju.
test2 (int x) : px (new int (x)) { }
//Konstruktor kopije.
test2 (const test2 & c) : px ( new int ( *c.px ) ) { }
//Destruktor oslobadja dinamicki alociranu memoriju.
~test2 ( ) { delete px ; }
} ;

int main ( )
{
test2 i1(5), i2(i1) ;
return 0 ;
}
Kada u prethodnom primeru ne bi bio definisan konstruktor kopije,
program prevodilac bi generisao podrazumevani javni konstruktor
kopije kojim bi se, kao što je prethodno opisano, redom kopirali
članovi jednog objekta u članove drugog objekta. U prikazanom
primeru član podatak px objekta i2bi kopiranjem iz i1 dobio isti
sadržaj kao i članpodatak px iz objekta i1.
Odnosno, px iz i2pokazivao bi na isti dinamički alociran deo
memorije kao i px iz i1. Problem bi nastao na kraju mainfunkcije,
kada bi se pozivali destruktori kreiranih objekata (i1 i i2), jer bi
oba destruktora pokušali da oslobode istu dinamički alociranu
memoriju, što bi dovelo do neregularnog prekida rada programa.
6. Konstruktor kopije (nastavak)
Da bi se prevazišao prethodno opisani problem definisan je
konstruktor kopije koji rezerviše posebnu lokaciju u dinamičkoj
memoriji u koju se kopira sadržaj iz dinamički alocirane memorije
na koju pokazuje pokazivač iz objekta koji se koristi za
inicijalizaciju. Pokazivač u novokreiranom objektu čuva adresu nove
dinamički alocirane lokacije. Na ovaj način, prilikom ukidanja
objekata, svaki destruktor oslobađa "svoju" dinamički alociranu
lokaciju. Ovakav način kreiranja jednog objekta iz drugog, istog
tipa, naziva se"duboko" kopiranje (deep copy).
Prethodni primer predstavlja jedan od slučajeva kada je neophodno
definisati konstruktor kopije. Kod klasa koje, kao članove podatke,
imaju pokazivače na dinamički alociranu memoriju, u većini
slučajeva neophodno je definisati konstruktor kopije. Sledećim
primerom se pokazuju neki slučajevi u kojima se poziva konstruktor
kopije, kao i ostale "specijalne" funkcije članice:
#include <iostream>
using namespace std ;
class test
{
public:
test ( )
{ cout << "default" << endl ; }
test ( const test & f )
{ cout << "copy" << endl ; }
~test ( )
{ cout << "destructor" << endl ; }
} ;
void func ( const test a, consttest & b )
{ cout "func" <<endl ; }

int main ( )
{
test x ; //Poziva sedefault konstruktor.
test y = x ; //Poziva sekonstruktor kopije.
func ( x, y ) ; //Poziva se konstruktor kopije
// za prvi argument.
return 0 ;
}
//REZLTAT IZVRSAVANJA
default
copy
copy
func
destructor
destructor
destructor

Važno je naglasiti da se prilikom poziva konstruktora kopije


neke klase pozivaju i konstruktori kopije svih članova podataka
te klase. Zbog toga, ako klasa ima član podatak koji je konstanta ili
referenca, ili ima člana klase koji ima konstruktor kopije deklarisan
kao privatni član, tada podrazumevani konstruktor kopije ne može
biti generisan od strane programa prevodioca.
Konstruktor kopije podleže kontroli prava pristupa kao i
sve funkcije članice klase. To znači da može da bude javni, privatni
ili zaštićen. Ukoliko programer želi sprečiti kopiranje objekata
neke klase, dovoljno je da deklariše konstruktor kopije kao privatni
član te klase.
7. Konverzije tipova
Ako klasa A ima konstruktor koji se može pozvati samo sa jednim
argumentom tipa različitog od posmatrane klase (npr. B), onda se
tim konstruktorom definiše korisnička konverzija (user-defined
conversion) iz tipa B u tip A. Ovakav konstruktor se naziva i
"konstruktorom konverzije". Tip B može da bude kako ugrađeni,
tako i korisnički definisan.
Kao i obični konstruktori, i konstruktori konverzije mogu da se
pozovu eksplicitno. Na primer, ako u klasi A postoji konstruktor
deklarisan kao "A ( B & )" i ako je b objekat tipa B , tada izraz "A( b
)" daje privremeni objekat tipa A. Isti efekat ima i izraz "(A) b".
Potrebno je istaknuti da je konverzija između korisnički definisanih
tipova, npr. tipa B u tip A , dozvoljena samo ako klasa A ima
konstruktor koji kao argument ima tip B. Nikakve dodatne
konverzije se ne sprovode. To znači da je korisnički definisana
konverzija moguća samo ako je neposredna.
Važno je naglasiti i da je korišćenjem konstruktora konverzije
moguće obezbediti konverzije iz ugrađenih u korisnički definisane
tipove, ali obrnuto nije moguće, jer ugrađeni tipovi nisu klase i ne
postoji mogućnost definisanja njihovih konstruktora. Da bi se i
takva konverzija obezbedila neophodno je preklopiti operator
konverzije tipa za korisnički definisan tip.
Ako postoji, konstruktor konverzije se poziva prilikom inicijalizacije
jednog objekta drugim različitog tipa, zatim prilikom poziva
operatora new, kada se kao argument zadaje objekat drugog tipa,
ali i prilikom pozivanja funkcija, kada se tip stvarnog argumenta
razlikuje od tipa formalnog argumenta. Ukoliko se ne želi
automatska konverzija, tj u svim slučajevima osim u slučajevima
inicijalizacije i poziva operatora new, tada je potrebno dodati
modifikator explicit ispred deklaracije konstruktora konverzije:
class A
{
int n ;
public :
A(int a) : n(a) { }
} ;

class B
{
int n ;
public :
explicit B(int x) : n(x) { }
} ;

class C
{ public : C (A a) { } } ;

class D
{ public : D (C c) { } } ;

void func ( A a, B b ) { }

void func2 ( C c ) { }

int main ( )
{
A x(1) ;
B y(2) ; //U redu: eksplicitni poziv.
func ( x, y ) ;
A g = 2 ;
B h = 3 ; //GRESKA: nije eksplicitni poziv!
func ( 0, -4 ) ; //GRESKA: pokusaj implicitne
konverzije
// drugog parametra!
func ( -2.1 , y ) ;
C z(x) ;
C t(1), q = 2 ; //GRESKA: konverzija nije neposredna!
func2 ( 3 ) ; //GRESKA:konverzija nije neposredna!
D d(z) ;
D n(1) ; //GRESKA: konverzija nije neposredna!
return 0 ;
}
8. Ugnežđene klase

Klasa može biti definisana i unutar definicije druge klase i tada se


naziva ugnežđena ili unutrašnja klasa (inner class). Ovakva klasa
nalazi se unutar imenskog prostora koji definiše okružujuća klasa,
pa je dostupnost njenog imena određena specifikatorima pristupa,
kao i za ostale članove klase. Drugim rečima, ime
ugnežđene klase vidljivo je izvan okružujuće klase samo ako je
definicija ugnežđene klase označena kao javni (public) član
okružujuće klase.
Oblasti važenja koje definišu ugnežđena i okružujuća klasa
međusobno su nezavisne i ne postoje nikakva posebna prava
pristupa između ovih klasa. To znači da se unutar ugnežđene klase,
bez ikakvih ograničenja, mogu koristiti samo imena tipova, statičkih
članova i konstanti tipa nabrajanja iz okružujuće klase. Pristup svim
ostalim članovima okružujuće klase moguć je samo preko
operatora . i -> i to samo kada su oni javni (public). Pristup bilo
kom članu iz ugnežđene klasemoguć je takođe samo preko
operatora . i -> pod uslovom da su članovi javni.
Činjenica da je jedna klasa definisana u drugoj ne znači da
okružujuća klasa sadrži član tipa ugnežđene klase, već samo da
njeno ime pripada oblasti važenja okružujuće klase.
Funkcije članice ugnežđene klase mogu, ali ne moraju da budu
definisane u samom telu (inline) definicije klase kojoj pripadaju.
Ukoliko se želi da se funkcija članica ugnežđene klase definiše izvan
definicije ugnežđene klase, tada se ona mora definisati u oblasti
važenja datoteke, tj. izvan okružujuće klase. U tom slučaju ispred
imena klase mora da stoji prvo ime okružujuće klase, zatim
operator dosega (::), zatim ime ugnežđene klase i na kraju ponovo
operator dosega.
ime_okružujuće_klase :: ime_ugnežđene_klase ::
ime_funkcije_članice
Statički članovi podaci ugnežđene klase moraju da budu definisani
izvan ugnežđene klase, ali i izvan okružujuće klase, tj. u oblasti
važenja datoteke. Ime statičkog člana podatka iz
ugnežđene klasemora da se označi na isti način kao i
ime funkcije članice ugnežđene klase:
ime_okružujuće_klase :: ime_ugnežđene_klase ::
ime_statičkog_člana_podatka

Primer:
class out
{
int x ;
public:
class inner //Definicija ugnezdjene klase.
{
static int a ;
int b ;
public:
int c ;
void f ( out ) ;
} ;
static int y ;
void g ( inner ) ;
} ;

int out::y = 5 ;

void out::g ( inner in )


{
int i =in.b ; //GRESKA: b je privatni clan klase in!
int j = c ; //GRESKA: c je clanklase in, mora preko .
ili ->
int k = in.c ; //Ispravan pristup.
}

int inner::a = 0 ; //GRESKA: imea je unutar


prostora klase out!
int out::inner::a = 0 ; //Ispravno.
//Zaglavlje definicije funkcijeclanice ugnezdjene klase.
void out::inner::f ( out o )
{
int i =o.y ;
int j = y ; //Ispravno, jer je ystaticki clan
okruzujuce klase.
int k = o.x ; //GRESKA: x je privatni clan klase out!
int l = x ; //GRESKA: x jenestaticki clan klase out,
// mora preko operatora . ili ->
}

int main ( )
{
out a ; //Objekatokruzujuce klase.
out::inner b ; //Objekat ugnezdjene klase.
b.f(a) ;
return 0 ;
}
9. Lokalne klase
Klase je moguće definisati i unutar funkcija. Takve klase nazivaju se
lokalnim klasama. Imena ovakvih klasa imaju oblast važenja
blokova u kojima se definicije nalaze. Unutar ovakvih klasa
dozvoljeno je korišćenje samo imena tipova, statičkih
članova funkcije i konstanti tipa nabrajanja iz okružujuće funkcije.
Pristupanje članovima klase iz okružujuće funkcije podleže kontroli
pristupa, tj. okružujuća funkcija nema nikakva posebna prava
pristupa članovima lokalne klase. Funkcije članice lokalnih klasa
moraju se definisati unutar definicija klasa (inline). Lokalne klase ne
mogu da imaju statičke članove.
Primer:
int main ( )
{
static double pi = 3.14 ;
class local //Definicija lokalne klase.
{
double d ;
static int s2 ; //GRESKA: lokalna klasa ne moze
//imati staticke clanove!
public:
int s ;
void test ( ) { d = pi ; } //Ispravno:unutar
lokalne klase dozvoljeno je
// koriscenje statickih objekataiz okruzujuce
// funkcije.
void test2 ( ) ; //GRESKA: funkcija clanica
lokalne
// klase mora biti definiasna u telu klase!
} ;
local l ;
l.d = 0 ; //GRESKA: d je privatni clan lokalne klase!
l.test ( ) ;
return 0 ;
}
10. Pokazivači na članove klase
Pokazivači na članove klase (class member pointers) predstavljaju
poseban izvedeni tip podataka različit od "klasičnih" pokazivača na
objekte. Treba razlikovati pokazivače na članove podatke od
pokazivača na funkcije članice. Prvi, tj.pokazivači na članove
podatke deklarišu se na sledeći način:
T X ::* identifikator ;
T predstavlja tip podatka člana klase, X predstavlja klasu na čiji član
podatak pokazuje pokazivač, aidentifikator predstavlja ime
pokazivača.
Ovakvi pokazivači suštinski se razlikuju od "klasičnih" pokazivača,
jer za nestatičkečlanove podatke ne sadrže adresu podatka na koji
pokazuju, već sadrže vrednost pomeraja (offset) uodnosu na
početak strukture podataka koja predstavlja objekat klase u
memoriji. Pokazivači na statičke članove podatke sadrže adresu
podatka poput "klasičnih" podataka.
Pokazivači na funkcije članice deklarišu se na sledeći način:
T ( X ::* identifikator ) ( <lista_tipova_argumenata> ) ;
identifikator predstavlja ime pokazivača na funkciju
članicu klase, X predstavlja klasu u kojoj se nalazi funkcija članica,
a T i lista_tipova_argumenata određuju tip funkcije članice na
koju pokazuje pokazivač. Za razliku od pokazivača na član
podatak klase, ovi pokazivači sadrže adresu koda funkcije članice
poput običnih pokazivača na funkcije. Ipak, ovi pokazivači se
razlikuju od običnih po tome što funkcije na koje pokazuju u listi
argumenata uvek imaju i pokazivač na objekat tipa klase u kojoj se
funkcija članica nalazi (this pokazivač). Naravno, ovo se odnosi na
pokazivače na nestatičke funkcije članice. Pokazivači na
statičke funkcije članice isti su kao pokazivači na globalne funkcije.
Za pokazivače na nestatičke funkcije članice važi da se njihovo ime
ne može implicitno konvertovati u pokazivač
na funkcije članice klase, za razliku od imena globalne funkcije čije
ime može da se implicitno konvertuje u pokazivač na odgovarajući
tip funkcije.
Važno je naglasiti da je nad pokazivačima na nestatičke članove
klasa moguće primenjivati samo operacije poređenja (== i !=), te
operator dodele (=). Pokazivači na članove klase čuvaju rezultat
primene adresnog operatora nad imenom člana klase. Mogu da
budu deklarisani i kao const i/ilivolatile.
Korišćenjem pokazivača na člana klase, konkretnom članu
objekta klase moguće je pristupiti na dva načina:
- pomoću operatora .* (objekat .* pokazivac_na_clana_klase),
- pomoću operatora ->* (pokazivac_na_objekat ->*
pokazivac_na_clana_klase).

Primer:
class R
{
int x, y ;
void test1 ( ) { } ;
public:
float z ;
void test ( int ) { } ;
} ;

class Q
{ public: float k ;} ;

int main ( )
{
R a, b ;
Q c ;
float * f ;
int R::* pri = &R::x ; // GRESKA: x je privatni
clan klase R!
float R::* prf = &R::z ;
float Q::* prf2 = &Q::k ;
prf2 = prf ; // GRESKA: prf i prf2 nisu istog tipa!
f = prf ; // GRESKA: prf i fnisu istog tipa!
a.*prf = 1.2 ; // a.z = 1.2 ;
b.*prf = -4.2 ; // b .z = -4.2 ;
c.*prf = 0.3 ; // GRESKA: prfnije pokazivac na
clana klase Q!
c.*prf2 = 0.3 ; // c.k = 0.3 ;
R * pr = & a ;
pr->*prf = 0 ; // pr->z = 0;
void ( R::* mfp ) ( ) ;
void ( R::* mfp2 ) ( int ) ;
mfp = & R::test1 ( ) ; // GRESKA: test1 je privatna
clanica klase R!
mfp2 = & R::test ;
( a.* mfp ) ( ) ; // OPASNO:pokazivac mfp ima slucajan
sadrzaj!
( a.* mfp2 ) ( 1 ) ; //a.test(1) ;
( pr->* mfp2 ) ( 1 ) ; // pr->test(1) ;
return 0 ;
}
11. Strukture u programskom jeziku C++
Strukture u programskom jeziku C++ imaju sve osobine klasa osim
činjenice da su im članovi podrazumevano javni.
Primer:
struct H
{
int x ;
void fun ( ) { }
private:
void whs ( ) { }
} ;

int main ( )
{
H a, b ;
a.x = 5 ; //Ispravno, x je javniclan strukture.
b.fun ( ) ; //Ispravno, fun je javna funkcija clanica
// strukture.
a.whs ( ) ; //GRESKA: whs je privatna
clanica strukture!
return 0 ;
}
12. Imenski prostori
U velikim programima često se javljaju problemi prilikom korišćenja
globalnih imena, tj. imena iz oblasti važenja datoteke. Naime, veliki
programi obično se sastoje iz većeg broja *.cpp i *.h datoteka koje,
između ostalog, imaju određen broj globalnih imena. Globalna
nestatička promenljiva ili objekat, kao i globalna funkcija u jednoj
datoteci, vidljivi su i iz ostalih datoteka. Ukoliko se desi da se neko
ime pojavi kao globalno u više *.cpp datoteka, mogu da nastanu
problemi prilikom povezivanja programskih modula (linking).Sličan
problem može da se desi i ukoliko se u više *.cpp datoteka uključi
istu *.h datoteku koja sadrži globalna imena.
Da bi se prevazišao ovaj problem u programski jezik C++ uveden je
koncept imenskih prostora (namespaces). Oni služe za grupisanje
globalnih imena tako što svaki imenski prostor predstavlja posebnu
oblast važenja imena. Ako se različiti delovi programskog koda
stave u različite imenske prostore, eliminiše se mogućnost
"konflikta" imena.
Imenski prostor definiše se na sledeći način:
namespace identifikator { <sadržaj imenskogprostora> }
namespace predstavlja ključnu reč programskog jezika
C++, identifikator predstavlja ime imenskog prostora, a sadržaj
imenskog prostora je programski kôd.
Važno je primetiti da se na kraju definicije imenskog
prostora ne nalazi zn ak ";".
Unutar jednog imenskog prostora svi globalni identifikatori iz tog
prostora koriste se bez ikakvih ograničenja. Izvan imenskog
prostora, globalni identifikatori iz imenskog prostora moraju se
koristiti uz pomoć operatora za razrešenje dosega i to na sledeći
način:
ime_imenskog_prostora :: globalni_identifikator
Ukoliko unutar jedne oblasti važenja imena postoji potreba da se
koristi neko ime iz nekog imenskog prostora bez navođenja imena
imenskog prostora i operatora za razrešenje dosega, moguće je
upotrebiti sledeću naredbu:
using ime_imenskog_prostora :: globalni_identifikator ;
using predstavlja ključnu reč u programskom jeziku C++.
Ukoliko postoji potreba da se unutar jedne oblasti važenja imena
koriste sva imena iz nekog imenskog prostora bez navođenja
njegovog imena i operatora ::, moguće je upotrebiti sledeću
naredbu:
using namespace ime_imenskog_prostora ;
Važno je naglasiti da su imenski prostori "otvoreni", što znači da se
identifikator koji označava ime imenskog prostora može koristiti za
više definicija imenskog prostora, pri čemu će svaka nova definicija
dodati novi sadržaj istom imenskom prostoru označenom tim
identifikatorom. Odnosno, program prevodilac neće prijaviti grešku
ukoliko naiđe na više definicija imenskog prostora sa istim imenom.
Primer:
namespace A
{
int x = 12 ;
float a = -1.0 ;
void hint ( ) { }
class T
{
public:
int x ;
} ;
}
namespace B
{ int a = 3 ; }

namespace A // Ispravno, nije redefinicija; sada je i ‘c’


{ char c ; } // deo imenskogprostora A.

int main ( )
{
T t ; //GRESKA: tip T nijeglobalno vidljiv!
A::T t ; //Ispravno.
using A::T ; //Ukljucivanje imena T iz imenskog
prostora A.
T t2 ; //Sada moze i ovako.
using namespace B ;
int x = a ; //Ispravno, ‘a’ iz imenskog prostora B.
usingnamespace A ; //Ukljucivanje citavog imenskog
prostora A.
float y = a ; //GRESKA: koje‘a’ je u pitanju,
// ‘a’ iz A ili ‘a’ iz B ?!
float y = A::a ; //Moraeksplicitno da se navede koje
// ‘a’ je u pitanju.
return 0 ;
}

You might also like