Upload
others
View
3
Download
0
Embed Size (px)
Citation preview
Uvod u OOP – vežbe C++ i Java
Vežbe iz predmeta Uvod u objektno-orijentisano programiranje, I god. II semestar. Školska
2012/2013
1
Programski jezik C++
1. Razlike C i C++-a neobjektne prirode
Zadatak 1. 1
Implementirati funkciju max koja daje maksimalni element celobrojnog niza, maksimum dva
cela broja, maksimalni od dva stringa, "maksimalni" od dva karaktera, za jedan string karakter
sa najvećom vrednošću unutar stringa.
_____________________________________________________________________________
#include <iostream> #include <string.h> using namespace std; int max(int *,int); inline int max(int,int); char * max(char *, char *); inline char max(char,char); char max(char*); void main(){ int a[]={1,5,4,3,2,6},n=6; cout<<max(a,n)<<endl; cout<<max("string","program")<<endl; cout<<max('a','A')<<endl; cout<<max(2,5)<<endl; cout<<max("string"); } int max(int *a,int n){ int m; m=a[0]; for(int i=1;i<n;i++) if(a[i]>m) m=a[i]; return m; } inline int max(int a,int b){ return a>=b?a:b; } char * max(char *a,char *b){ if(strcmp(a,b)>=0) return a; else return b; } inline char max(char a,char b){ return a>=b?a:b; } char max(char *a){ char m; int i; m=a[0]; i=1; while(a[i]!='\0') { if(a[i]>m)
2
m=a[i]; i++; } return m; }
_____________________________________________________________________________
U ovom zadatku je ilustrovana upotreba inline funkcija i preklapanje imena funkcija. Na
samom početku je potrebno obratiti pažnju na korišćenje biblioteke iostream.h kako bi se
omogućila upotreba naredbi za unos i očitavanje podataka cin>> i cout<<, respektivno. Funkcije
su realizovane redosledom kojim su navedene u postavci zadatka. U samoj deklaraciji funkcija ne
uočavaju se razlike u odnosu na programski jezik C, osim kod funkcija za poređenje dva cela broja
i dva karaktera. Ove funkcije su realizovane kao inline funkcije što znači da će prilikom svakog
pozivanja neke funkcije ista biti ugrađena u kod. Iz same definicije inline funkcije se može
zaključiti da je potrebno voditi računa o tome koja će funkcija biti realizovana kao takva. Naime,
kao inline se realizuju samo one funkcije koje su veoma jednostavne te bi vreme koje je potrebno
za prenos podataka programa na stek, prosleđivanje stvarnih argumenata funkciji, a nakon
izvršavanja programa brisanje podataka koji pripadaju funkciji i uzimanje podataka sa steka, bilo
duže od izvršavanja same funkcije. Ključna reč inline ispred zaglavlja funkcije govori kompajleru
da je u pitanju inline funkcija. Primjećujemo da sve funkcije imaju isto ime, što u programskom
jeziku C nije bilo moguće. U C++ je moguće funkcijama koje obavljaju srodne radnje, nad
različitim tipovima podataka, dati ista imena. Za svaki tip i broj podataka je potrebno zasebno
realizovati funkciju. Ovde se mora voditi računa da se sve funkcije razlikuju po broju ili tipu
argumenata, kako ne bi nastupila dvosmislenost prilikom njihovog pozivanja što bi izazvalo
grešku pri kompajliranju, o čemu će biti reči u nekom od kasnijih primera.
Prva funkcija, za određivanje maksimalnog elementa niza, je realizovana na isti način kao što bi
se to odradilo u programskom jeziku C. Niz je prosleđen preko pokazivača, kako se radi o nizu
celih brojeva prosleđen je i broj elemenata niza. U funkciji se prvi element niza proglašava za
najveći, zatim se, u for petlji, vrši pretraživanje niza od drugog do poslednjeg elementa. Svaki
element se poredi sa trenutno maksimalnim i ukoliko je veći od njega proglašava se
maksimalnim. Kao rezultat se vraća vrednost promenljive m u kojoj je smeštena vrednost
maksimalnog elementa niza. Treba obratiti pažnju na zaglavlje for petlje, u njemu postoji jedna
razlika u odnosu na programski jezik C. Naime, u C++ je moguće izvršiti definiciju promenljive u
okviru prvog izraza u naredbi for. Doseg tako uvedene promenljive je do kraja tela te for petlje {}.
Nakon njenog izvršavanja, tako definisana promenljiva više ne postoji.
Druga funkcija, koja daje maksimum dva cela broja, je realizovana kao inline. Kompajler
to zna zbog navođenja ključne reči inline ispred standardnog zaglavlja ove funkcije. Za njenu
realizaciju smo koristili ternarni operator ? : koji ispituje da li je a veće od b, ako jeste kao
rezultat funkcije se vraća a a u suprotnom b.
Treća funkcija upoređuje dva stringa i vraća pokazivač na veći od njih. Kao i u
programskom jeziku C, i ovde je string realizovan kao niz karaktera. Poređenje se vrši funkcijom
strcmp() čija se upotreba ne razlikuje u odnosu na programski jezik C a omogućena je
uključivanjem bibloteke string.h.
Funkcija za poređenje dva karaktera je, zbog svoje jednostavnosti, realizovana kao inline.
3
Narednom funkcijom se traži karakter sa najvećom vrednošću u stringu. Ovde je
iskorišćena činjenica da se string realizuje kao niz karaktera i da se kraj tog niza označava kao
’\0’, tako da funkciji nije potrebno proslijeđivati dužinu niza već se u njoj brojač i postavlja na 0 i
inkrementira u petlji sve dok ne dođe do kraja niza ’\0’.
Primer rada programa
_____________________________________________________________________________
6
string
a
5
t
_____________________________________________________________________________
Protumačiti za koji poziv u glavnom programu će se izvršiti koja funkcija. Vidimo da sve funkcije
izvršavaju blisku radnju, traženje maksimuma, i da je samimim tim preklapanje imena funkcija
potpuno opravdano.
Zadatak 1.2
Postoje najave funkcija:
int f(int,char='0');
int f(char,char);
int f(double);
Da li će sledeći pozivi izazvati sintaksu grešku i koja će funkcija biti izvršena?
b=f('0');
b=f(2.34);
c=f('c',3);
Obrazložiti.
_____________________________________________________________________________
Uočimo da i ovde sve funkcije imaju isto ime. Iskoristili smo još jednu novinu
programskog jezika C++, a to je da funkcije mogu imati podrazumevane vrednosti za neke
formalne argumente. Ovo znači da prvu funkciju iz našeg primera možemo pozvati sa samo
jednim stvarnim argumentom, pri čemu će se za vrednost drugog argumenta uzeti
podrazumevana vrednost. Ukoliko navedemo dva argumenta u pozivu funkcije, za vrednost
drugog argumenta se uzima navedena, a ne podrazumevana vrednost. S obzirom da u našem
programu sve funkcije imaju isto ime, na osnovu ranije navedenog, znamo da će se razrešavanje
prilikom poziva funkcije izvršiti na osnovu tipova stvarnih argumenata. Posmatramo kojoj funkciji
najbolje odgovaraju argumenti za koje je pozvana.
4
Prvi poziv kao stvarni argument ima tip char. Prvi argumet u drugoj funkciji jeste tipa char ali se
ona ne može izvršiti za ovaj poziv, jer se funkcija poziva sa samo jednim stvarnim argumentom a
ona očekuje dva, jer drugi nema podrazumevanu vrednost. Za jedan argument se mogu pozvati
prva i treća funkcija. S obzirom da postoji standardna konverzija iz tipa char u tip int izvršiće se
prva funkcija. Poziv će, zapravo, biti f('0','0').
Drugim pozivom će se, bez sumnje, izvršiti poslednja funkcija. Naime, jedino ova funkcija
ima float argument.
Kod trećeg poziva je veći problem jer i prva i druga funkcija mogu da odgovaraju prvom
argumentu (bolje odgovara druga funkcija) ali drugi argument ne odgovara ni jednoj funkciji tako
da će doći do greške u programu.
Zadatak 1. 3
Dat je niz celobrojnih elemenata. Kreirati funkciju koja po defaultu okreće elemente niza
"naopačke", a ako je specificiran celi broj veći od nula formira novi niz koji predstavlja
elemente starog niza od prvog sa datim preskokom do kraja, a za negativan broj ide od
poslednjeg elementa niza ka početku sa odgovarajućim preskokom.
_____________________________________________________________________________
#include <iostream> #include <conio.h> using namespace std; int uredi(int *, int,int *,int=-1); main(){ int a[]={2,3,2,1,5},c; int i, b=5, * r; r=new int[b]; c=uredi(a,b,r); for(i=0;i<c;i++) cout<<r[i]; cout<<endl; c=uredi(a,b,r,2); for(i=0;i<c;i++) cout<<r[i]; cout<<endl; c=uredi(a,b,r,-2); for(i=0;i<c;i++) cout<<r[i]; cout<<endl; delete []r; r=0; getch(); } int uredi(int *a, int n,int *c, int m){ int j=0; if(m>0) for(int i=0;i<n;j++,i+=m) c[j]=a[i]; else for(int i=n-1;i>=0;j++,i+=m) c[j]=a[i]; return j; }
5
_____________________________________________________________________________ Treba obratiti pažnju da se i u ovom zadatku, kao i u prethodnom, podrazumevani
argumenti nalaze na kraju. Naime, ukoliko imamo podrazumevanu vrednost formalnog
argumenta, nakon njega sme biti samo argument sa podrazumevanom vrednošću. Ovo je i
logično, jer će se prilikom poziva funkcije stvarni argumenti dodjeljivati formalnim onim
redoslijedom kojim su ispisani. Prilikom odlučivanja koju vrednost da uzmemo kao default,
iskoristili smo činjenicu da obrtanje elemenata niza "naopačke", u stvari, jeste formiranje novog
niza od poslednjeg ka prvom elementu sa preskokom 1, minus govori da se počne od kraja niza.
Funkcija za preuređivanje je realizovana tako što joj se šalje pokazivač na niz koji treba
preurediti, broj njegovih elemenata i pokazivač na početak niza u koji ćemo smjestiti preuređene
elemente. Kao rezultat se vraća broj elemenata novonastalog niza. U glavnom programu se vrši
dinamičko zauzimanje memorije, na isti način kao ranije. U funkciji se postavlja brojač j na nulu,
zatim se proverava da li treba uzimati elemente od početka (m>0) ili kraja niza (m<0). U oba
slučaja se za preskok koristi naredba i+=. U prvom slučaju će to povećavati indeks i za m a u
drugom umanjivati jer je m negativno. j predstavlja broj elemenata novog niza.
U ovom programu se koristi naredba cout<<endl, која predstavlja prelazak u novi red. Može se, i
dalje, koristiti cout<<'\n'.
Obratite pažnju da je vrednost podrazumevanog argumenta navedena samo u zaglavlju funkcije
prilikom njegove deklaracije. Naime, podrazumevana vrednost se ne može redefinisati, čak ni u
istu vrednost. Ovo znači da, kada imamo odvojeno deklaraciju i definiciju funkcije,
podrazumevanu vrednost navodimo samo u deklaraciji a ne i u kompletnoj definiciji funkcije.
U našem primeru se vrši dinamička alokacija memorije. Za to smo koristili operator new. r=new
int[b]; govori kompajleru da želimo da zauzmemo memoriju u koju može da se smjesti niz od b
elemenata cjelobrojnog tipa, kao i da vrednost pokazivača na početak te memorije smještamo u
promenljivu r. Na kraju programa je potrebno osloboditi memoriju koju je niz zauzimao. Za to
koristimo operator delete (delete []r;). Uglaste zagrade pre pokazivača p se koriste uvek kada
imamo niz, tako govorimo da je potrebno osloboditi memoriju koju je zauzeo cio niz a ne samo
prvi element. Nakon ovog, pokazivač r će i dalje sadržati adresu prvog elementa niza čiji smo
sadržaj upravi izbrisali. To nikako nije poželjno jer nam pokazivač pokazuje na nešto što više ne
postoji. Naredbom r=0 ostavljamo ga u ispravnom, praznom, stanju. Ne ukazuje ni na jednu
memorijsku adresu. Kada bismo u glavnom programu zahtevali da se prvo stvara novi niz sa
preskokom 2 od početka, a nakon toga sa preskokom 3 od kraja, ukoliko isčitamo sve elemente
drugog niza (ima ih 3) u drugom slučaju bi kao treći imali element 5, koji ne bi trebalo da bude
deo tog niza. Zašto je došlo do toga i kako to da eliminišemo? Zašto smo u funkciji uredi(...)
definisali promenljivu j van zaglavlja for petlje? Da smo je deklarisali kada i promenljivu i da li bi
nam nakon izvršavanja for petlje bila sačuvana vrednost koja se nalazi u njoj? Da li bi ta
promenljiva bila definisana nakon izvršavanja petlje.
Primer rada programa
_____________________________________________________________________________
51232
6
225
522
_____________________________________________________________________________
Zadatak 1. 4
Napisati program za računanje težine tela. Ako je zadat jedan argument računati težinu na
površini zemlje Q=m*9.81, ako su zadata dva argumenta računati na visini r iznad zemlje:
Q=6.672*10-11
*m*4.9156*1024
/(6371377+r)2; a ako je zadato m, r i M težinu na rastojanju r od
planete mase M: Q=6.672*10-11
*m*M/r2.
_____________________________________________________________________________
#include <iostream> using namespace std; float tezina(int); float tezina(int,int); float tezina(int,int,int); main(){ int n, m, M, r; cout<<"Koliko argumenata zelite da unesete?"<<endl; cin>> n; if(n==1){ cin>>m; cout<<tezina(m)<<endl; } else if(n==2){ cin>>m>>r; cout<<tezina(m,r)<<endl; } else { cin>>m>>r>>M; cout<<tezina(m,r,M)<<endl; } } float tezina(int a){ const float k=9.81; return a*k; } float tezina(int a, int b){ const float k=6.672*pow(10,-11)*4.9156*pow(10,24), k1=6371377; return k*a/pow((k1+b),2); } float tezina(int a, int b, int c){ const float k=pow(10,-11)*6.672; return k*a*c/pow(b,2); }
_____________________________________________________________________________
Ovde se, na samom početku, mora zapaziti da je nemoguće uspostaviti vezu između ova tri
načina za računanje težine tela, tako da realizacija sa podrazumevanim parametrima odmah
otpada. Funkcije obavljaju srodnu aktivnost pa će se iskoristiti mogućnost preklapanja imena. Što
se tiče same realizacije pojedinih funkcija, ne postoji razlika u odnosu na programski jezik C.
7
Primer rada programa
_____________________________________________________________________________
Koliko argumenata zelite da unesete?
1
60
588.6
Koliko argumenata zelite da unesete?
2
60
2
484.74
_____________________________________________________________________________
2. Klase
Zadatak 2. 1
Projektovati klasu sa elementarnim operacijama nad kompleksnim brojevima (sabiranje,
oduzimanje).
_____________________________________________________________________________
#include <iostream> using namespace std; class complex{ float imag, real; public: void cUcitaj(float, float); complex cSab(complex); complex cOduz(complex); float cRe()const {return real;}; float cIm()const {return imag;}; }; void complex::cUcitaj(float a, float b){ real=a; imag=b; } complex complex::cSab(complex a){ complex priv; priv.real=real+a.real; priv.imag=imag+a.imag; return priv; } complex complex::cOduz(complex a){ complex priv; priv.real=real-a.real; priv.imag=imag-a.imag; return priv; }
8
main(){ complex c1, c2, c3; float a, b; cout<<"Unesi vrednost za realni i imaginarni deo c1"<<'\n'; cin>>a>>b; c1.cUcitaj(a,b); cout<<"Unesi vrednost za realni i imaginarni deo c2"<<'\n'; cin>>a>>b; c2.cUcitaj(a,b); c3=c1.cSab(c2); cout<<"Zbir dva data kompleksna broja je"<<"("<<c3.cRe()<<","\ <<c3.cIm()<<")"<<endl; c3=c1.cOduz(c2); cout<<"Razlika dva data kompleksna broja je"<<"("<<c3.cRe()<<","\ <<c3.cIm()<<")"<<endl; }
_____________________________________________________________________________
U ovom zadatku je prvo izvršena definicija klase. Na početku ove definicije se nalazi
rezervisana reč class, nakon ove reči pišemo identifikator, odnosno ime klase. Između vitičastih
zagrada se nalazi deklaracija promjenjivih i funkcija, a može biti i definicija neke funkcije.
Obavezno je nakon vitičastih zagrada staviti ; jer to govori kompajleru da smo završili definiciju
klase i da nakon toga možemo definisati pojedine funkcije članice. Koji će članovi klase i funkcije
biti vidljivi van klase određuju rezervisane reči public i private. Ukoliko ih nema podrazumeva se
da su svi podaci članovi i funkcije članice privatni. Praksa je da se podaci članovi postavljaju za
privatne a funkcije članice za javne. U ovom primeru je tako učinjeno. Iz primera se vidi da će ako
se ne navede ključna reč private, početni deo klase, pre prve rezervisane reči public, biti privatan.
Dobra programerska praksa je da se uvek eksplicitno navede koji je deo klase privatan. Klasa,
zapravo, predstavlja korisnički definisan tip podataka. Isto kao za ugrađene tipove podataka i za
ovaj, korisnički definisan, možemo uvoditi instance, primerke tog tipa. Primerak klase je objekat i
dekleriše se isto kao i primerak ugrađenog tipa, navođenjem identifikatora tipa, complex, a
nakon njega identifikatora za konkretan primerak tog tipa complex c1,c2,c3;. Mi ovde želimo
definisati tip podataka koji će imati osobine kompleksnih brojeva. Znači, želimo omogućiti
dodeljivanje vrednosti kompleksnom broju, očitavanje njegovog realnog i imaginarnog dijela, kao
i sabiranje dva kompleksna broja. S obzirom da smo promenljive koje predstavljaju realni i
imaginarni deo kompleksnog broja realizovali kao privatne podatke članove, za izvršavanje ranije
navedenih operacija potrebno je realizovati odgovarajuće funkcije članice klase, one imaju pravo
da pristupaju podacima članovima iste klase koji su privatni. U našem programu imamo 6
funkcija članica. Prva služi za inicijalizaciju objekta klase complex (primeraka klase) i ona će
dodeliti odgovarajuće vrednosti podacima članovima objekta za koji je pozvana. Vidimo da
funkcije članice pored svojih formalnih argumenata sadrže i podrazumevani argument a to je
objekat za koji su pozvane (odnosno pokazivač na taj objekat). Članovima tog objekta može da se
pristupa navođenjem samo identifikatora (imena) podatka člana. Naime, u ovoj funkciji smo
dodeljivanje vrednosti realnom dijelu objekta, za koji ćemo je pozvati, izvršili naredbom real=a;
(na isti način smo odradili i za imaginarni deo). Sada pogledajmo što će se, zapravo, desiti kada u
glavnom programu pozovemo funkciju c1.cUcitaj(a,b);. Mi smo ovu funkciju pozvali za konkretan
objekat c1 pa će se pristupati njegovom podatku članu real. Dakle, funkcija može direktno, bez
navođenja imena objekta kojem pripadaju, da pristupa članovima objekta za koji je pozvana. Iz
tog razloga kada je potrebno obaviti neku radnju samo sa jednim objektom nema potrebe da se
šalje taj objekat kao argument funkcije, funkcija može da pristupa njegovim podacima direktno.
9
Ukoliko želimo da obavimo neku radnju nad više objekata uvek šaljemo za jedan manje, jer će
objekat za koji pozivamo funkciju biti dostupan samim pozivom te funkcije a ostale moramo
poslati kao argumente funkcije. Funkcija cSab() vrši sabiranje dva kompleksna broja. Rezultat je
tipa complex jer je zbir dva kompleksna broja takođe kompleksan broj. Sada je kao argument
potrebno poslati jedan objekat (jedan kompleksan broj) dok će se članovima drugog, za koji je
funkcija pozvana, moći pristupiti direktno. Naime, naredbom c3=c1.cSab(c2); (u glavnom
programu) vršimo sabiranje dva kompleksna broja c1 i c2, pri čemu smo c2 poslali kao stvarni
argument a podacima objekta c1 smo mogli direktno pristupiti, pokazivač na njega this je
implicitni argument ove funkcije. Obratiti pažnju da argumenti kao i rezultati funkcije članice
mogu biti tipa complex.
Kada se vrši realizacija funkcije izvan definicije klase, klasa kojoj pripada funkcija se mora
naglasiti u zaglavlju funkcije navođenjem imena klase nakon kojeg imamo operator dosega ::. Na
ovaj način kompajler zna da je funkcija čije ime navodimo posle :: članica date klase. Ukoliko je
neka funkcija realizovana u okviru definicije klase biće implicitno inline. Kod nas su to funkcije
cRe() i cIm(). Vidimo da će ove funkcije vratiti vrednost realnog i imaginarnog dijela kompleksnog
broja (objekta) za koji su pozvane. (Napomenimo, još jednom, da se ovo mora učiniti na ovaj
način jer ukoliko nam bude potreban realan deo objekta c3 u glavnom programu mu ne možemo
pristupiti naredbom c3.real; jer je real privatan podatak klase complex i njemu ne možemo
pristupati direktno.). Nakon navođenja liste parametara ovih funkcija imamo rezervisanu reč
const što znači da je data funkcija inspektor, ne menja stanje objekta klase, dok mutatori
menjaju. Realizacija funkcije cOduz() je izvršena veoma slično kao i funkcije za sabiranja pri čemu
je praćena logika oduzimanja kompleksnih brojeva jer je nama cilj da modeliramo kompleksne
brojeve.
U glavnom programu, prilikom očitavanja rezultata sve naredne nisu stale u jedan red pa
smo ih prebacili u drugi iz pomoć \. Ovim je kompajleru dato na znanje da sve naredbe koje slede
nakon ovog znaka pripadaju prethodnom redu. Važno je zapamtiti da ukoliko imamo funkciju bez
argumenata koje joj eksplicitno šaljemo, kao kod nas cIm() i cRe(), moramo, uvek, prilikom
njihovog pozivanja navoditi zagrade posle identifikatora te funkcije, jer ukoliko bismo napisali
cRe a ne cRe(), kompajler bi mislio da nam je to identifikator za promenljivu a ne za funkciju i
javio bi grešku.
Primer rada programa
_____________________________________________________________________________
Unesi vrednost za realni i imaginarni deo c1
1 2.5
Unesi vrednost za realni i imaginarni deo c2
-3 8.9
Zbir dva data kompleksna broja je(-2,11.4)
Razlika dva data kompleksna broja je(4,-6.4)
_____________________________________________________________________________
10
Zadatak 2.2
Projektovati klasu sa elementarnim operacijama nad tačkama u ravni (računanje udaljenosti
zadate tačke od koordinatnog početka, rastojanja dve tačke). U glavnom programu uneti
podatke za dve tačke a dati na izlazu koordinate tačke koja je na većem rastojanju od
koordinatnog početka i rastojanje te dve tačke. Koordinate tačke su privatni članovi klase.
_____________________________________________________________________________
#include <iostream> #include <math.h> using namespace std; class tacka{ double x, y; public: void pravi(double a, double b){x=a;y=b;} double aps()const{return x;} double ord() const{return y;} double poteg()const; double rastojanje(tacka) const; void ispisi() const; }; double tacka::poteg()const{ return sqrt(pow(x,2)+pow(y,2)); } double tacka::rastojanje(tacka a)const{ return sqrt(pow(x-a.x,2)+pow(y-a.y,2)); } void tacka::ispisi()const{cout<<"("<<x<<","<<y<<")"<<"\n";} void main(){ int n; tacka t1; cout<<" Unesite koordinate prve tacke "<<endl; double x, y; cin>>x>>y; t1.pravi(x,y); tacka t2; cout<<" Unesite koordinate druge tacke "<<endl; cin>>x>>y; t2.pravi(x,y); cout<<"Koordinate dalje tacke su: "; t1.poteg()>t2.poteg()?t1.ispisi():t2.ispisi(); cout<<"Rastojanje zadatih tacaka je: "<<t1.rastojanje(t2)<<'\n'; }
_____________________________________________________________________________
U ovom primeru želimo da definišemo korisnički tip tačka koji će imati neke od osobina tačaka
u Dekartovom koordinatnom sistemu. Zaglavlje za matematičku biblioteku <math.h> je
uključeno jer se koriste matematičke funkcije. Klasa tacka ima dva privatna podatka člana tipa
double. Rad sa ovim podacima omogućen je funkcijama članicama. Iako u postavci zadatka nije
eksplicitno navedeno da je potrebno praviti prve tri funkcije, morali smo ih formirati kako bismo
mogli zadovoljiti zahtjeve iz glavnog programa. Ne možemo pristupiti privatnim podacima iz
glavnog programa neposredno, već je potrebno za to imati funkciju koja ima pravo pristupa. U
ovom slučaju je to funkcija članica pravi(), koja zapravo inicijalizuje podatke članove na željenu
11
vrednost. Ova funkcija je definisana u okviru definicije klase pa će biti realizovana kao inline, što
je i opravdano s obzirom da nije pretjerano komplikovana, svega dve mašinske instrukcije. Kao
argument joj šaljemo dva podatka tipa double, koja će ona dodeliti podacima x i y, objekta za koji
je budemo pozvali u glavnom programu t1 i t2. Rezultat joj je void to jest ne vraća ništa, iako je,
zapravo, rezultat objekat za koji je pozvana sa novododijeljenim vrednostima. Ovo je i logično
ako se posjetimo da svaka funkcija članica ima sakriveni argument this koji je pokazivač na
objekat za koji je pozvana pa, kao što ga nije potrebno slati, nije ga potrebno ni vraćati. Sve će se
radnje nad podacima članovima objekta za koji je funkcija pozvana automatski odraziti na njih jer
je ovaj objekat, implicitno, poslat po referenci. Funkcije aps() i ord() nam daju podatak o apscisi i
ordinati odgovarajuće tačke, objekta za koji su pozvane. Obratiti pažnju na rezervisanu reč const
u zaglavlju nekih funkcija članica. Podsjetiti se iz prethodnog primera šta ona znači. Funkcija
poteg() računa rastojanje tačke od koordinatnog početka po poznatoj formuli i vraća ga kao
rezultat funkcije tipa double. Funkcija rastojanje() računa rastojanje dve tačke, zadate
koordinatama x i y. Jednu tačku šaljemo kao argument funkcije a elementima druge možemo
direktno pristupiti, tačka za koju pozivamo funkciju. Ovde smo realizovali i funkciju ispisi() za
ispisivanje koordinata tačaka u formatu koji se najčešće koristi u matematici a(x,y). U principu bi i
bez ove funkcije mogli ispisati koordinate odgovarajuće tačke ali bi tada morali pozivati dve
funkcije i stalno voditi računa o formatiranju pa je preporučljivo uvek imati funkciju za ispis
podataka klase koju formiramo. Ova funkcija ne vraća rezultat.
U glavnom programu smo uveli (deklarisali) objekat t1 klase tacka, zatim smo, nakon unošenja
koordinata tačaka u dve promenljive tipa double, poslali te dve promenljive kao argumente
funcije pravi() naredbom t1.pravi(x,y);. Rekli smo kompajleru da se u ovoj funkciji direktno
pristupa podacima članovima objekta t1. Na isti način smo formirali objekat t2 i dodijelili mu
vrednost. Naredba t1.rastojanje(t2) šalje funkciji kao stvarni argument objekat t2 dok se
podacima objekta t1 direktno pristupa. Koristeći odgovarajuću funkciju članicu ispisali smo
koordinate tačke koja je udaljenija od koordinatnog početka.
Primer rada programa
_____________________________________________________________________________
Unesite koordinate prve tacke
2.3 4
Unesite koordinate druge tacke
3 1.2
Koordinate dalje tacke su: (2.3,4)
Rastojanje zadatih tacaka je: 2.88617
_____________________________________________________________________________
Zadatak 2.3
Prepraviti zadatak 5 tako da sadrži konstruktore.
_____________________________________________________________________________
# include <iostream>
12
using namespace std; class complex{ float imag, real; public: complex(){}; complex(float, float); complex cSab(complex); complex cOduz(complex); float cRe()const {return real;}; float cIm()const {return imag;}; }; complex::complex(float a, float b):real(a),imag(b){} complex complex::cSab(complex a){ complex priv; priv.real=real+a.real; priv.imag=imag+a.imag; return priv; } complex complex::cOduz(complex a){ complex priv; priv.real=real-a.real; priv.imag=imag-a.imag; return priv; } main(){ float a, b; cout<<"Unesite vrednost za realni i imaginarni deo c1"<<'\n'; cin>>a>>b; complex c1(a,b); cout<<"Unesite vrednost za realni i imaginarni deo c2"<<'\n'; cin>>a>>b; complex c2(a,b); complex c3; c3=c1.cSab(c2); cout<<"Zbir dva data kompleksna broja je "<<"("<<c3.cRe()<<","\ <<c3.cIm()<<")"<<endl; c3=c1.cOduz(c2); cout<<"Razlika dva data kompleksna broja je "<<"("<<c3.cRe()<<","\ <<c3.cIm()<<")"<<endl; }
_____________________________________________________________________________
Kao što se vidi i iz postavke, ova klasa se razlikuje od klase u zadatku 5 samo po tome
što sadrži konstruktore. Konstruktor je funkcija članica klase koja se koristi za automatsku
inicijalizaciju primeraka svoje klase u momentima formiranja tih primeraka. Konstruktor će
biti pozvan automatski svaki put kada bude deklarisan objekat određene klase ili kada je
kreiran na bilo koji drugi način. Data klasa može imati više konstruktora, bez obzira na to
koliko ih ima svi moraju imati isti identifikator (ime) kao i klasa kojoj pripadaju. Primetimo da
se konstruktor razlikuje od ostalih funkcija i po tome što nema označen tip rezultata, to je s
toga što on i nema rezultat koji vraća, nije dozvoljena ni upotreba reči void za označavanje
tipa rezultata koji se vraća. Ukoliko ne definišemo nijednog konstruktora kompajler će sam
definisati podrazumevanog (default). Međutim, ukoliko definišemo makar jednog, moramo i
podrazumevanog jer bi inače naredbe tipa complex c3; koje pozivaju podrazumevanog
13
konstruktora tj. konstruktora bez argumenata, izazvale grešku u kompajliranju. Podsjetimo se
da se bez argumenata može pozvati i funkcija koja ima sve argumente sa podrazumevanim
vrednostima tako da se default konstruktor može realizovati i na taj način, u tom slučaju
ćemo prilikom deklaracije izvršiti i inicijalizaciju datog objekta (dodeljivanje nekih početnih
vrednosti podacima članovima) dok bi deklaracija podrazumevanim konstruktorom, koji nije
realizovan sa podrazumevanim vrednostima argumenata, samo zauzela prostor u memoriji za
njegove podatke, bez dodeljivanja smislenog sadržaja. U zavisnosti od potreba u narednim
primerima ćemo koristiti jedan ili drugi tip realizacije podrazumevanog konstruktora. I za
konstruktore, kao i ostale funkcije članice, važi da će biti realizovani kao inline funkcije ukoliko
su definisani unutar definicije klase. Ukoliko se definišu van definicije klase potrebno je
koristiti ime klase i operator dosega:: kao i kod običnih funkcija članica. U našem slučaju to bi
značilo da je potrebno pisati za drugog konstruktora complex::complex(float a, float
b):real(a),imag(b){}. Prva reč complex ukazuje na to da želimo definisati funkciju članicu te
klase a drugo complex nakon operatora :: govori da je u pitanju konstruktor (ima isto ime kao
klasa kojoj pripada, svojstvo konstruktora). Vidimo da je iskorišćen nov način definicije
funkcije, takozvano navođenje inicijalizacione liste. Ova lista se piše odmah nakon liste
parametara a kompajleru se najavljuje postavljanjem :. Ovde vidimo da će podatak član real
biti inicijalizovan vrednošću a, real(a), na isti način imamo imag(b). Iako smo inicijalizaciju
izvršili van tela funkcije, i ova funkcija nema nijednu naredbu u njemu, svaka funkcija mora
imati telo funkcije. Znači, nakon liste inicijalizatora mora postojati telo funkcije iako u njemu
nema nijedne naredbe, što znači da moramo postaviti vitičaste zagrade, koje označavaju telo
funkcije. Konstruktor se može definisati na isti način kao i sve funkcije. U ovom slučaju bi to
bilo complex::complex(float a, float b){real=a; imag=b;}, može se koristiti i kombinacija ova
dva načina: na primer complex::complex(float a, float b):real(a){imag=b;}. Moramo se odlučiti
samo za jednu od ovih realizacija jer bi ukoliko koristimo više različitih realizacija istog
konstruktora u jednoj klasi došlo do greške. Naime, konstruktori su ovde funkcije sa
preklopljenim imenima pa i za njih važi da moraju imati argumente takve da kompajler
nedvosmisleno zna za koji poziv koju funkciju da realizuje. Ovde bi svi konstruktori bili pozvani
sa dva argumenta tipa float i kompajler bi javio grešku. Mi ćemo, kad god je to moguće,
koristiti novo uvedeni način definisanja konstruktora. On se mora koristiti ukoliko naša klasa
poseduje podatke članove koji su delarisani kao const, njima nije moguće dodeliti neku
vrednost koristeći operator dodele pa se to mora učiniti na ovaj način, ista stvar važi i kada
kao podatak imamo referencu.
14
Konstruktori se ne pozivaju na isti način kao obične funkcije članice. Pogledajmo naredbu
complex c1(a,b); u glavnom programu. Ovom naredbom se poziva drugi konstruktor, koji
očekuje dva argumenta tipa float. Podsjetimo se, neku od funkcija članica bi pozvali
narednom, na primer, c1.cRe(). Znači ime objekta, operator . pa ime funkcije. Konstruktor se
može pozvati samo prilikom inicijalizacije novog objekta i poziva se tako što pišemo ime
novog objekta i u zagradama parametre kojima ćemo ga inicijalizovati, koji se moraju
poklapati sa onima koje konstruktor očekuje. Drugi način pozivanja konstruktora bi bio bez
navođenja parametara u zagradama complex c1; ovom naredbom bi se pozvao
podrazumevani konstruktor koji bi formirao promenljivu c1. Postoji još jedan način pozivanja
konstruktora, complex c1=complex(a,b); ovakav način pozivanja konstruktora se ne
preporučuje jer bi kompajler mogao da stvori privremeni objekat koji bi inicijalizovao pa onda
njega kopirao u c1, što je nepotrebno produžavanje vremena izvršavanja programa. Postoje
neke izuzetne situacije u kojima je ovaj drugi način pozivanja konstruktora bolji, i o tome će
biti reči kasnije. U glavnom programu se za c1 i c2 poziva konstruktor sa parametrima tipa
float dok ze za c3 poziva podrazumevani konstruktor. Ostatak programa protumačite sami.
Primer rada programa
_____________________________________________________________________________
Unesite vrednost za realni i imaginarni deo c1
2 -3.4
Unesite vrednost za realni i imaginarni deo c2
1.2 8
Zbir dva data kompleksna broja je (3.2,4.6)
Razlika dva data kompleksna broja je (0.8,-11.4)
_____________________________________________________________________________
Zadatak 2.4
Realizovati klasu Text koja će predstavljati dinamičke znakovne nizove nad kojima je moguće
izvršiti sledeće operacije: izračunavanje dužine znakovnog niza, čitanje i zamenu zadatog
karaktera u nizu. Konstruisati odgovarajuće konstruktore i destruktore za datu klasu.
#include <iostream> #include <string.h> #include <conio.h> using namespace std;
15
class Text{ private: char *p; public: Text(){p=0;}//Dodeljujemo pokazivacu vrednost 0 Text(const char *);//da upotrebom destruktora ne bismo brisali ~Text(); //pokazivac slucajnog sadrazaja i sam sadrzaj, delete. int tLength(); char tRead(int); void tWrite(int, char); }; Text::Text(const char* m){ p=new char[strlen(m)+1]; strcpy(p,m);} Text::~Text(){ delete []p; p=0; } char Text::tRead(int i){ return p[i]; } void Text::tWrite(int i, char c){ p[i]=c; } int Text::tLength(){ return strlen(p);} void main(){ char *ch={"docar dan"}; Text T(ch); cout<<"Koji karakter zelite da zamenite?"<<endl; int j; cin>>j; cout<<"Koji karakter zelite da postavite"<<endl; char c; cin>>c; T.tWrite(j,c); cout<<"Novi tekst je: "<<endl; int l=T.tLength(); for(int i=0; i<=l;i++) cout<<T.tRead(i); cout<<endl; getch(); }
_____________________________________________________________________________
Ovaj zadatak je lako realizovati ako se ima na umu sve iz prethodnog. Jedina razlika jeste
što nam sad u default konstruktoru postoji naredba za postavljane pokazivača, koji ukazuje na
početak niza, na nulu. To radimo jer je moguće da se desi da se nakon deklaracije nekog objekta
defalult konstruktorom izvrši brisanje tog objekta. Da nemamo naredbu p=0; pokazivaču p bi se
dodjeljivala neka proizvoljna vrednost, on bi ukazivao na nešto u memoriji što bi se brisalo,
nestankom ovog objekta odnosno pozivanjem destruktora. Sledeći konstruktor ne vrši samo
zauzimanje memorije za elemente niza već vrši i njihovu inicijalizaciju, proslijeđen mu je
pokazivač na početak znakovnog niza. Ostatak programa protumačite sami.
Primer rada programa
16
Koji karakter zelite da zamenite?
2
Koji karakter zelite da postavite
b
Novi tekst je:
dobar dan
_____________________________________________________________________________
Zadatak 2.5
Koristeći klasu tačka (koja će sadržati koordinate tačke i funkciju za računanje rastojanja dve
tačke) konstruisati klasu krug koja će kao članove imati tačku centar i neku tačku sa kruga i
funkcije članice za izračunavanje površine i obima kruga.
_____________________________________________________________________________
#include <iostream> #include <math.h> using namespace std; class tacka{ float x,y; public: tacka(){} tacka(float,float); float rastojanje(tacka) const; }; tacka::tacka(float a,float b):x(a){y=b;} float tacka::rastojanje(tacka a)const{ return sqrt(pow(x-a.x,2)+pow(y-a.y,2)); } class krug{ private: tacka centar; tacka sa_kruga; public: krug(){} krug(float, float, float, float); //Moze i na sledeci nacin //krug(tacka, tacka); float kPovrsina(); float kObim(); }; krug::krug(float x1, float x2, float x3, float x4):centar(x1,x2){sa_kruga=tacka(x3,x4);} /* za drugi nacin realizacije bi morali imati naredbe krug::krug(tacka t1, tacka t2):centar(t1){ sa_kruga=t2;}; */ float krug::kPovrsina(){
17
float r; const float pi=3.14159; r=centar.rastojanje(sa_kruga); return pow(r,2)*pi; } N float krug::kObim(){ float r; const float pi=3.14159; r=centar.rastojanje(sa_kruga); return 2*pi*r; } void main(){ cout<<"Unesite koordinate centra kruga i tacke sa kruga"<<'\n'; float x, y, x1,y1; cin>>x>>y>>x1>>y1; /*za drugi nacin realizacije bi morali imati naredbe tacka t1(x,y),t2(x1,y1); krug k1(t1,t2);*/ krug k1(x,y,x1,y1); cout<<"Povrsina kruga je: "<<k1.kPovrsina()<<endl; cout<<"Obim kruga je: "<<k1.kObim(); }
_____________________________________________________________________________
U ovom primeru je pokazano da podatak član klase može biti i objekat druge klase. U
našem slučaju podatak klase krug je objekat klase tacka. Da bi to mogli realizovati potrebno je
pre definicije klase (krug), koja poseduje objekat neke druge klase (tacka), izvršiti, makar,
deklaraciju te klase u našem slučaju je potrebno imati naredbu tipa class tacka; kako bi
kompajler znao da je podatak član nove klase korisnički izvedenog tipa. Mi smo izvršili odmah
kompletnu definiciju klase tacka. Ova klasa ima default konstruktora i konstruktora koji ima dva
argumenta tipa float što je i logično jer ćemo njihove vrednosti dodeliti podacima članovima ove
klase koji su istog tipa. Klasa poseduje i funkciju za računanje rastojanja dve tačke. Klasa krug
poseduje, kao što je zahtevano postavkom zadatka, dva podatka tipa tacka koji predstavljaju
koordinate centra kruga i tačke sa kruga, kao i funkciju za računanje površine i obima kruga.
Potrebno je zapaziti da smo za računanje poluprečnika kruga koristili funkciju članicu klase tacka
koju smo pozivali iz funkcija za računanje površine i obima. Ovo smo morali realizovati na ovaj
način jer iako klasa krug ima kao podatke objekte tipa tacka, centar kruga i tacka sa njegovog
oboda, ne možemo direktno pristupati koordinatama ovih tačaka jer su one privatni podaci
članovi. Njima se može pristupiti samo uz pomoć funkcija članica klase kojoj pripadaju ili uz
pomoć prijateljskih funkcija i klasa o čemu će biti reči kasnije. Dakle, naredba tipa
r=pow(centar.x-sa_kruga.x,2)+pow(centar.y-sa_kruga.y,2); u okviru funkcije za računanje
površine i obima bi izazvala grešku i kompajler bi nam javljao da na tom mestu podaci članovi
tačaka centar i sa_kruga nisu dostupni. Klasa krug, takođe, poseduje odgovarajuće konstruktore.
Realizacija default konstruktora se ne razlikuje od realizacije za ranije korišćene klase ali moramo
voditi računa da klasa čiji su objekti njeni podaci takođe mora imati default konstruktor jer bi
kompajler u suprotnom prijavio grešku, s obzirom da ovaj konstruktor nema argumente i poziva
konstruktora za klasu čiji objekat sadrži bez argumenata, dakle dafault. Sljedeći konstruktor, koji
će ujedno izvršiti inicijalizaciju objekta klase za koji bude pozvan, se može realizovati na više
načina. Mi smo koristili sledeći način krug::krug(float x1, float x2, float x3, float x4) :centar(x1,x2)
{sa_kruga=tacka(x3,x4);}. Ovde vidimo da smo kao argumente konstruktora poslali četri podatka
tipa float a onda eksplicitno pozivali konstruktora za klasu tačka sa odgovarajućim argumentima.
18
U glavnom programu smo konstruktoru prosledili četri podatka tipa float, nakon toga će on sam
pozivati odgovarajuće konstruktore za klasu tacka u skladu sa načinom na koji smo ga realizovali.
Vidimo da smo jednom to odradili u inicijalizacionoj listi a drugi put u tijelu konstruktora.
Konstruktor se može realizovati i kao krug::krug(tacka t1, tacka t2):centar(t1){ sa_kruga=t2;}; pri
čemu moramo voditi računa da tačka nije ugrađeni tip podataka već izvedeni i da u skladu sa tim
pre poziva ovog konstruktora sa argumentima tipa tacka prvo moramo stvoriti te argumente tj.
inicijalizovati ih pozivom odgovarajućeg konstruktora. Dakle, u glavnom programu bismo imali
naredbe tacka t1(x,y),t2(x1,y1); krug k1(t1,t2);. Naravno, za prvi način realizacije je potrebno
imati zaglavlje tipa krug(float, float, float, float); a za drugi krug(tacka, tacka);. Parametri u
deklaraciji funkcije moraju odgovarati onima prilikom njene realizacije.
Ostatak koda je izvršen u skladu sa ranijim primerima. Obratite pažnju da se ispisivanje
komentara u više redova vrši na isti način kao u programskom jeziku C, dok se komentar može
ispisati u jednom redu koristeći //.
Primer rada programa
_____________________________________________________________________________U
nesite koordinate centra kruga i tacke sa kruga
0 0 2.5 2.5
Povrsina kruga je: 39.25
Obim kruga je: 22.2144
_____________________________________________________________________________
Zadatak 2. 6
Realizovati klasu red koja će predstavljati niz celih brojeva proizvoljne dužine, neka klasa sadrži
i podatak o dužini reda. Omogućiti da indeks prvog člana može biti različit od nule ukoliko
korisnik to zada. Omogućiti pristup članovima reda za upis i izmenu podataka. Napisati glavni
program u kojem će se unositi odgovarajući red i na izlazu davati suma članova tog reda kao i
red koji je sastavljen od kvadrata elemenata reda koji je korisnik uneo.
_____________________________________________________________________________
#include <iostream> using namespace std; class Array{ private: int *p; //Pokazivac na pocetak niza celih brojeva int num; //Duzina niza const int i1; //Indeks pocetnog elementa niza (moze biti razlicit od 0) public: Array(int first_index=1,int number=0); Array(const Array & a); ~Array(); int first(){return i1;} int length(){return num;} int last(){return i1+num-1;} int read(int index){return p[index-i1];} void change(int, int);
19
}; Array::Array(int first_index, int number):i1(first_index), num(number){ p=new int[num]; } Array::Array(const Array &a): i1(a.i1), num(a.num){ p=new int[num]; for(int i=0;i<num;i++) p[i]=a.p[i]; } Array::~Array(){delete[] p; p=0;} void Array::change(int index, int value){ p[index-i1]=value; } int sum(Array &a){ int s=0; for(int i=a.first();i<=a.last();i++) s+=a.read(i); return s; } void square(Array &a){ for(int i=a.first();i<=a.last();i++) { int x; x=a.read(i); a.change(i,pow(x,2)); } } void main(){ int i, n,x; cout<<"Koliko clanova zelite da ima vas red?"<<'\n'; cin>>n; cout<<"Od kojeg indeksa zelite da pocinje indeksiranje vaseg reda"<<'\n'; cin>>i; cout<<"Unesite clanove reda"<<endl; Array a1(i,n); for(int j=i;j<n+i;j++){ cin>>x; a1.change(j,x); } Array a2(a1); square(a2); cout<<"Suma clanova zadatog reda je: "<<sum(a1)<<endl; cout<<"Red dobijen kvadriranjem clanova zadatog je: "<<endl; for(int j=a2.first(); j<=a2.last();j++) cout<<a2.read(j)<<" "; }
_____________________________________________________________________________
Dati primer ilustruje korišćenje konstruktora kopije. Veoma često u programima želimo
inicijalizovati novi objekat tako da ima iste vrednosti podataka članova kao i neki već postojeći.
To ćemo učiniti konstruktorom kopije. Ukoliko mi ne definišemo konstruktor kopije pozvaće se
default. Ovakav konstruktor kopije kopira podatke članove postojećeg objekta u objekat koji
stvaramo jedan po jedan. Ukoliko data klasa ima kao podatak objekat neke druge klase (tacka u
prethodnom zadatku) za kopiranje tog podatka (objekta) poziva se konstruktor kopije za klasu
(tacka) čiji je on objekat. Kopiranje se dešava i prilikom korišćenja objekata u inicijalizacionoj listi
pri realizaciji konstruktora. U prethodnom primeru bi to bilo pri realizaciji konstruktora kao
krug::krug(tacka t1, tacka t2):centar(t1){ sa_kruga=t2;}, prilikom deklaracije krug k1(t1,t2);
konstruktor kopije klase tacka će se pozivati dva puta, za kopiranje t1 u centar i t2 u sa_kruga.
Konstruktor kopije se ne poziva samo prilikom eksplicitne inicijalizacije novog objekta već i
20
prilikom transfera argumenata funkcije koji su objekti neke klase i kada kao rezultat funkcije
imamo objekat neke klase, jer se i u tim slučajevima stvaraju novi, privremeni, objekti.
Default konstruktor kopije, u većini slučajeva, zadovoljava naše potrebe i nema svrhe
definisati novi. Međutim, ukoliko radimo sa klasama koje dinamički alociraju memoriju moramo
definisati sopstvenog konstruktora kopije. Naime, u ovim slučajevima kao podatak član imamo
pokazivač, u našem primeru p koji je pokazivač na niz celih brojeva. Ukoliko bismo u slučajevima
kada nam je to potrebno koristili ugrađenog konstruktora kopije on bi kopirao vrednost ovog
pokazivača tako da bismo imali dva objekta koja pokazuju na isti niz, ukoliko bismo sada
izmijenili elemente jednog niza ta izmjena bi se odrazila i na drugi. Može se zaključiti da kada god
u nekoj klasi vršimo dinamičko alociranje memorije potrebno ja da definišemo sopstvenog
konstruktora kopije.
U ovom primeru želimo uvesti korisnički tip podataka, niz čiji indeks može počinjati od bilo kojeg
broja. Da bi imali sve potrebne informacije za manipulaciju datim nizom naša klasa ima tri
podatka člana: pokazivač na početak niza p, broj elemenata niza num i vrednost prvog ineksa i1.
Kada smo koristili nizove onako kako ih kompajler sam formira dovoljno je bilo imati podatke o
dužini niza i broju elemenata jer je početni elemant uvek imao indeks 0. Mi ovde želimo da nam
početni indeks koji ćemo koristiti za pristup elementima ovog niza ima bilo koju vrednost pa taj
podatak moramo imati. U prethodnom primeru smo rekli da ukoliko definišemo bilo koji
konstruktor moramo definisati i deafult. Na prvi pogled, može djelovati, da smo ovde prekršili to
pravilo. Ako se bolje pogleda konstruktor vidi se da on može biti pozivan bez ijednog argumenta,
ima podrazumevane parametre koji će se u tom slučaju koristiti pa ćemo imati niz sa prvim
indeksom 1 a bez elemenata, može se pozvati i sa samo jednim argumentom, drugi će dobiti
podrazumevanu vrednost, ili sa oba. Tek kada pošaljemo oba argumenta vrši se dinamička
alokacija memorije. Ovde smo formirali objekat koji predstavlja istancu niza sa indekasom prvog
člana i1, num elemenata, zauzeli memoriju za njih i vratili pokazivač na početak te memorije p. U
okviru glavnog programa ćemo elementima niza dodeliti neke vrednosti. I konstruktor kopije,
kao i običan konstruktor, ima isto ime kao i klasa kojoj pripada, razlika je, pored same radnje koju
obavlja, u tome što konstruktor kopije ima tačno određen tip podatka koji mu je argument, to je
konstantna referenca na objekat klase za koju se pravi dati konstruktor kopije Array (const Array
& a), može se izostaviti samo const. Konstruktor kopije preslikava vrednosti podataka objekta
koji se šalje kao argument u vrednosti podataka novog objekta num i i1, vrši alociranje novog
dijela memorije, odgovarajućeg kapaciteta, u koji presipa elemente niza. Sada dobijamo dva
objekta koji imaju isti sadržaj ali ukazuju na različite djelove memorije. Obratite pažnju na način
pristupanja elementima objekta koji se šalje kao argument i objekta za koji se poziva konstruktor
kopije. Vidimo da smo prilikom presipanja elemenata pristupali im kao da počinju od indeksa 0
pa do kraja niza iako smo rekli da možemo imati proizvoljan početni indeks. Ovo je i logično jer
će se elementi niza čuvati od indeksa 0 pa do kraja niza a mi treba da realizujemo klasu tako da
korisnik ima osjećaj da počinju od nekog proizvoljnog indeksa i1. To ćemo učiniti u funkcijama za
pistup gdje će on za zadati početni indeks i1 pristupati prvom elementu pozivajući ga kao i1 a
zapravo će očitavati element sa indeksom 0. Znači, u funkciji read() kada pošaljemo vrednost
indeksa index naredbom return p[index-i1]; mi vraćamo odgovarajući element. Ukoliko zadamo
da je početni indeks niza 3 a želimo očitati element sa indeksom 5, kao vrednost stvarnog
argumenta za index u pozivu funkcije šaljemo 5 a očitavamo 5-3, dakle element sa indeksom 2.
Ovo je i logično jer ćemo u memoriji imati niz sa indeksima 0,1,2...num-1 a korisnik ima osjećaj
21
da su indeksi i1, i1+1,..., i1+num-1. Podsjetite se koje je ovo svojstvo objektno orjentisanog
pristupa. Sličnu logiku smo koristili i prilikom realizacije funkcije change(), ovde smo kao
argumente slali i indeks čija se vrednost mijenja i novu vrednost koja mu se dodeljuje. Funkcije
first(), lenght() i last() vraćaju indeks (zadati) prvog elementa niza, dužinu niza i indeks poslednjeg
elementa niza. One su veoma jednostavne i zato su implementirane kao inline.
Obratite pažnju da funkcije za računanje sume i kvadrata elemenata nisu funkcije članice
pa iako rade samo sa jednim objektom mora im se taj objekat a proslijediti. Mi smo prosledili
referencu na objekat jer kada bismo prosledili objekat vršilo bi se kopiranje prilikom prenosa
parametara funkciji i stvaranje privremenog objekta u toku izvršavanja funkcije a na ovaj način
smo to izbjegli. Princip realizacije ovih funkcija se ne razlikuje od onog koji bismo koristili u
programskom jeziku C. Trebate obratiti pažnju da, s obzirom da ovo nije funkcija članica klase
Array, ni njena prijateljska funkcija, nema pravo pristupa njenim privatnim podacima pa kada joj
bude potreban početni indeks niza neće mu moći pristupiti direktno naredbom a.i1 već mora
pozivati funkciju za očitavnje vrednosti tog podatka, koja je javna i kao takvu je može koristiti
a.first() (for(int i=a.first();i<=a.last();i++) s+=a.read(i);). U funkciji square() šaljemo kao argument
referencu na neki objekat (niz) pa sve što budemo radili sa njegovim elementima odražava se na
sam objekat (prenos po referenci). Znači, iako je tip rezultata void mi imamo rezultat (niz koji
smo poslali kao stvarni argument i čiji su elementi sada kvadrirani) ali ga nije potrebno vraćati jer
smo poslali referencu na objekat.
U glavnom programu korisnik zadaje početnu vrednost niza i broj elemenata, nakon ovog se vrši
inicijalizacija objekta a1. Ovo je moguće izvršiti pre unošenja vrednosti pojedinih elemenata niza
jer konstruktor samo zauzima memoriju za traženi broj elemenata i postavlja vrednosti za
početni element i dužinu niza. Nakon toga korisnik unosi elemente niza kojima mi pristupamo na
način kako to korisnik očekuje, od indeksa i1, i menjamo im vrednost uz pomoć funkcije chage()
jer nam oni nisu direktno dostupni ovde, privatni podaci klase. Sada smo u potpunosti
inicijalizovali ovaj objekat i možemo ga koristiti u pozivu konstruktora kopije za inicijalizaciju
novog objekta Array a2(a1); (može i Array a2=a1;) dok se naredbom Array a2; a2=a1; ne bi
pozivao konstruktor kopije već operator dodijele koji, ukoliko mi nismo definisali drugačije, vrši
dodjelu na isti način kao default konstruktor kopije. Dakle i a1 i a2 bi ukazivali na isti deo u
memoriji pa bi se sve što uradimo sa a1 odrazilo na a2 a mi to ne želimo već želimo da u a1
sačuvamo nepromijenjenu vrednost elemenata niza a u a2 da smjestimo kopiju članova koju
ćemo proslijediti funkcijama za računaje sume i kvadrata elemenata niza. Ove funkcije nisu
funkcije članice već globalne funkcije pa ih u skladu sa tim i pozivamo na odgovarajući način, bez
korišćena operatora dot(.).
Još jedna funkcija članica koja se automatski poziva i ugrađena je jeste destruktor. Ova
funkcija ima isto ime kao i klasa kojoj pripada i znak tilda ~ ispred njega ~Array();. Deklariše se
kao i ostale funkcije članice. Bez obzira što jezik poseduje sopstvenog default destruktora, dobra
programerska praksa je definisati ga uvek, čak i kada ima prazno telo funkcije. Mora se definisati
kada imamo dinamičko dodeljivanje memorije, kao u ovom slučaju. Klasa može imati samo
jednog destruktora i on ne može imati argumente kao ni povratni tip podataka, nije dozvoljena
ni upotreba reči void. Ukoliko klasa sadrži kao podatak član objekat druge klase prvo se poziva
destruktor za taj objekat, pa tek onda za klasu čiji je objekat član.
22
U našem primeru je bilo neophodno definisati destruktor jer se u konstruktoru vrši dinamička
alokacija memorije. Nakon uništavanja primeraka klase potrebno je osloboditi memoriju koju je
taj primerak zauzimao. Mi smo to i uradili u tijelu destruktora Array::~Array(){delete[] p; p=0;}.
Uglaste zagrade pre pokazivača p se koriste uvek kada imamo niz, tako govorimo da je potrebno
osloboditi memoriju koju je zauzeo cio niz a ne samo prvi element. Nakon ovog, pokazivač p će i
dalje sadržati adresu prvog elementa niza, čiji smo sadržaj upravi izbrisali. To nikako nije poželjno
jer nam pokazivač pokazuje na nešto što više ne postoji. Naredbom p=0; ostavljamo ga u
ispravnom, praznom, stanju. Ne ukazuje ni na jednu memorijsku adresu.
Primer rada programa
_____________________________________________________________________________
Koliko clanova zelite da ima vas red?
5
Od kojeg indeksa zelite da pocinje indeksiranje vaseg reda
3
Unesite clanove reda
2 5 13 26 4
Suma clanova zadatog reda je: 50
Red dobijen kvadriranjem clanova zadatog je:
4 25 169 676 16
_____________________________________________________________________________
Zadatak 2. 7
Implementirati klasu koja će predstavljati realizaciju steka uz mogućnost brojanja podataka
koji se nalaze na steku. Napisati glavni program u kojem će se na stek postaviti pet podataka,
izbrisati dva i dopisati tri a zatim na izlaz dati izgled tako dobijenog steka.
_____________________________________________________________________________
#include <iostream> #include <conio.h> #include <stdlib.h> using namespace std; class stek{ private: static int ukupno; static stek *vrh; int broj; stek *prethodni; public: static void stavi_na_stek(int); static int uzmi_sa_steka(); static int prazan(){return vrh==0;}
23
static int imaih(){return ukupno;} }; int stek::ukupno=0; stek* stek::vrh=0; void stek::stavi_na_stek(int i){ stek *pom=new stek; pom->prethodni=vrh; pom->broj=i; vrh=pom; ukupno=ukupno+1; } int stek::uzmi_sa_steka(){ if(!vrh) exit(1); stek *pom; pom=vrh; int i=vrh->broj; vrh=pom->prethodni; delete pom; pom=0; ukupno=ukupno-1; return i; } int main(){ int n; cout<<"Koliko brojeva zelite da ima stek?\n"; cin>>n; int m; cout<<"Unesite podatke koje zelite da postavite na stek"<<endl; for(int i=0; i<n;i++){cin>>m; stek::stavi_na_stek(m);} cout<<"Koliko brojeva zelite da uzmete sa steka?"<<'\n'; cin>>n; cout<<"Uzeli ste brojeve: "; for(int i=0;i<n;i++) cout<<stek::uzmi_sa_steka()<<" "; cout<<endl; cout<<"Koliko brojeva zelite da postavite na stek?"<<'\n'; cin>>n; cout<<"Unesite podatke koje zelite da postavite na stek"<<endl; for(int i=0;i<n;i++) {cin>>m; stek::stavi_na_stek(m);} cout<<"Ukupno je"<<stek::imaih()<<endl; cout<<"Na steku su brojevi od vrha ka pocetku"; while(!stek::prazan()) cout<<stek::uzmi_sa_steka()<<" "; }
_____________________________________________________________________________
Stek se ponaša kao memorijski mehanizam tipa "poslednji-u, prvi iz" (engl. last in, first
out). Stek ima svojstvo podataka. Mi smo već ranije naveli da klasama definišemo korisnički tip
podataka. Stek će nam, zapravo, biti linearna lista sa specijalnim svojstvom da ne možemo čitati
element sa bilo kojeg mesta u njoj već samo poslednji element i da ne možemo postavljati
podatke na bilo koje mesto već samo na vrh steka. Mi želimo upisivati cele brojeve na stek i oni
će biti sačuvani u podatku članu broj i biti deo pojedinog objekta klase. Svaki objekat naše klase
će biti vezan za jedan element steka, svi objekti čine stek. Statički podatak se koristi za smeštanje
informacija koje su zajedničke za sve objekte klase. Mi smo rekli da će nam cela klasa biti stek.
Svaki stek, kao specifičan niz, ima jedan vrh i on je zajednički za sve članove klase, svi oni su deo
steka koji ima jedan kraj-vrh. Znači, podatak o kraju steka ćemo realizovati kao static. Elementi
steka su realizovani kao objekti naše klase pa će pokazivač na vrh steka biti tipa pokazivača na
24
objekat klase stek i mi smo mu dali ime vrh. Ono što je zajedničko za sve objekte koji čine klasu
jeste i broj tih objekata, elemenata steka, pa i taj podatak realizujemo kao statički, ukupno.
Karakteristika pojedinačnih objekata je adresa prethodnog elementa, (elementa koji je pre toga
postavljen na stek) nju moramo imati kako bi nam lista bila povezana. Naime, kada izbrišemo
neki podatak sa steka moramo znati adresu prethodnog jer je to sada adresa vrha.
Potrebne su nam funkcije za upisivanje elemenata na stek, očitavanje elementa, proveru da li je
stek prazan, kao i informacija o broju elemenata. Nju imamo kao statičku promenljivu u svakom
objektu ali je privatna pa zato pravimo funkciju koja će očitavati njenu vrednost. Sve funkcije
smo definisali kao static iz razloga što statičke funkcije rade sa klasom kao cjelinom, tj. sa stekom
u našem slučaju. Objekat nad kojim želimo izvršiti neku radnju može da bude argument statičke
funkcije, lokalna promenljiva u njoj ili globalna promenljiva. Nas, u ovom slučaju, ne interesuju
pojedinačni objekti iz klase i ako pogledate glavni program primjetićete da nigdje nismo
eksplicitno deklarisali novi objekat. Interesuje nas skup objekata date klase i stek koji oni čine i
radimo sve sa klasom kao cjelinom pa je logično da zato koristimo funkcije koje je tako
posmatraju a to su static funkcije. Da bi pozvali funkciju članicu potrebno je da posedujemo
jedan konkretan objekat. Funkcija static se poziva za klasu i ne poseduje implicitni argument
pokazivač this, zbog toga pojedinačnim podacima članovima klase ne možemo pristupati samo
navođenjem njihovog imena već moramo navesti i kojoj klasi pripadaju dok statičkim podacima
pristupamo neposredno. Statički podaci članovi se razlikuju od pojedinačnih po tome što se
njihova definicija i inicijalizacija ne vrši u konstruktoru, što je i logično jer se konstruktor poziva
za svaki objekat pojedinačno a statički član se inicijalizuje samo jedan put. Statički podataci se
definišu van funkcija i van definicije klase. Definišu se posebnim naredbama za definiciju i
inicijalizuju se samo konstantom ili konstantnim izrazom. Ovo se najčešće vrši posle definicije
klase a pre glavnog programa. Pošto se na tom mestu nalaze van dosega klase mora se koristiti
operator :: da bi se označilo kojoj klasi pripadaju. int stek::ukupno=0; stek* stek::vrh=0;. Vidimo
da se rezervisana reč static koristi samo u deklaraciji kako statičkih promjenjivih tako i statičkih
funkcija, dok se prilikom definicije ne smije navoditi.
U programu imamo četri funkcije. Prva vrši operaciju postavljanja podatka na stek. Kao podatak
joj šaljemo samo celobrojnu vrednost koju želimo postaviti na stek. Da bi postavila neku
vrednost na stek potrebna joj je adresa vrha steka koja se nalazi u promjenjivoj koja je static a
rekli smo da ovoj promjenjivoj funkcija može da pristupi samo navodjenjem njenog imena.
Dodavanje novog podatka na stek znači uvođenje novog objekta i njegovo povezivanje sa ranijim
objektima. U ovoj funkciji prvo vršimo dinamičku alokaciju memorije za novi element. Vrednost
pokazivača na ovaj objekat će nam biti vrednost pokazivača na novi vrh steka. Mi tu vrednost ne
smještamo odmah u statičku promenljivu vrh, već prvo koristimo prethodnu vrednost te
promenljive (vrh), jer ono što je bilo na vrhu steka pre uvođenja novog elementa steka sada će
biti na jednom mestu iza vrha, dakle, prethodni za objekat koji je na vrhu steka. Nakon toga u
njegovu promenljivu broj upisujemo vrednost koja je proslijeđana funkcijom. I tek onda, kada
smo povezali taj objekat sa prethodnim elementima, postavljamo vrh tako da ukazuje na njega i
povećavamo broj elemenata steka za jedan. Primetimo način pristupanja promjenjivim broj i
prethodni. Nije bilo dovoljno navesti samo njihovo ime jer statička funkcija nema pokazivač this.
Funkcija uzmi_sa_steka prvo proverava da li je stek prazan. To se može desiti u dva slučaja, da
ništa nismo stavili na stek, tada je vrednost vrh=0 jer smo je tako inicijalizovali ili kada uzmemo
sve sa steka, kada je opet vrednost na koju ukazuje stek 0 a što ćemo obezbijedi realizacijom ove
25
funkcije. Dakle, na samom početku proveravamo da li je stek prazan, ako jeste prekidamo rad
programa funkcijom exit(), da bi je mogli koristiti uključujemo biblioteku stdlib.h. Ukoliko ima
nešto na steku vršimo očitavanje. Nema potrebe da šaljemo koji ćemo podatak očitati jer je
priroda steka takva da možemo uzeti samo vrednost sa vrha steka. Ukoliko ima nešto na steku u
nekoj lokalnoj promjenjivoj pamtimo pokazivač na taj element vrh. Očitavamo vrednost koja je
bila upisana na vrhu steka, što ćemo kasnije i vratiti kao rezultat funkcije. Sada isključujemo
element sa steka tako što postavljamo da vrh ne ukazuje na njega već na element koji mu je
prethodeo u steku, a koji će nakon uzimanja ovog podatka biti na vrhu steka i oslobađamo
memoriju. Primetimo da smo ovom realizacijom omogućili da je vrednost promenljive vrh kada
smo sve očitali sa steka jednaka nuli. Kada očitamo element koji je poslednji element na steku, to
je na osnovu pravila o pristupu elementima steka element koji smo prvi upisali, pokazivač vrh
nakon očitavanja ukazuje na njegov prethodni element a on je dobio vrednost koju je imala
promenljiva vrh pre nego što je bilo što upisano na stek, dakle nula. Broj elemenata steka
umanjujemo za jedan. Funkcija prazan proverava da li je vrh==0 dakle, da li je stek prazan.
Funkcija ukupno nam vraća koliko ukupno elemenata imamo na steku.
Na početku su i vrh i ukupno inicijalizovane na nulu. Korisnik unosi koliko elemenata želi
da stavi na stek, unosi vrednost koje želi da stavi na njega i u for petlji unosi te vrednosti u
odgovarajuće elemente. Nakon toga pitamo korisnika da li želi da očita, i koliko elemenata, a
kada izvrši očitavanje javljamo mu koliko je elemenata ostalo na steku.
Primer rada programa
_____________________________________________________________________________
Koliko brojeva zelite da ima stek?
5
Unesite podatke koje zelite da postavite na stek
1 4 6 9 23
Koliko brojeva zelite da uzmete sa steka?
2
Uzeli ste brojeve: 23 9
Koliko brojeva zelite da postavite na stek?
3
Unesite podatke koje zelite da postavite na stek
2 4 1
Ukupno je6
Na steku su brojevi od vrha ka pocetku1 4 2 6 4 1
_____________________________________________________________________________
26
Zadatak 2. 8
Realizovati klasu radnici koja će imati tri podatka člana i to: celobrojnu promenljivu za
kofecijent za platu, pokazivač na celobrojnu promenljivu za identifikacioni broj radnika i javnu
statičku promenljivu koja će služiti za brojanje ukupnog broja radnika (objekata date klase).
Klasa poseduje konstruktor, destruktor i konstruktor kopije, kao i funkcije članice za pristup
podacima radi očitavanja i izmjene i funkciju koja računa koji od dva radnika ima veću platu i
vraća identifikacioni broj istog.
_____________________________________________________________________________
#include <iostream> #include <conio.h> using namespace std; class radnici{ private: int koeficijent; int *idbroj; public: radnici(){idbroj=0; broj++;} radnici(int,int); radnici(const radnici&); ~radnici(); int daj_koef()const{return koeficijent;} int daj_idbroj()const{return *idbroj;} void promijeni_koef(int); void promijeni_idbroj(int); int placeniji(radnici); static int broj; }; radnici::radnici(int koef, int idb):koeficijent(koef){ idbroj=new int; *idbroj=idb; broj=broj+1; } radnici::radnici(const radnici& stari):koeficijent(stari.koeficijent){ idbroj=new int; *idbroj=*stari.idbroj; broj=broj+1; } radnici::~radnici(){ delete idbroj; idbroj=0; broj=broj-1; } void radnici::promijeni_koef(int a){ koeficijent=a; } void radnici::promijeni_idbroj(int a){ *idbroj=a; } int radnici::placeniji(radnici a){ if(koeficijent>=a.koeficijent) return *idbroj; else return *a.idbroj; } int radnici::broj=0; main(){
27
int a,b; cout<<"Unesete podatke za dva radnika"<<endl; cin>>a>>b; radnici a1(a,b); cin>>a>>b; radnici a2(a,b); cout<<"Vise je placen radnik: "<<a1.placeniji(a2)<<endl; cout<<"Ukupno imamo "<<radnici::broj<<" radnika"<<endl; }
_____________________________________________________________________________
U ovom zadatku koristimo ranije uvedene pojmove: konstruktor, destruktor, konstruktor kopije i
statičku promenljivu. Tip promjenjivih nam je zadat postavkom zadatka, za statičku promenljivu
je rečeno da treba da bude javna. Podaci članovi su tipa int i int *, i konstruktor pozivamo sa dva
cela broja pri čemu u okviru konstruktora dodeljujemo vrednost promjenjivoj int na koju ukazuje
pokazivač idbroj. Obratite pažnju da to vršimo naredbom *idbroj=idb; što je i logično jer mi
želimo upisati vrednost koju smo prosledili konstruktoru u promenljivu na koju ukazuje
pokazivač idbroj a njoj pristupamo upotrebom operatora * ispred imena pokazivača. Konstruktor
kopije smo realizovali kao u ranijim primerima, samo što sad nemamo celi niz već samo jedan
element na koji nam ukazuje pokazivač i vrednost promenljive na koju ukazuje pokazivač iz
prosleđenog objekta (stari) prepisujemo u objekat koji kopiramo naredbom
*idbroj=*stari.idbroj;. U zadatku se od nas zahteva da vodimo evidenciju o tome koliko imamo
radnika. Imamo ih onoliko koliko imamo objekata. Svaki put kada stvorimo novi objekat poziva se
konstruktor, bilo da smo taj objekat eksplicitno deklarisali ili da se formira privremeni objekat u
koji će da se kopira vrednost objekta koji je stvarni argument funkcije itd.. Zbog ovoga ćemo
povećati vrednost za statičku promenljivu broj svaki put kada pozovemo konstruktora. Pošto se
objekti ne stvaraju samo kada ih mi eksplicitno deklarišemo već i kada želimo da pozovemo neku
funkciju, kao što je funkcija za računanje koji radnik ima veću platu, da bi imali pravo stanje o
broju radnika potrebno je da u destruktoru imamo naredbu koja će smanjivati broj radnika za 1
svaki put kada se destruktor pozove. Ukoliko smo formirali privremeni objekat u nekoj funkciji
nakon završetka izvršavanja te funkcije taj objekat se uništava i poziva se destruktor. Funkcije za
promjenu koeficijenta i identifikacionog broja su veoma jednostavne, samo treba voditi računa o
tome da je idbroj pokazivač na celobrojnu promenljivu i u skladu sa tim na odgovarajući način
pristupiti vrednosti promenljive na koju on ukazuje i promijeniti je. Isto važi i za funkciju za
računanje koji radnik ima veću platu. Potrebni su nam podaci o oba radnika, mi šaljemo samo
jedan objekat, podsjetite se zašto to činimo na ovaj način.
U glavnom programu smo unijeli podatke o dva radnika, a kao rezultat ispisujemo koji više
zarađuje i koliko ih ima.
Primer rada programa
_____________________________________________________________________________
Unesete podatke za dva radnika
224
3452
312
5431
Vise je placen radnik: 5431
Ukupno imamo 2 radnika
_____________________________________________________________________________
28
Zadatak 2. 9
Realizovati klasu trougao koja će kao podatke članove imati tri tačke koje predstavljaju
objekte klase tačka. Klasa tačka treba da ima odgovarajuće konstruktore i destruktore kao i
funkcije za računanje rastojanja dve tačke i ugla između prave koju čine dve tačke i x ose. Klasa
trougao treba da poseduje odgovarajuće konstruktore, destruktore kao i funkciju članicu koja
će za tri date tačke proveravati da li čine trougao ili su to tačke sa jedne prave i druga funkcija
članica proverava da li je trougao, na ovaj način zadat, jednakokraki. Napisati i glavni program
u kojem će se unositi koordinate za željene tačke, inicijalizovati potrebni objekti i proveravati
da li date tačke čine trougao i da li je isti jednakokraki.
_____________________________________________________________________________
#include <iostream> #include <math.h> #include <stdlib.h> using namespace std; class tacka{ float x; float y; public: tacka(){}; tacka(float,float); ~tacka(){} float Aps(){return x;} float Ord(){return y;} float rastojanje(tacka); double radius(tacka); }; tacka::tacka(float a,float b):x(a),y(b){} float tacka::rastojanje(tacka t){ float d; d=sqrt(pow(x-t.x,2)+pow(y-t.y,2)); return d; } double tacka::radius(tacka a){ return ((x-a.x)==0||(y-a.y)==0)?0:atan2((y-a.y),(x-a.x)); } class trougao{ private: tacka t1; tacka t2; tacka t3; public: trougao(){}; trougao(tacka, tacka,tacka); ~trougao(){} bool jednakokraki(); bool je_li_trougao(); }; trougao::trougao(tacka a,tacka b,tacka c):t1(a),t2(b),t3(c){} bool trougao::jednakokraki(){ if(t1.rastojanje(t2)==t2.rastojanje(t3)||t1.rastojanje(t2)==t1.rastojanje(t3)||\ t2.rastojanje(t3)==t1.rastojanje(t3)) return true;
29
else return false; } bool trougao::je_li_trougao(){ if((t1.rastojanje(t2)+t1.rastojanje(t3))<t2.rastojanje(t3)||\ (t1.rastojanje(t2)+t2.rastojanje(t3))<t1.rastojanje(t3)||\ (t1.rastojanje(t3)+t2.rastojanje(t3))<t1.rastojanje(t2)) return false; else return true; } void main(){ float x1,y1; cout<<"Unesite koordinate za tjeme A trougla"<<endl; cin>>x1>>y1; tacka A(x1,y1); cout<<"Unesite koordinate za tjeme B trougla"<<endl; cin>>x1>>y1; tacka B(x1,y1); cout<<"Unesite koordinate za tjeme C trougla"<<endl; cin>>x1>>y1; tacka C(x1,y1); trougao tr(A,B,C); if(tr.je_li_trougao()) cout<<"Zadate tacke predstavljaju trougao"<<endl; else { cout<<"Zadate tacke ne predstavljaju trougao"<<endl; getch(); exit(EXIT_FAILURE); } if(tr.jednakokraki()) cout<<"Trougao je jednakokraki"<<endl; else cout<<"Trougao nije jednakokraki"; }
_____________________________________________________________________________
Sama realizacija programa predstavlja kombinaciju ranije rađenih zadataka. Kao uslov za
određivanje da li su neke tačke temena trougla koristili smo činjenicu da zbir dve stranice trougla
mora biti veći od treće.
Primer rada programa
_____________________________________________________________________________ Unesite koordinate za teme A trougla
0 0
Unesite koordinate za teme B trougla
1 0
Unesite koordinate za teme C trougla
0.1
0
Zadate tacke predstavljaju trougao
Trougao nije jednakokraki
Unesite koordinate za teme A trougla
0 0
Unesite koordinate za teme B trougla
0 1
Unesite koordinate za teme C trougla
30
1 0
Zadate tacke predstavljaju trougao
Trougao je jednakokraki
_____________________________________________________________________________
3. Prijateljske klase i funkcije
Zadatak 3.1
Realizovati klasu niz koja će imati kao podatak član pokazivač na niz kompleksnih brojeva, kao
i dužinu niza. Implementirati funkciju koja daje proizvod dva kompleksna broja, pri čemu je
realizovati kao funkciju članicu i kao prijateljsku funkciju i napisati glavni program u kojem će
se ukazati na način pozivanja obe funkcije. Uneti niz kompleksnih brojeva i implementirati u
okviru klase niz funkciju koja će isčitavati dati niz, uzeti da je klasa niz prijateljska klasa klasi
kompleksnih brojeva. Klasa sadrži konstruktor, destruktor i konstruktor kopije.
_____________________________________________________________________________
#include <iostream> #include <conio.h> using namespace std; class kompleks{ float real; float imag; public: kompleks(){} kompleks(float a,float b):real(a),imag(b){}; ~kompleks(){}; kompleks prod(kompleks); float kRe(){return real;} float kIm(){return imag;} friend kompleks kprod(kompleks,kompleks); friend class niz;//sada koristim i class jer klasa niz jos uvek nije deklarisana. }; kompleks kompleks::prod(kompleks a){ kompleks temp; temp.real=real*a.real-imag*a.imag; temp.imag=imag*a.real+real*a.imag; return temp; } kompleks kprod(kompleks a,kompleks b){ kompleks temp; temp.real=b.real*a.real-b.imag*a.imag; temp.imag=b.imag*a.real+b.real*a.imag; return temp; } class niz{ private: kompleks *nizk; int ukupno; public: niz(){nizk=0;}
31
niz(int,kompleks *); niz(niz &); ~niz(); void read(){ for(int i=0;i<ukupno;i++){ cout<<nizk[i].real; if(nizk[i].imag<0) cout<<"-j"; else cout<<"+ j"; cout<<abs(nizk[i].imag)<<endl;} } }; niz::niz(int d, kompleks *k):ukupno(d){ nizk=new kompleks[ukupno]; for(int i=0;i<ukupno;i++) nizk[i]=k[i]; } niz::niz(niz &a):ukupno(a.ukupno){ nizk=new kompleks[ukupno]; for(int i=0;i<ukupno;i++) nizk[i]=a.nizk[i]; } niz::~niz(){ delete []nizk; nizk=0; } int main(){ float a,b; cout<<"Unesite prvi kompleksan broj"<<endl; cin>>a>>b; kompleks k1(a,b); cout<<"Unesite drugi kompleksan broj"<<endl; cin>>a>>b; kompleks k2(a,b); kompleks k3; k3=k1.prod(k2); cout<<"("<<k3.kRe()<<","<<k3.kIm()<<")"<<endl; k3=kprod(k1,k2); cout<<"("<<k3.kRe()<<","<<k3.kIm()<<")"<<endl; int n; cout<<"Koliko clanova imaju vasi nizovi?"; cin>>n; cout<<"Unesite elemente niza < 10"<<endl; float x,y; kompleks k[10]; for(int i=0;i<n;i++){ cin>>x>>y; k[i]=kompleks(x,y);} niz n1(n,k); n1.read(); getch(); }
_____________________________________________________________________________
Funkcije prijatelji neke klase su funkcije koje nisu članice te klase ali imaju pravo pristupa
privatnim elementima klase. Prijateljstvo se ostvaruje tako što ga klasa poklanja datoj funkciji i to
tako što se u definiciji date klase navede prototip funkcije i ključna reč friend na njegovom
početku. Klase takođe mogu biti prijatelji drugih klasa. U ovom primeru je data ilustracija klase i
funkcije prijatelja. Postavkom zadatka nam je rečeno da naša klasa kao podatak član ima
32
pokazivač na niz kompleksnih brojeva, da bismo deklarisali takav podatak član potrebno je da
imamo definiciju (ili makar deklaraciju klase kompleksnih brojeva). Mi smo odmah u potpunosti
definisali klasu kompleksnih brojeva i njene prijatelje. U odnosu na ranije korišćene definicije ove
klase novina je postojanje prijatelja. Prijateljstvo smo poklonili sledećim naredbama u okviri
definicije date klase: friend kompleks kprod(kompleks,kompleks); friend class niz;. Ukoliko je pre
realizacije klase koja želi pokloniti prijateljstvo nekoj drugoj klasi izvršena deklaracija ili definicija
klase prijatelja dovoljno je posle friend napisati samo identifikatora (ime) te klase, kod nas to nije
slučaj pa smo pre identifikatora naveli ključnu reč class kako bi kompajler znao da je u pitanju
klasa. Kada klasu učinimo prijateljem druge klase mi smo zapravo sve njene funkcije članice
učinuli prijateljem te klase.
Poređenje definicije i načina pristupa podacima funkcije članice i prijateljske funkcije učinjeno je
realizacijom funkcije za računanje proizvoda dva kompleksna broja jedan put kao funkcije članice
a drugi kao prijatelja. Što se tiče funkcije članice prod() njena realizacija je jasna. Jedan objekat
šaljemo kao argument a podacima drugog funkcija direktno pristupa zahvaljujući implicitnom
argumentu this koji je pokazivač na objekat za koji se ta funkcija poziva. Prijateljske funkcije
nemaju ovaj argument, pa ukoliko želimo pomnožiti dva kompleksna broja oba moramo poslati
kao argumente. Novina u odnosu na običnu globalnu funkciju je to što ima pravo pristupa
privatnim članovima objekata koji su joj prosleđeni. Primetite da im pristupamo direktno a ne
preko poziva funkcije članice što smo ranije morali raditi kada koristimo globalnu funkciju.
Zapamtite da se kod globalne funkcije mora poslati kao stvarni argument svaki objekat čijem
podatku želimo da pristupimo, što je i logično jer ona nije član klase kojoj je prijatelj pa nema
pokazivač this, samo joj je poklonjena privilegija pristupanja privatnim članovima klase kojoj je
prijatelj.
U klasi Niz smo pokazali da neka klasa kao podatak član može imati niz objekata druge klase.
Pojedinom objektu se pristupa navođenjem identifikatora pointera na početak niza i indeksa tog
elementa, kao što bi pristupili i-tom elementu niza celih brojeva. Pogledajmo realizaciju funkcije
za očitavanje elemenata niza, objektu koji je i-ti element niza nizk se pristupa naredbom nizk[i].
Svaki od ovih članova niza je objekat koji ima svoje podatke članove kao što je definisano klasom
čiji su oni objekti. Podatku real i-tog elementa u nizu nizk pristupamo naredbom nizk[i].real.
Normalno, da klasa kompleksnih brojeva nije poklonila prijateljstvo ovoj klasi ne bismo imali
pravo da na ovaj način pristupamo elementima te klase jer su oni privatni, pa bi za očitavanje i
izmenu svakog od njih morali pisati posebnu funkciju. S obzirom da je naša klasa Niz prijatelj klasi
kompleksnih brojeva ima pravo da pristupi privatnim podacima članovima te klase. Konstruktor
ove klase niz se razlikuje od ranijih po tome što u njemu ne vršimo samo zauzimanje memorije za
elemente niza kompleksnih brojeva već ih i inicijalizujemo, poslali smo kao argument pokazivač
na početak niza kompleksnih brojeva kojim će se izvršiti inicijalizacija. Rekli smo da su sve
funkcije članice klase koja se učini prijateljskom prijateljske pa će biti i konstruktor, on je
specijalna vrsta funkcije članice, dakle, direktno pristupa podacima članovima elemenata niza
kompleksnih brojeva. Ovde nismo prepisivali podatak po podatak iz objekata (kompleksnih
brojeva) u nove koji se inicijalizuju već smo iskoristili ugrađeni operator dodele koji će sam
izvršiti dodjelu tako da prepiše podatke članove objekta sa desne strane jednakosti u podatke
članove objekta sa lijeve, jedan po jedan. U nekim slučajevima ne treba koristiti ugrađeni
operator dodele već formirati novi, o čemu će biti reči kod obrade preklapanja operatora.
33
U glavnom programu smo unijeli podatke za dva komleksna broja k1 i k2 a proizvod računali prvi
put uz pomoć funkcije članice klase kompleksnih brojeva k3=k1.prod(k2); a nakon toga
pozivanjem prijateljske funkcije date klase koja vrši istu operaciju k3=kprod(k1,k2);. Primjetite
razliku u načinu pozivanja. Nakon očitavanja rezultata dobenih jednom i drugom funkcijom
inicijalizovali smo niz komleksnih brojeva koji smo kasnije iskoristili za inicijalizaciju objekta klase
niz, njegov konstruktor očekuje niz komleksnih brojeva. Na kraju je izvšeno očitavanje tog
objekta.
Primer rada programa
_____________________________________________________________________________
Unesite prvi kompleksan broj
2 -3
Unesite drugi kompleksan broj
1.1 2.3
(9.1,1.3)
(9.1,1.3)
Koliko clanova ima vas niz?4
Unesite elemente niza < 10
1 2
3.4 -2
2 4.3
1 2.3
Niz ima elemente:
1+ j2
3.4- j2
2+ j4.3
1+ j2.3
_____________________________________________________________________________ \
4. Preklapanje operatora
Zadatak 4. 1
Realizovati apstraktni tip podataka koji predstavlja racionalne brojeve, izvršiti preklapanje
operatora +, += kao i operatora za prefiksno i postfiksno inkrementiranje. Prilikom
realizacije operatora + uzeti u obzir i mogućnosti sabiranja racionalnih brojeva sa celim
brojem, pri čemu se ceo broj može očekivati i kao levi i kao desni operand.
_____________________________________________________________________________ #include <iostream> #include <conio.h> using namespace std; class Razlomci{ private: int brojilac; int imenilac; public: Razlomci(int=0,int=1); ~Razlomci(){} friend Razlomci operator+(Razlomci,Razlomci);///Ne saljem referencu jer Razlomci& operator+=(const Razlomci &);//mi je potreban poziv konstruktora Razlomci& operator++();// posto uzimam u obzir da mogi sabirati sa celim brojevima Razlomci operator++(int); void ispisi(){cout<<brojilac<<"/"<<imenilac<<endl;} };
34
Razlomci::Razlomci(int a,int b):brojilac(a),imenilac(b){} Razlomci operator+(Razlomci r1,Razlomci r2){ Razlomci temp; temp.brojilac=r1.brojilac*r2.imenilac+r2.brojilac*r1.imenilac; temp.imenilac=r1.imenilac*r2.imenilac; return temp; } Razlomci &Razlomci::operator+=(const Razlomci &r){ brojilac=brojilac*r.imenilac+imenilac*r.brojilac; imenilac=imenilac*r.imenilac; return *this; } Razlomci &Razlomci::operator++(){ return (*this)+=1; } Razlomci Razlomci::operator++(int i){ Razlomci temp(*this);//Kreira kopiju objekta koji inkrementiramo. (*this)+=1;//Inkrementira vrednost objekta. return temp;//Vraca vrednost pre inkrementiranja. } main(){ int b,i; cout<<"Unesite vrednosti za brojioce i imenioce razlomaka"<<endl; cin>>b>>i; Razlomci r1(b,i); cin>>b>>i; Razlomci r2(b,i); Razlomci priv; priv=r1+r2; priv.ispisi(); r1++; r1.ispisi(); priv=r1++; priv.ispisi(); ++r1; r1.ispisi(); r1+=r2; r1.ispisi(); r1+=(r2++); r1.ispisi(); priv=r1+r2+5; priv.ispisi(); (5+r2).ispisi(); getch(); }
_____________________________________________________________________________
Rekli smo da klase predstavljaju korisnički definisani tip podataka. Znamo da kod ugrađenih tipova podataka postoje i ugrađeni operatori kojima vršimo operacije nad njima. Logično je očekivati da kad pravimo novi tip podataka želimo da imamo mogućnost manipulacije nad podacima tog tipa. U ovom slučaju želimo da imamo mogućnost sabiranja, inkrementiranja i dekrementiranja racionalnih brojeva. Operatori se preklapaju tako da im se ne izgubi smisao. Dakle, ako želimo preklopiti operator + za dva racionalna broja on treba da obavlja funkciju sabiranja na očekivani način. Operatori se mogu preklopiti bilo kao funkcije članice ili kao prijateljske funkcije. Uputno je operatore koji menjaju vrednost levog operanda realizovati kao funkciju članicu, a ako se njime obavlja neka simetrična operacija kao prijateljsku funkciju. Bitno je zapamtiti da nije dozvoljeno uvođenje novih simbola za operatore ni promena prioriteta. Ako za jednu klasu preklopimo operator + i *, uvek će * imati veći prioritet nego +, kao i kod standardnih tipova podataka. Objasnimo na početku šta se podrazumeva pod zahtevom da operator sabiranja treba da bude preklopljen tako da se racionalni brojevi mogu sabirati sa celim, pri čemu se celi broj može naći i kao lijevi i kao desni operand. Ovaj uslov nas ograničava
35
da operator sabiranja moramo realizovati kao prijateljsku funkciju. Kada bi je realizovali kao funkciju članicu i ukoliko bismo u programu imali naredbu tipa 5+r2; pri čemu je r2 objekat naše klase pozvali bismo funkciju 5.operator+(r2); i došlo bi do greške u programu. Ukoliko bi imali naredbu tipa r2+5; izvršila bi se odgovarajuća konverzija cijelog broja u racionalni i program bi radeo kako treba, pogledajmo i zašto. Kada operator preklapamo kao funkciju članicu šalje se samo desni operand kao argument. Ukoliko smo rekli da šaljemo objekat, a ne referencu na taj objekat, i pošaljemo celi broj 5 vršiće se kopiranje vrednosti koju smo poslali u privremeni objekat. Mi nismo poslali tip koji odgovara pa će se tražiti funkcija koja može da izvrši konverziju tipa, ukoliko takva funkcija ne postoji doći će do greške u programu. Mi zapravo pozivamo konstruktora koji će inicijalizovati privremenu promenljivu tim brojem 5. Da ne bi došlo do greške mi moramo realizovati konstruktora tako da vrši konverziju cijelog broja u racionalni, to smo i učinili. Kada se naš konstruktor pozove sa jednim argumentom (5), drugi uzima podrazumevanu vrednost i celi broj 5 pretvara u racionalni 5/1. Sada se postavlja pitanje zašto nastaje greška ako je celi broj sa leve strane operatora a operator je preklopljen kao funkcija članica. U ovom slučaju bi se pozvala funkcija 5.operator+(r2); i imali bi grešku jer se konverzija tipa vrši samo za prave argumente a ne i za skriveni argument (pokazivac this kojim se prenosi objekat za koji je pozvana funkcija, u ovom slučaju 5), pa bi pozivali funkciju članicu klase racionalnih brojeva za podatak koji nije član te klase. Dakle, važno je zapamtiti da ukoliko realizujemo operator koji je simetričan poželjno ga je realizovati kao prijateljsku funkciju, a ukoliko se očekuje potreba za konverzijom tipa i sa lijeve i sa desne strane operatora mora se tako realizovati. Pored toga što u tom slučaju mora biti realizovan kao prijateljska funkcija, argumenti funkcije ne smiju biti tipa reference na objekat jer u tom slučaju ne bi došlo do kopiranja i konverzije tipa formalnog argumenta u stvarni, ne bi se pozivao konstruktor. Kada god nam nije potrebna konverzija argumente šaljemo kao referencu na objekat da se ne bi vršilo bespotrebno usporavanje programa kopiranjem stvarnih u formalne argumente. Operator + smo preklopili kao prijateljsku funkciju i, s obzirom da je to globalna funkcija a ne funkcija članica, kao argumente moramo poslati oba operanda. Prijateljska je, dakle ima prava pristupa privatnim podacima članovima klase. Sama logika realizacije u potpunosti prati način sabiranja dva razlomka. Kao rezultat dobijamo takođe razlomak. Postavlja se pitanje zašto je potrebno ovde vratititi objekat a ne referencu na njega. Vidimo da se vraća vrednost privremene promenljive temp, znamo da ova promenljiva postoji samo u toku izvršavanja funkcije odnosno, čim se završi izvršavanje ove funkcija ta promenljiva više ne postoji. Dakle, kada bismo vratili referencu na nju vratili bi referencu na nešto što ne postoji. Kada rezultat vratimo kao objekat mi zapravo vršimo kopiranje vrednosti tog objekta u neku promenljivu koja prihvata rezultat, što je dozvoljeno. Operator += će na već postojeću vrednost levog operanda dodavati vrednost desnog i vraćati kao rezultat njegovu novu vrednost. Na osnovu ranije rečenog zaključujemo da ga je poželjno realizovati kao funkciju članicu. Ovo je binarni operator pa obzirom da je funkcija članica klase šaljemo joj samo jedan objekat, desni. Za levi će se pozivati i biće joj prosleđen preko implicitnog argumenta this. Realizacija prati logiku same operacije. Primjećujemo da se ovde vraća kao rezultat referenca na objekat, to je ovde dozvoljeno jer je to *this odnosno referenca na objekat za koji je pozvana funkcija a koji postoji i nakon izvršavanja programa. Mogli smo vratiti i vrednost tog objekta ali bi na taj način vršili nepotrebno kopiranje i usporavanje programa. Postoji jedna specifičnost u realizaciji prefiksne i postfiksne operacije za sabiranje. Obe su unarne operacije i menjaju vrednost operanda pa će biti realizovane kao funkcije članice. Unarne su a funkcije članice dakle nije im potrebno proslijeđivati ništa. Iz tog razloga bi obe imale ime operator++() i bilo bi ih nemoguće razlikovati. Razlikovanje je omogućeno tako što se postfiksnoj funkciji uvodi formalni argument tipa int, koji služi samo da bi se omogućilo razlikovanje ove dve funkcije, nije mu potrebno, čak, ni davati ime prilikom definicije te funkcije. Prefiksno inkrementiranje menja vrednost objekta za koji je pozvana funkcija i vraća tako izmenjeni objekat. Primetite da smo prilikom realizacije ove funkcije iskoristili već preklopljeni operator +=. Iz istog razloga kao za prethodnu funkciju tip rezultata je referenca. Postfiksno inkrementiranje vrši inkrementiranje objekta koji smo mu poslali ali vraća njegovu prethodnu vrednost. Zašto
36
ovde nismo stavili da je tip rezultata referenca već objekat. Obratite pažnju koje se funkcije pozivaju za koju operaciju u glavnom programu. Kako bi glasilo njihovo eksplicitno pozivanje?
Primer rada programa
_____________________________________________________________________________
Unesite vrednosti za brojioce i imenioce razlomaka
2 3
3 4
17/12
5/3
5/3
11/3
53/12
248/48
2288/192
27/4
_____________________________________________________________________________
Zadatak 4.2
Realizovati klasu koja predstavlja realne brojeve u zapisu sa decimalnom tačkom (npr.
23.45). Klasa će imati dva podatka člana: jedan za realan broj (23.45) a drugi za celi deo tog
broja (23). Izvršiti preklapanje operatora -, -= kao i operatora za prefiksno i postfiksno
dekrementiranje. Prilikom realizacije operatora - uzeti u obzir i mogućnosti oduzimanja
brojeva u decimalnom zapisu i celih brojeva, pri čemu se celi broj može očekivati i kao levi i
kao desni operand.
_____________________________________________________________________________ #include <iostream> #include <math.h> #include <conio.h> using namespace std; class Decimalni{ private: float broj; int celi_deo; public: Decimalni(float=0);//,int=0); ~Decimalni(){} friend Decimalni operator-(Decimalni,Decimalni); Decimalni &operator-=(const Decimalni &); Decimalni &operator--(); Decimalni operator--(int); void citaj(){cout<<"Broj je:"<<broj<<". Celi deo je:"<<celi_deo<<endl;} }; Decimalni::Decimalni(float a):broj(a),celi_deo((int)broj){} Decimalni operator-(Decimalni a,Decimalni b){ Decimalni temp; temp.broj=a.broj-b.broj; temp.celi_deo=int(temp.broj); return temp; } Decimalni &Decimalni::operator-=(const Decimalni &a){
37
*this=(*this)-a; return *this; } Decimalni &Decimalni::operator--(){ (*this)-=1; return *this; } Decimalni Decimalni::operator--(int){ Decimalni temp=*this; (*this)-=1; return temp; } main(){ Decimalni a(23.45),b(12.11),c; (a-b).citaj(); c=a-5; c.citaj(); c=5-b;c.citaj(); c=b--; c.citaj(); b.citaj(); c=--b; c.citaj(); b.citaj(); b-=a; b.citaj(); b-=5; b.citaj(); getch(); }
_____________________________________________________________________________
Ovaj primer se realizuje prateći istu logiku kao u prethodnom. Voditi računa o načinu realizacije
operatore - - i - =. Uzeti u obzir da se celi deo ne može dobiti prostim oduzimanjem podataka
članova objekata u kojima je sadržana celobrojna vrednost. Naime, ukoliko imamo dva objekta
2.13 i 3.21 i izvršimo oduzimanje 3.13-2.21=0.92, ceo deo rezultata je 0 dok bi prostim
oduzimanjem celih delova dobili 1, dakle, pogrešili bi.
Primer rada programa
_____________________________________________________________________________
Broj je:11.34. Celi deo je:11
Broj je:18.45. Celi deo je:18
Broj je:-7.11. Celi deo je:-7
Broj je:12.11. Celi deo je:12
Broj je:11.11. Celi deo je:11
Broj je:10.11. Celi deo je:10
Broj je:10.11. Celi deo je:10
38
Broj je:-13.34. Celi deo je:-13
Broj je:-18.34. Celi deo je:-18
_____________________________________________________________________________
Zadatak 4. 3
Realizovati apstraktni tip podataka koji predstavlja kompleksne brojeve, izvršiti preklapanje operatora ==, != i <. Klasa sadrži konstruktore i destruktore.
_____________________________________________________________________________ #include <iostream> #include <conio.h> using namespace std; class Kompleks{ private: float real; float imag; public: Kompleks(float a=0, float b=0):real(a),imag(b){} ~Kompleks(){} friend bool operator==(const Kompleks,const Kompleks); friend bool operator<(const Kompleks, const Kompleks); friend bool operator!=(const Kompleks, const Kompleks); void citaj(){cout<<real<<"+ j"<<imag;} }; bool operator==(const Kompleks a,const Kompleks b){ return (a.real==b.real)&&(a.imag==b.imag); } bool operator!=(const Kompleks a,const Kompleks b){ return !(a==b); } bool operator<(const Kompleks a,const Kompleks b){ return (pow(a.real,2)+pow(a.imag,2))<(pow(b.real,2)+pow(b.imag,2)); } main(){ Kompleks k1(1,2), k2(1,3), k3(1,1), k4(1,2); if(k1==k2) { k1.citaj(); cout<<"=="; k2.citaj();cout<<endl;} else {k1.citaj(); cout<<"!="; k2.citaj();cout<<endl;} if(k1==k4) { k1.citaj(); cout<<"=="; k4.citaj();cout<<endl;} else {k1.citaj(); cout<<"!="; k4.citaj();cout<<endl;} if(k1!=k2) { k1.citaj(); cout<<"!="; k2.citaj();cout<<endl;} else {k1.citaj(); cout<<"=="; k2.citaj();cout<<endl;} if(k1!=k4) { k1.citaj(); cout<<"!="; k4.citaj();cout<<endl;} else {k1.citaj(); cout<<"=="; k4.citaj();cout<<endl;} if(k1<k2) { k1.citaj(); cout<<"<"; k2.citaj();cout<<endl;} else {k1.citaj(); cout<<">="; k2.citaj();cout<<endl;} if(k1<k3) {
39
k1.citaj(); cout<<"<"; k3.citaj();cout<<endl;} else {k1.citaj(); cout<<">="; k3.citaj();cout<<endl;} if(k1<k4) { k1.citaj(); cout<<"<"; k4.citaj();cout<<endl;} else {k1.citaj(); cout<<">="; k4.citaj();cout<<endl;} if(k1<3) { k1.citaj(); cout<<"< 3"<<endl;} else {k1.citaj(); cout<<">= 3";cout<<endl;} if(k1!=1) { k1.citaj(); cout<<"!= 1";cout<<endl;} else {k1.citaj(); cout<<"== 1"; cout<<endl;} if(k1==1) { k1.citaj(); cout<<"== 1";cout<<endl;} else {k1.citaj(); cout<<"!= 1"; cout<<endl;} getch(); }
_____________________________________________________________________________
Rešenje ovog problema je veoma jednostavno, sve smo operatore realizovali kao prijateljske fukcije jer su svi simetrični. Obratite pažnju na to da smo kao fiktivne argumente funkcija u svim slučajevima imali objekte a ne reference. Zašto?
Primer rada programa
_____________________________________________________________________________
1+ j2!=1+ j3
1+ j2==1+ j2
1+ j2!=1+ j3
1+ j2==1+ j2
1+ j2<1+ j3
1+ j2>=1+ j1
1+ j2>=1+ j2
1+ j2< 3
1+ j2!= 1
1+ j2!= 1
_____________________________________________________________________________
Zadatak 4. 4
Kreirati klasu Knjiga koja će imati kao podatak član pokazivač na celi broj koji predstavlja broj
stranica. Za datu klasu izvršiti preklapanje operatora =.
_____________________________________________________________________________
#include <iostream> #include <conio.h> using namespace std; class Knjiga{ private: int *br_strana; public: Knjiga(){br_strana=0;} Knjiga(int);
40
Knjiga(const Knjiga&); ~Knjiga(); Knjiga & operator=(const Knjiga&); void dodaj_strane(int a){*br_strana+=a;} void ocitavanje(){cout<<"Knjiga ima "<<*br_strana<<" stranica"<<endl;} }; Knjiga::Knjiga(int br){ br_strana=new int; *br_strana=br; } Knjiga::Knjiga(const Knjiga & stara){ br_strana=new int; *br_strana=*stara.br_strana; } Knjiga::~Knjiga(){ delete br_strana; br_strana=0; } Knjiga &Knjiga::operator=(const Knjiga & a){ if(this!=&a) {//provjera da li je dodjela a=a; delete br_strana; br_strana=new int; *br_strana=*a.br_strana; } return *this; } main(){ Knjiga k1; cout<<"Unesi broj stranica"<<endl; int n; cin>>n; Knjiga k2(n); Knjiga k3=k2; //Poziva se konstruktor kopije; k1=k2;//Vrsi se operacija dodeljivanja-poziva se operator dodeljivanja. k1.ocitavanje(); k3.dodaj_strane(5); k2.ocitavanje(); k3.ocitavanje(); getch(); }
_____________________________________________________________________________
Ranije smo pomenuli da kompajler automatski definiše operator dodele. Način na koji
operator dodele vrši dodeljivanje je analogan načinu na koji default konstruktor kopije vrši
incijalizaciju. Iz istih razloga kao kod konstruktora kopije i ovde, u nekim slučajevima, moramo
definisati sopstveni operator dodele. Uvek kada je za neku klasu neophodno definisati
konstruktor kopije neophodno je definisati i operator dodele ukoliko ćemo ga koristiti u radu sa
tom klasom. Podsetimo se da je konstruktor kopije potrebno definisati kad neka klasa poseduje
promenljivu pokazivačkog tipa. To je slučaj u našem primeru. Realizacija konstruktora,
destruktora i konstruktora kopije je izvršena na uobičajen način. Prvi put se srećemo samo sa
preklapanjem operatora dodele. On se uvek mora realizovati kao funkcija članica. Kao argument
mu se može poslati objekat, referenca na objekat ili konstantna referenca na objekat iste klase.
Mi ćemo uvek slati konstantnu referencu na objekat. Referencu da ne bismo vršili bespotrebno
kopiranje, a konstantnu da bi izbjegli slučajno mijenjanje vrednosti objekta. Logika operacije
41
dodeljivanja jeste da se dodijeli vrednost desnog operanda lijevom i da se njegova vrednost pri
tome ne mijenja i mi ćemo se toga pridržavati. Važno je primjetiti da se operator dodele razlikuje
od konstruktora kopije po tome što on mijenja vrednost nekom objektu koji već postoji, dok
konstruktor kopije stvara novi objekat. Čim taj objekat postoji znači da je imao neku staru
vrednost. Rekli smo da se potreba za realizovanjem sopstvenog operatora dodele javlja kad kao
podatak član imamo pokazivačku promenljivu. U našem zadatku to nije slučaj ali ovakva
promenljiva najčešće ukazuje na početak nekog niza koji ima proizvoljan broj elemenata.
Pokazivačka promenljiva na početak niza kojem se dodeljuje nova vrednost može ukazivati na niz
sa manje ili više objekata od niza na koji ukazuje promenljiva objekta čija se vrednost dodeljuje.
U oba slučaja ne možemo u isti memorijski prostor prosto prepisati vrednosti niza novog objekta.
U prvom slučaju nemamo dovoljno memorije a u drugom bi dobili niz koji na početku ima
elemente novog niza a na kraju starog. Da se ovakve stvari ne bi dešavale dobra praksa je uvek
prvo izbrisati stari sadržaj promenljive (niza) na koju je ukazivao pokazivač, pa onda zauzeti novi
memorijski prostor koji će biti dovoljan da prihvati novu promenljivu (niz) i onda izvršiti kopiranje
element po element. U našem slučaju smo imali pokazivač na celobrojnu promenljivu i izgleda
suvišno oslobađati memoriju koju je zauzimala cjelobrojna promenljiva pa zauzimati novu, s
obzirom da u oba slučaja imamo potrebu za istom količinom memorije, ali smo to učinili u duhu
ranijeg razmatranja. Bitno je uzeti u obzir mogućnost dodele tipa k1=k1 pri čemu je k1 objekat
klase za koju se preklapa operator dodele. S obzirom da prvo oslobađamo memoriju koju je
zauzimala stara vrednost promenljive kojoj se nešto dodeljuje i brišemo tu vrednost, u slučaju
ovakve naredbe bi značilo da smo izbrisali vrednost promenljive koju želimo da dodelimo. Da bi
se to izbjeglo mi proveravamo da li je adresa objekta čiju vrednost dodeljujemo ista kao onog
kojem se ta vrednost dodeljuje, ukoliko jeste, ne činimo ništa sa objektom, on već sadrži željenu
vrednost, i vraćamo referencu na objekat za koji smo pozvali funkciju. Ukoliko nije, vršimo
prethodne operacije, menjamo vrednost objekta za koji je pozvana funkcija i vraćamo referencu
na taj objekat, koji sada ima novu, željenu, vrednost. Primetimo da ovde, nakon brisanja sadržaja
pokazivačke promenljive nemamo naredbu kojom joj dodeljujemo vrednost nula, to je iz razloga
što bi to bila suvišna naredba s obzirom da već u sledećem koraku ta pokazivačka promenljiva
dobija novu vrednost. Funkcija dodaj_strane() je veoma jednostavna i upotrebili smo je da
pokažemo kako će ovakvom realizacijom operatora dodeljivanja objekti k3 i k2 biti potpuno
nezavisni nakon naredbe k2=k3; što ne bi bio slučaj da smo koristili default operator dodele, već
bi se sve promene jednog objekta odrazile na drugi.
Primer rada programa
Unesi broj stranica
123
Knjiga ima 123 stranica
Knjiga ima 123 stranica
Knjiga ima 128 stranica
_____________________________________________________________________________
Zadatak 4. 5
Realizovati klasu student koja će imati podatke o imenu i prezimenu studenta, godini studija i
godini upisa, kao i statičku promenljivu koja računa ukupan broj studenata. Klasa ima
odgovarajuće konstruktore i destruktora, preklopljene operatore dodele i postfiksnog i
prefiksnog inkrementiranja (inkrementiranje povećava godinu studija za jedan), kao i
42
prijateljsku funkciju koja za dati skup studenata računa, na svakoj godini, koji student je
najduže na studijama i ispisuje njegovo ime, datum upisa kao i godinu na kojoj je.
_____________________________________________________________________________
#include <iostream.h> #include <conio.h> #include <string.h> using namespace std; class Student{ private: char *ime; char *prezime; int gd_upisa; int gd_studija; public: static int ukupno; Student(){ime=0; prezime=0; ukupno++;} Student(int,int,char *,char *); Student(const Student &); ~Student(){delete []ime; ime=0; delete []prezime; prezime=0;ukupno--;} Student &operator=(const Student &); Student &operator++(); Student operator++(int); friend void Pretrazi(Student *, int); void citaj(){cout<<ime<<" "<<prezime<<" "<<gd_upisa<<" "<<gd_studija<<endl;} }; int Student::ukupno=0; Student::Student(int a,int b,char *ime1, char *prezime1):gd_upisa(a),gd_studija(b){ ime=new char[strlen(ime1)+1]; strcpy(ime,ime1); prezime=new char[strlen(prezime1)+1]; strcpy(prezime,prezime1); ukupno=ukupno++; } Student::Student(const Student & stari):gd_studija(stari.gd_studija),gd_upisa(stari.gd_upisa){ ime=new char[strlen(stari.ime)+1]; strcpy(ime,stari.ime); prezime=new char[strlen(stari.prezime)+1]; strcpy(prezime,stari.prezime); ukupno=ukupno++; } Student &Student::operator=(const Student &stari){ if(&stari!=this){ delete []ime; delete []prezime; gd_studija=stari.gd_studija; gd_upisa=stari.gd_upisa; ime=new char[strlen(stari.ime)+1]; strcpy(ime,stari.ime); prezime=new char[strlen(stari.prezime)+1]; strcpy(prezime,stari.prezime); } return *this; } Student &Student::operator++(){ gd_studija+=1;
43
return *this; } Student Student::operator++(int){ Student temp(*this); temp.gd_studija+=1; return temp; } void Pretrazi(Student *grupa,int broj){ Student temp[5]; for(int i=0;i<5;i++) temp[i]=Student(2003,i+1,"Nema","studenata"); for(int j=0;j<broj;j++){ if(grupa[j].gd_studija==1){ if(grupa[j].gd_upisa<temp[0].gd_upisa) temp[0]=grupa[j];} else if(grupa[j].gd_studija==2){ if(grupa[j].gd_upisa<temp[1].gd_upisa) temp[1]=grupa[j];} else if(grupa[j].gd_studija==3){ if(grupa[j].gd_upisa<temp[2].gd_upisa) temp[2]=grupa[j];} else if(grupa[j].gd_studija==4){ if(grupa[j].gd_upisa<temp[3].gd_upisa) temp[3]=grupa[j];} else{ if(grupa[j].gd_upisa<temp[4].gd_upisa) temp[4]=grupa[j];} } for(int i=0;i<5;i++){ cout<<"Student koji najduze studira na "<<temp[i].gd_studija\ <<" -oj godini se zove"; cout<<temp[i].ime<<" "<<temp[i].prezime; if(temp[i].gd_upisa!=2003) cout<<" i upisao je "<<temp[i].gd_upisa<<endl; } } main(){ Student ps[5]; int upis; int gd; char pom1[10]; char pom2[10]; ps[0]=Student(2001,1,"Slavica","Petranovic"); cout<<"Unesite podatke za 5 studenata: godinu upisa, godinu na kojoj je"; cout<<" ime i prezime"<<endl; for(int i=1;i<5;i++){ cin>>upis>>gd>>pom1>>pom2;; ps[i]=Student(upis,gd,pom1,pom2);} Student a(122,12,"Janko","Marjanovic"); Student b; b=a; b.citaj(); Pretrazi(ps,5); for(int i=0;i<5;i++){ps[i].citaj();} getch(); }
_____________________________________________________________________________
Ovaj primer objedinjuje ranije rađene zadatke. Primetite da smo prijateljsku funkciju realizovali tako što smo na početku pretpostavili da nema studenta na datoj godini pa onda koristili ranije pomenuti metod za traženje najvećeg, najmanjeg člana u nizu. Ukoliko je objekat niza odgovarao željenoj godini studija proveravali smo godinu upisa, ukoliko je manja od godine upisa tekućeg, najstarijeg studenta, najstariji je postajao taj objekat.
44
Primer rada programa
_____________________________________________________________________________
Unesite podatke za 5 studenata: godinu upisa, godinu na kojoj je ime i
prezime
1999 2 Ilija Ilickovic
2001 3 Maja Marjanovic
2000 2 Janko Jovicevic
1978 3 Sanja Mugosa
Janko Marjanovic 122 12
Student koji najduze studira na 1 -oj godini se zove: Slavica Petranovic i
upisao je 2001
Student koji najduze studira na 2 -oj godini se zove: Ilija Ilickovic i
upisao je 1999
Student koji najduze studira na 3 -oj godini se zove: Sanja Mugosa i upisao
je 1978
Student koji najduze studira na 4 -oj godini se zove: Nema studenata
Slavica Petranovic 2001 1
Ilija Ilickovic 1999 2
Maja Marjanovic 2001 3
Janko jovicevic 2000 2
Sanja Mugosa 1978 3
_____________________________________________________________________________
45
5. Nasleđivanje
Zadatak 5 1
Projektovati klase krug i kvadrat koje su izvedene iz klase figure (sadrži težiste kao
zajedničku karakteristiku za sve figure, funkciju koja omogućava pomeraj težista za zadatu
vrednost i virtuelne funkcije obim, površina i čitaj). Klase treba da imaju specifične funkcije
za računanje obima i površine kao i očitavanje odgovarajućih podataka članova.
_____________________________________________________________________________ #include<iostream.h> #include<conio.h> class Tacka{ private: float x; float y; public: Tacka(float a=0,float b=0):x(a),y(b){} ~Tacka(){} float aps(){return x;} float ord(){return y;} void Tcitaj(){cout<<"("<<x<<","<<y<<")";} }; const Tacka KP; class Figura{ private: Tacka teziste; public: Figura(Tacka t=KP):teziste(t){} virtual ~Figura(){} void pomjeri(float a, float b){teziste=Tacka(teziste.aps()+a,teziste.ord()+b);} virtual float Obim()const=0; virtual float Povrsina()const=0; virtual void citaj(){cout<<"T="; teziste.Tcitaj();} }; class Krug : public Figura{ private: float poluprecnik; public: Krug(float rr=1, Tacka k=KP):Figura(k),poluprecnik(rr){}; ~Krug(){} float Obim()const{const float pi=3.14; return 2*poluprecnik*pi;} float Povrsina()const{const float pi=3.14; return pow(poluprecnik,2)*pi;} void citaj(); }; void Krug::citaj(){ cout<<"U pitanju je krug: [ r="<<poluprecnik<<","; Figura::citaj(); cout<<", O="<<Obim()<<", P="<<Povrsina()<<" ]"<<endl; } class Kvadrat:public Figura{ private: float osnovica; public: Kvadrat(float a=1, Tacka t=KP):Figura(t),osnovica(a){} ~Kvadrat(){} float Obim()const{return 4*sosnovica;}
46
float Povrsina()const{return pow(osnovica,2);} void citaj(); }; void Kvadrat::citaj(){ cout<<"U pitanju je kvadrat: [ a="<<osnovica<<","; Figura::citaj(); cout<<", O="<<Obim()<<", P="<<Povrsina()<<" ]"<<endl; } main(){ Figura *pf[4]; pf[0]=new Krug; pf[1]=new Kvadrat; pf[2]=new Krug(2,Tacka(3,3)); pf[3]=new Kvadrat(2.5,Tacka(1.3,2)); for(int j=0;j<4;j++) pf[j]->citaj(); pf[0]->pomjeri(1,0.5); pf[1]->pomjeri(0.5,1); for(int j=0;j<2;j++) pf[j]->citaj(); for(int j=0;j<4;j++) {delete pf[j]; pf[j]=0;} getch(); }
_____________________________________________________________________________
Uvek kada imamo naleđivanje klasa prvo definišemo osnovnu klasu da bi imali pregled
onoga što radimo. U postavci zadatka je rečeno da osnovna klasa ima podatak o težištu figure.
Težište se predstavlja koordinatama u Dekartovom koordinatnom sistemu. Mi smo to rešili tako
što smo prvo definisali klasu Tacka pa smo kao podatak član klase Figura uzeli objekat klase
Tacka. Moglo se to odraditi i bez definicije posebne klase Tacka ali s obzirom na prirodu
problema, rad sa geometrijskim figurama, može se očekivati potreba za više objekata tipa Tacka
kada bi morali imati klasu Tacka zasebno realizovanu. Osnovna klasa može imati funkcije članice
koje se realizujuju kao i u ranije korišćenim klasama ali se kod nasleđivanja pojavljuju i virtuelne
funkcije. Potreba za njima se javlja zbog same prirode nasleđivanja. Nasleđivanje koristimo kada
imamo neke nove tipove koji imaju iste osobine kao neki postojeći tipovi (osnovne klase) ali i
neke specifične osobine. U našem primeru smo izveli klase Krug i Kvadrat iz klase Figura. Obe
geometrijske figure imaju težište ali krug ima i poluprečnik dok smo kvadrat karakterisali
dužinom osnovice. Svaka geometrijska figura se karakteriše mogućnošću izračunavanja obima i
površine ali se za svaku od njih izračunava na drugi način. Takođe će i način ispisivanja
komentara o određenoj figuri zavisiti od toga koja je: krug ili kvadrat. Shodno tome dolazi do
potrebe za korišćenjem virtuelnih funkcija. Da bi kompajler znao da je u pitanju virtuelna funkcija
potrebno je navesti ključnu reč virtual u deklaraciji funkcije u osnovnoj klasi, u definicijama
izvedenih klasa nije potrebno to ponavljati. Virtuelne funkcije se karakterišu time da će se
izvršavati na način koji je deklarisan u klasi za koju se pozivaju bez obzira što se pozivaju preko
pokazivača na osnovnu klasu. Naime, kao što se vidi u glavnom programu, moguće je izvršiti
implicitnu konverziju pokazivača na objekat izvedene klase u pokazivač na objekat osnovne klase.
Obratno se ne preporučuje! U glavnom programu smo imali skup krugova i kvadrata, da ne bi
47
svaki put proveravali da li je u pitanju krug ili kvadrat pa onda pozivali odgovarajuću funkciju mi
smo iskoristili virtuelne funkcije. Deklarisali smo niz pokazivača na objekte osnovne klase, zatim
svakom dodijelili vrednost pokazivača na neku od izvedenih klasa i u jednoj petlji pozivali
virtuelne funkcije za računanje obima i površine. S obzirom da su virtuelne, koriste dinamičko
povezivanje odnosno pozivaju onu funkciju koja odgovara objektu na koji ukazuje taj pokazivač a
ne objektu koji odgovara tipu pokazivača. Dakle, bez obzira što je pokazivač deklarisan kao
pokazivač na osnovnu klasu, Figure, izvršavaće se funkcija koja odgovara klasi čija je instanca
objekat na koji ukazuje taj pokazivač, Krug ili Kvadrat. Primetimo da kada funkciji pristupamo
preko pokazivača koristimo operator—>. Da bi se funkcije realizovale kao virtuelne nije dovoljno
samo navesti ključnu reč virtual u osnovnoj klasi. Potrebno je da kada budemo definisali tu
funkciju u izvedenim klasama ona bude identična po pitanju imena, broja i tipa argumenata kao i
tipa rezultata onoj u osnovnoj klasi, čak je potrebno u svim klasama navesti ključnu reč const
ukoliko je ona navedena u osnovnoj klasi kao što je slučaj kod nas. Ukoliko ovo nije izvršeno
sakrili smo funkciju iz osnovne klase. Dakle neće biti u pitanju funkcija sa preklopljenim imenom,
i možemo joj pristupiti samo uz pomoc operatora ::. Primer virutelnih funkcija su obim(),
povrsina() i citaj(). Vidimo da je samo poslednja i definisana u okviru osnovne klase, to je iz
razloga što ne znamo na koji ćemo način izračunati obim i površinu nekog objekta dok ne vidimo
koji je to objekat (krug ili kvadrat) a moramo joj navesti deklaraciju u osnovnoj klasi da bi
realizovali mehanizam virtuelne funkcije. Da se ne bi pisalo prazno telo funkcije uvodi se čisto
virtuelna funkcija. Kompajler je razlikuje po tome što nema telo funkcije ali ima =0 na kraju
standardnog zaglavlja. Klasa koja poseduje makar jednu čisto virtuelnu funkciju je apstraktna
klasa i ne mogu se deklarisati objekti te klase ali mogu pokazivači i reference na tu klasu. U
našem slučaju nismo imali potrebu za pristupanjem podacima članovima osnovne klase Figura
(koji su ujedno i podaci članovi izvedenih klasa, naslijedili su ih od osnovne) u nekoj od izvedenih
klasa pa smo ih realizovali kao private. Ukoliko bi imali potrebu za pristupanjem realizovali bi ih
kao protected. Na ovaj način dajemo dozvolu klasi koja se izvodi iz osnovne da može da direktno
pristupa tim podacima, dok se van te izvedene klase i dalje nema pravo pristupa istim.
Primetimo da smo konstruktor klase Tacka realizovali tako da kada je pozvan bez
argumenata postavlja sve koordinate na nulu. To smo iskoristili da uvedemo jednu globalnu
promenljivu KP koju ćemo koristiti kao podrazumevanu vrednost u svim sledećim
konstruktorima. U osnovnoj klasi je ujedno realizovan i default konstruktor i standardni. Ukoliko
se pozove kao default, težište je u koordinatnom početku. Funkcija za pomjeranje koordinata
težišta (članica klase Figura) ne može direktno da pristupa koordinatama objekta (instanca klase
Tacka) koji je podatak te klase jer su one njegovi privatni podaci i samo funkcije članice klase čija
48
je on instanca imaju pravo pristupa, pa smo te funkcije i iskoristili da očitamo koordinate. Za
promjenu njihovih vrednosti smo koristili konstruktor. Naime, mi ovde stvaramo novi objekat
težište koji će sadržati informaciju o pomjerenim koordinatama. Funkcija citaj() očitava
koordinate. Klasa Krug je izvedena javno iz klase Figura. To znači da će svi članovi (podaci i
funkcije) koje je ova klasa nasledila od klase Figura imati istu vidljivost kao u njoj, dakle javni
ostaju javni a privatni su i dalje privatni. Izvedena klasa nasleđuje iz osnovne klase sve podatke
članove i funkcije članice i može im pristupati navođenjem njihovog imena ukoliko ima pravo
pristupa. Krug ima kao specifičan podatak u odnosu na klasu Figura, poluprečnik ali takođe i
teziste koje je nasledeo iz nje. S obzirom na ovo konstruktoru se moraju proslijediti vrednosti za
inicijalizaciju oba člana. Karakteristika je kod izvedene klase samo to što se ne mora voditi računa
o tome kako da inicijalizujemo članove koje smo naslijedili iz osnovne klase već se briga o tome
prepušta konstruktoru osnovne klase koji se prvi poziva u konstrukoru izvedene klase. Mi smo
ovde istovremeno realizovali i default konstruktor i konstruktor kojem proslijeđujemo podatke
za inicijalizaciju podataka članova pa smo zato u dijelu za inicijalizaciju eksplicitno naveli poziv
konstruktora osnovne klase kao i podatak koji smo mu poslali. Da smo zasebno realizovali default
konstruktor ne bi morali da pozivamo u njemu default konstruktor osnovne klase, već bi on bio
automatski pozvan na početku izvršavanja konstruktora za izvedenu klasu. Napomenimo da
redosled kojim se navode podaci u inicijalizacionoj listi ne utiče na redosled inicijalizovanja u
smislu da iako poziv konstruktora osnovne klase nije naveden kao prvi ipak će se on prvi pozivati.
U ovoj klasi je moguće definisati funkcije obim() i povrsina() tako da računaju odgovarajuće
podatke na način koji odgovara njenim objektima. Primjećujemo da smo u okviru realizacije
funkcije citaj() pozivali odgovarajuću funkciju klase Figura kojoj smo pristupili pomoći operatora
::. U principu, ovo smo mogli izbjeći da smo podatke klase Figura realizovali kao protected. Na taj
način bi sada imali pravo pristupa tim članovima a da ne moramo pozivati nijednu funkciju
članicu te klase. Ovde smo koristili ovakav način pristupa jer smo željeli da pokažemo razliku
između virtuelne i čisto virtulene funkcije, odnosno, da definišemo funkciju citaj() u osnovnoj
klasi Figura.
Klasa Kvadrat je realizovana koristeći istu logiku kao za Krug.
Obratimo pažnju šta se dešava u glavnom programu. Na početku smo rekli da je pf niz od
četiri pokazivača na objekte klase Figura. Nakon toga smo izvršili dinamičko zauzimanje
memorije za objekte klasa Krug i Kvadrat i postavili pojedine članove niza pokazivača da ukazuju
na tako dobene objekte. Ovo je moguće jer postoji implicitna konverzija pokazivača izvedene
klase na pokazivače osnovne klase. Zanimljivo je da smo sada dobili niz pokazivača koju ukazuju
na objekte koji pripadaju različitim klasama. Nakon toga smo u for petlji pomoću ovih pokazivača
49
pozvali funkciju citaj(). Zahvaljujući činjenici da nam je ovo virtuelna funkcija uvek će se izvršavati
funkcija citaj() koja pripada klasi na čiji objekat stvarno ukazuje pokazivac pf[i], bez obzira što je
on tipa pokazivača na objekte klase Figure. Nakon for petlje smo pozvali funkciju pomjeri()
pomoću pokazivača koji ukazuju na krug i na kvadrat. U oba slučaja je izvršena funkcija na način
definisan u okviru osnovne klase što je i logično jer data funkcija nije virtuelna pa će se izvršiti za
tip pokazivača a ne za ono na što on stvarno ukazuje. Pošto smo na početku zauzeli dinamički
memoriju moramo je i osloboditi koristeći operator delete. Sada je jasno zašto smo destruktor
realizovali kao virtuelnu funkciju, prilikom izvršavanja naredbe delete pf[i]; poziva se destruktor.
Da nije bio realizovan kao virtuelna funkcija pozivao bi se na osnovu tipa pokazivača, dakle, za
osnovnu klasu pa ne bi bila oslobođena memorija koju zauzimaju članovi specifični za izvedenu
klasu već samo samo memorija zauzeta članovima naslijeđenim od osnovne. Ne bi se oslobodila
sva, ranije, zauzeta memorija. Destruktor realizujemo kao virtuelnu funkciju tako da će se pozvati
destruktor za klasu na čije objekte svarno ukazuje pokazivač i osloboditi sva zauzeta memorija.
Primer rada programa
_____________________________________________________________________________
U pitanju je krug: [ r=1,T=(0,0), O=6.28, P=3.14 ]
U pitanju je kvadrat: [ a=1,T=(0,0), O=4, P=1 ]
U pitanju je krug: [ r=2,T=(3,3), O=12.56, P=12.56 ]
U pitanju je kvadrat: [ a=2.5,T=(1.3,2), O=10, P=6.25 ]
U pitanju je krug: [ r=1,T=(1,0.5), O=6.28, P=3.14 ]
U pitanju je kvadrat: [ a=1,T=(0.5,1), O=4, P=1 ]
Zadatak 5. 2
Pojektovati klase pravougaonik i trougao koje su izvedene iz klase figure (sadrži težište kao
zajedničku karakteristiku za sve figure, funkciju koja omogućava pomeraj težišta za zadatu
vrednost i virtuelne funkcije dijagonala, poluprečnik opisanog i čitaj). Klase treba da imaju
specifične funkcije za računanje dijagonale i poluprečnika opisanog kruga kao i očitavanje
odgovarajućih podataka članova. Za klasu trougao treba realizovati konstruktor tako da može
stvoriti jednakostranični trougao kada se unosi podatak za dužinu jedne stranice kao i opšti
slučaj. Funkcija za računanje dijagonale u slučaju trougla treba da vraća -1. Poluprečnik
opisanog kruga, za trougao (stranica a, b i c) se može računati kao:
_____________________________________________________________________________ #include<iostream.h> #include<math.h> #include<conio.h>
50
class Tacka{ private: float x; float y; public: Tacka(float a=0,float b=0):x(a),y(b){} ~Tacka(){} void tcitaj()const{cout<<"("<<x<<","<<y<<")";} float aps(){return x;} float ord(){return y;} }; const Tacka KP; class Figure{ protected: Tacka teziste; public: Figure(Tacka t=KP):teziste(t){} virtual ~Figure(){} void Pomjeri(float dx,float dy){teziste=Tacka(dx+teziste.aps(),dy+teziste.ord());} virtual float Dijagonala()const=0; virtual float Poluprecnik()const=0; virtual void citaj()const=0; }; class Pravougaonik:public Figure{ private: float a; float b; public: Pravougaonik(float a1=1,float b1=0.5,Tacka t=KP):a(a1),b(b1),Figure(t){} ~Pravougaonik(){} float Dijagonala()const{return sqrt(a*a+b*b);} float Poluprecnik()const{return Dijagonala()/2;} void citaj()const; }; void Pravougaonik::citaj()const{ cout<<"U pitanju je pravougaonik:[a="<<a<<", b="<<b; cout<<". Teziste je :";teziste.tcitaj(); cout<<", d="<<Dijagonala()<<", R="<<Poluprecnik()<<"]"<<endl; } class Trougao:public Figure{ private: float a1,b1,c1; public: Trougao(float a2=1,Tacka t=KP):Figure(t){a1=b1=c1=a2;} Trougao(float a2,float b2,float c2,Tacka t=KP):a1(a2),b1(b2),c1(c2),Figure(t){} ~Trougao(){} float Dijagonala()const{return -1;} floatPoluprecnik()const{return a1*b1*c1/sqrt(pow(a1*a1+b1*b1+c1*c1,2)+\ 2*(pow(a1,4)+pow(b1,4) +pow(c1,4)));} void citaj()const; }; void Trougao::citaj()const{ cout<<"U pitanju je trougao:[a="<<a1<<", b= "<<b1<<", c= "<<c1; cout<<". Teziste je :";teziste.tcitaj(); cout<<", d="<<Dijagonala()<<", R="<<Poluprecnik()<<"]"<<endl; } main(){
51
Figure *pf[5]; pf[0]=new Pravougaonik; pf[1]=new Trougao; pf[2]=new Trougao(2.5,3); pf[3]=new Pravougaonik(2,3,Tacka(1,2)); pf[4]=new Trougao(2,4,5,Tacka(1,2)); for(int i=0;i<5;i++) pf[i]->citaj(); pf[0]->Pomjeri(2.3,4); pf[1]->Pomjeri(3.1,2); for(int i=0;i<2;i++) pf[i]->citaj(); for(int j=0;j<4;j++) {delete pf[j]; pf[j]=0;} getch(); }
Ovaj se zadatak razlikuje u odnosu na prethodni samo po tome što smo podatke članove klase
Figure, tj. težište realizovali kao protected tako da smo mogli i iz izvedene klase pristupati
objektu teziste i njegovim funkcijama, konkretno funkciji za očitavanje koordinata. Takođe smo
funkciju citaj() realizovali kao čisto virtuelnu funkciju. Specifičan je i zahtjev za realizacijom
konstruktora klase Trougao, tako da se može formirati jednakostranični trougao kada se unosi
podatak za dužinu jedne stranice. Pogledajmo default konstruktor. On se može pozvati bez
ijednog argumenta, samo sa jednim (koji će biti vrednost za a2 jer je on prvi u listi argumenata,
dok će biti t=KP, i u tom slučaju se ta vrednost a2 dodeljuje dužinama svih stranica, dakle imamo
jednakostraničan trougao) i sa dva argumenta. Razmislite kako biste realizovali konstruktor da se
zahtevalo da za poziv sa dva argumenta formira jednakokraki trougao.
Primer rada programa
_____________________________________________________________________________
U pitanju je pravougaonik:[a=1, b=0.5. Teziste je :(0,0), d=1.11803,
R=0.559017]
U pitanju je trougao:[a=1, b= 1, c= 1. Teziste je :(0,0), d=-1, R=0.258199]
U pitanju je trougao:[a=2.5, b= 2.5, c= 2.5. Teziste je :(3,0), d=-1,
R=0.645497]
U pitanju je pravougaonik:[a=2, b=3. Teziste je :(1,2), d=3.60555,
R=1.80278]
U pitanju je trougao:[a=2, b= 4, c= 5. Teziste je :(1,2), d=-1, R=0.64727]
U pitanju je pravougaonik:[a=1, b=0.5. Teziste je :(2.3,4), d=1.11803,
R=0.559017]
U pitanju je trougao:[a=1, b= 1, c= 1. Teziste je :(3.1,2), d=-1,
R=0.258199]
_____________________________________________________________________________
Zadatak 5. 3
Realizovati klasu časovnik koja će predstavljati vreme u satima, minutima i sekundama. Klasa
ima funkciju za povećavanje broja sekundi za po jednu. Iz date klase izvesti klasu let koja sadrži
naziv i broj leta, neka podaci nasleđeni iz osnovne klase časovnik predstavljaju vreme polaska i
neka postoji funkcija koja će omogućiti promenu ovog vremena za neko zadato kašnjenje.
Napisati glavni program u kojem se zadaje vreme polaska nekog leta i kašnjenje a čita novo
vreme polaska, sa uračunatim kašnjenjem.
_____________________________________________________________________________ #include <iostream.h> #include <conio.h>
52
#include <string.h> using namespace std; class Casovnik{ protected: int sec,min,sati; public: Casovnik(){} Casovnik(int h,int m,int s):sati(h),min(m),sec(s){} ~Casovnik(){} int Daj_s()const{return sec;} int Daj_m()const{return min;} int Daj_sat()const{return sati;} void Otkucaj(); void citaj(){cout<<sati<<" "<<min<<" "<<sec<<endl;} }; void Casovnik::Otkucaj(){ if(sec==59){ sec=0;min=min+1; if(min==60){ min=0; sati=sati+1; if(sati==24)sati=0;}} else sec=sec+1; } class Let:public Casovnik{ private: char *naziv; public: Let(){naziv=0;} Let(char *,int,int,int); Let(const Let&); ~Let(){delete []naziv;naziv=0;} void Kasnjenje(Casovnik); void Citaj(){cout<<"Naziv leta je: "<<naziv<<". Vreme odlaska je:"; Casovnik::citaj(); } }; Let::Let(char *ime,int h,int m,int s):Casovnik(h,m,s){ naziv=new char[strlen(ime)+1]; strcpy(naziv,ime); } Let::Let(const Let & stara):Casovnik(stara.sati,stara.min,stara.sec){ naziv=new char[strlen(stara.naziv)+1]; strcpy(naziv,stara.naziv); } void Let::Kasnjenje(Casovnik vreme){ int s=0; s=vreme.Daj_sat()*60*60+vreme.Daj_m()*60+vreme.Daj_s(); for(int i=0;i<s;i++) Otkucaj(); } main(){ Let prvi("Beograd-Podgorica",20,30,0); Let drugi(prvi); prvi.citaj(); cout<<"Unesite kasnjenje"; int h,m,s; cin>>h>>m>>s;
53
prvi.Kasnjenje(Casovnik(h,m,s)); prvi.Citaj(); drugi.Citaj(); }
_____________________________________________________________________________
Realizacija ovog programa je izvršena u skladu sa ranije urađenim primerima. Treba obratiti
pažnju na to da smo podatke članove osnovne klase realizovali kao protected tako da smo im
mogli direktno pristupati u konstruktorima. Ipak, treba praviti razliku kada imamo u funkciji
Kasnjenje() kao argument objekat tipa Casovnik. To je nezavisan objekat koji se šalje kao
argument funkcije, nije ga klasa naslijedila, i njegovim podacima članovima moramo pristupati uz
pomoć javnih funkcija članica klase čija je taj objekat instanca. Vidimo da u okviru realizacije iste
funkcije pristupamo funkciji Otkucaj() samo navođenjem njenog identifikatora, naša klasa ju je
naslijedila od klase Casovnik i može joj tako pristupati u svojoj funkciji članici. Inače, jedna
funkcija članica može pozivati drugu funkciju članicu u svojoj realizaciji samo navođenjem njenog
identifikatora i potrebnih argumenata.
Primer rada programa
_____________________________________________________________________________
20 30 0
Unesite kasnjenje3 30 5
Naziv leta je: Beograd-Podgorica. Vreme odlaska je:0 0 5
Naziv leta je: Beograd-Podgorica. Vreme odlaska je:20 30 0
_____________________________________________________________________________
Zadatak 5. 4
Implementirati klasu motocikl koja je izvedena iz klasa motorno vozilo i vozilo na dva točka a koje su izvedene iz klase vozilo. Klasa vozilo poseduje podatak o registarskim tablicama, motorno vozilo sadrži podatak o jačini motora a klasa vozilo na dva točka o broju sedišta. Klasa motocikl ima kao sopstveni podatak član marku motocikla.
#include <iostream.h> #include <conio.h> #include <string.h> using namespace std; class Vozilo{ protected: char *tablice; public: Vozilo(){tablice=0;} Vozilo(char *); virtual ~Vozilo(){delete []tablice;tablice=0;} virtual void citaj(); }; Vozilo::Vozilo(char * tab){ tablice=new char[strlen(tab)+1]; strcpy(tablice,tab); } void Vozilo::citaj(){
54
cout<<"U pitanju je vozilo reg. tab. "<<tablice<<endl; } class MotornoVozilo:virtual public Vozilo{ protected: int snaga_motora; public: MotornoVozilo(){} MotornoVozilo(char * br_tab, int sm):snaga_motora(sm), Vozilo(br_tab){} ~MotornoVozilo(){} void citaj(); }; void MotornoVozilo::citaj(){ cout<<"Snaga motora je "<<snaga_motora<<endl; } class Vozilo_na_dva_tocka:virtual public Vozilo{ protected: int br_sjedista; public: Vozilo_na_dva_tocka(int a=0):br_sjedista(a){}//za Vozilo_na_dva_tocka v; // dodeljuje 0 broju sjedista i sam poziva default konstruktor za Vozilo. Vozilo_na_dva_tocka(char *ch,int a):br_sjedista(a),Vozilo(ch){} ~Vozilo_na_dva_tocka(){} void citaj(); }; void Vozilo_na_dva_tocka::citaj(){ cout<<"Broj sjedista je:"<<br_sjedista<<endl; } class Motocikl:public Vozilo_na_dva_tocka,public MotornoVozilo, virtual Vozilo{ private: char * proizvodjac; public: Motocikl(){proizvodjac=0;} Motocikl(char *,char*,int,int); ~Motocikl(){delete []proizvodjac; proizvodjac=0;} void citaj(); }; Motocikl::Motocikl(char * marka,char *tab, int bs,int sm):MotornoVozilo(tab,sm),\ Vozilo_na_dva_tocka(tab,bs), Vozilo(tab){ proizvodjac=new char[strlen(marka)+1]; strcpy(proizvodjac,marka);} void Motocikl::citaj(){ Vozilo::citaj(); MotornoVozilo::citaj(); Vozilo_na_dva_tocka::citaj(); cout<<"Marka motora je "<<proizvodjac<<endl; } void main(){ Vozilo *pv=new Motocikl("Suzuki","PG1234",2,800); pv->citaj(); delete pv;//da destruktor osnovne klase nije virtuelni ovom naredbom bi //oslobodili samo deo memorije u kojem se nalazi niz tablice jer bi se pozvao //destruktor samo za klasu koja odgovara tipu pokazivaca-Vozilo bez obzira //sto pokazivac ukazuje na Motocikl. getch(); }
_____________________________________________________________________________
55
U ovom slučaju klasa Motocikl je direktno izvedena iz dve klase MotornoVozilo i
Vozilo_na_dva_tocka. Zaključujemo da je u pitanju višestruko nasleđivanje. Klase
MotornoVozilo i Vozilo_na_dva_tocka su izvedene iz osnovne klase Vozilo. S obzirom da
znamo da svaka izvedena klasa naslijeđuje iz osnovne klase sve podatke ovo bi značilo da
klasa motocikl ima dva podatka tablice, jedan naslijeđen od klase MotornoVozilo a drugi od
Vozilo_na_dva_tocka. Logično je da jedan motocikl ne može imati dvoje različitih tablica pa
da bi bili dosledni prirodi problema i naš motocikl mora imati samo jedne tablice. U
programskom jeziku C++ je to omogućeno uvođenjem virtuelnih osnovnih klasa. Primerci
podataka članova virtuelne osnovne klase će biti naslijeđeni samo jedan put bez obzira na to
iz koliko je klasa, koje su izvedene iz ove osnovne, izvedena naša klasa. Neka klasa se čini
virtuelnom osnovnom klasom tako što se u zaglavlju klasa koje se izvode iz nje piše
rezervisana reč virtual. Dakle, već pri izvođenju klasa MotornoVozilo i Vozilo_na_dva_tocka
morali smo imati na umu da će se iz obe klase izvesti jedna nova i da se podaci njihove
osnovne klase ne trebaju naslijeđivati bez jedan put pa je tu potrebno postaviti identifikator
virtual, kasnije se to ne može učiniti. Ranije smo rekli da se prilikom realizacije konstruktora
neke izvedene klase poziva konstruktor njene osnovne klase, bilo automatski ako nije
naveden eksplicitan poziv ili ga mi pozivamo eksplicitnim pozivom kada želimo proslijediti
neku vrednost za inicijalizaciju. Mi želimo inicijalizovati sve podatke klase Motocikl, kako one
naslijeđene od osnovnih klasa tako i za nju specifičan podatak, proizvodjac. Podatke
naslijeđene od osnovnih klasa inicijalizujemo proslijeđujući odgovarajuće argumente
konstruktoru tih klasa. Konstruktor klase MotornoVozilo zahteva podatke za broj tablica i
snagu motora respektivno. Konstruktor klase Vozilo_na_dva_tocka, takođe, zahteva dva
podatka, za broj tablica i broj sjedišta. Iz realizacije konstruktora za klasu Motocikl vidimo da
će se oba konstruktora pozvati. Zaključilo bi se da se onda dva puta inicijalizuje podatak
tablice naslijeđen iz osnovne klase Vozilo a mi smo je učinili virtuelnom da bi imali samo
jedan podatak tablice. Mehanizam virtuelne osnovne klase se realizuje tako da se u
konstruktoru klase izvedene iz više klasa sa zajedničkom virtuelnom osnovnom klasom,
konstruktor virtuelne osnove klase poziva samo jedan put i to iz konstruktora klase koja je
navedena poslednja u zaglavlju (class Motocikl:public Vozilo_na_dva_tocka, public
MotornoVozilo, virtual Vozilo). S obzirom na ovo dobra programerska praksa je da se uvek
navodi kao poslednja virtuelna osnovna klasa. Ovim nismo ništa promijenili jer će se njeni
podaci naslijediti za sve klase samo jedan put, realizovana je kao virtuelna, ali smo se
osigurali da će njen konstruktor biti sigurno pozvan. Vidimo da smo, bez obzira što sve klase
56
naslijeđujemo kao javne, za svaku klasu navodili ključnu reč public, da to nismo učinili za
neku klasu ona bi bila naslijeđena kao privatna.
Primer rada programa
_____________________________________________________________________________
U pitanju je vozilo reg. tab. PG1234
Snaga motora je 800
Broj sjedista je:2
Marka motora je Suzuki
_____________________________________________________________________________
6. Šabloni
Zadatak 6. 1
Sastaviti šablonsku funkciju kojom se od dva uređena niza formira treći, na isti način
uređen, niz. Sastaviti glavni program koji primenjuje ovu funkciju nad nizovima celih
brojeva i tačaka, pri čemu su koordinate tačaka celi brojevi kao i nad dve tačke koje imaju
realne koordinate. Klasu tačka realizovati kao šablonsku klasu kako bi koordinate mogle
biti traženog tipa.
_____________________________________________________________________________ #include <iostream> #include <conio.h> #include <math.h> using namespace std; template<class T> void uredi(T *prvi, int n1,T* drugi, int n2,T *treci){ for(int ia=0,ib=0,ic=0;ia<n1||ib<n2;ic++) treci[ic]=ia==n1 ? drugi[ib++]: (ib==n2 ? prvi[ia++] : (prvi[ia] < drugi[ib] ? prvi[ia++] : drugi[ib++])); } template<class S> class sablon{ S x; S y; public: sablon(S=0,S=0); ~sablon(){}; //bool operator<(const sablon<S>&); bool operator<(const sablon&);//Moze i bez <S> void citaj(){cout<<"("<<x<<","<<y<<")"<<endl;} }; template<class S> sablon<S>::sablon(S a,S b):x(a),y(b){} template<class S> bool sablon<S>::operator<(const sablon& b){ return (pow(x,2)+pow(y,2))<(pow(b.x,2)+pow(b.y,2)); } void main(){ int d1[]={1,2,5,7,9}; int d2[]={3,4,7,8}; int *d3=new int[9];
57
// int n3; uredi(d1,5,d2,4,d3); cout<<"Uredjeni niz je:"<<endl; for(int i=0;i<9;i++)cout<<" "<<d3[i]; cout<<endl; delete []d3; sablon<int> *p; cout<<"Koliko ima tacaka prvi niz?"<<endl; int i1; cin>>i1; cout<<"Unesite niz tacaka sa celobrojnim koordinatama"<<endl; p=new sablon<int>[i1]; int a,b; for(int i=0;i<i1;i++) {cin>>a>>b; p[i]=sablon<int>(a,b);} cout<<"Koliko ima tacaka drugi niz?"; int i2; cin>>i2; cout<<"Unesite niz tacaka sa celobrojnim koordinatama"<<endl; sablon<int> *q; q=new sablon<int>[i2]; for(int i=0;i<i2;i++) {cin>>a>>b; q[i]=sablon<int>(a,b);} int i3; i3=i2+i1; sablon<int> *r; r=new sablon<int>[i3]; uredi(p,i1,q,i2,r); for(int i=0;i<i3;i++) {cout<<" "; r[i].citaj();} cout<<endl; delete []p; delete []q; delete []r; // float a1,b1; sablon <float> t1(2.3,4.6),t4(2.5,6.8); if(t1<t4) { cout<<"Tacka ima koordinate: "; t1.citaj();} else { cout<<"Tacka ima koordinate: "; } }
_____________________________________________________________________________
U zadatku se od nas zahteva da realizujemo funkciju koja će za dva uređena niza
formirati treći, takođe uređen, sastavljen od elemenata dva niza koja šaljemo kao argumente
funkcije. Zapravo šaljemo pokazivače na početak tih nizova. Sama realizacija funkcije nije
novina. Ovo smo, kada su u pitanju nizovi celih i realnih brojeva, mogli odraditi i u
programskom jeziku C. Tamo smo morali davati različita imena funkcijama iako obavljaju istu
radnju jer imaju različite tipove argumenata. Na početku ovog kursa smo rekli da se u C++
može formirati više funkcija sa istim imenom i da će kompajler na osnovu broja i tipova
argumenata za koje se funkcija poziva vršiti razrešavanje poziva. I ako bismo koristili ovakav
pristup opet bismo morali da realizujemo tri funkcije: za cele, realne brojeve i niz tačaka. U
principu bi sve te funkcije realizovali na isti način, samo bi na određenim mestima imali
različite oznake za tip podataka sa kojima radimo. U jeziku C++ postoje šablonske funkcije
koje nam omogućavaju da ovo realizujemo jednom funkcijom tako što ćemo napraviti šablon
58
za rešavanje datog problema (formiranje trećeg niza na osnovu postojeća dva) ne navodeći
konkretan tip podataka. Na osnovu ovog šablona kasnije će se generisati odgovarajuća
funkcija za konkretan tip podataka. Naime, kada kompajler naiđe na poziv ove funkcije sa
stvarnim argumentima tipa int generisaće funkciju za formiranje sortiranog niza celih
brojeva, kada naiđe na poziv za nizove tačaka generiše funkciju za podatke tipa tacka. Isto
važi i za klase, i one mogu biti šablonske. U našem slučaju potrebna nam je klasa koja
predstavlja tačke zadate koodinatama cjelobrojnog tipa i klasa za realne koordinate. Da ne bi
vršili definiciju dve klase koje se razlikuju samo u tipu podataka članova mi ćemo ovu klasu
realizovati kao šablonsku.
Pogledajmo najpre realizaciju tražene funkcije. Primjećujemo da se pre zaglavlja klase
nalazi nova linija koda template<class T>. Rezervisana reč template govori kompajleru da
ono što slijedi nije obična funkcija već šablonska i da je tretira kao takvu. Unutar zagrada <>
se navode parametri šablona, može ih biti više. Reč class govori da je T tip podatka, bilo koji
tip, bilo neki od ugrađenih, bilo korisnički definisan. Prilikom generisanja funkcije za dati tip
podataka može se zamisliti da kompajler zapravo prolazi kroz funkciju i da na svako mesto na
koje nađe identifikator parametra šablona (u našem slučaju T) postavlja odgovarajući tip i
realizuje takvu funkciju. Na primer, za poziv iz našeg glavnog programa uredi(d1,5,d2,4,d3); T
će se zamijeniti sa int i generisati funkcija koja će raditi sa nizovima celih brojeva. Što se tiče
same funkcije uredi realizujemo je po logici koju smo koristili i u programskom jeziku C.
Šaljemo pokazivač na početak dva niza i broj elemenata niza, kao i pokazivač na početak
novoformiranog niza (treba nam kao rezultat). Umesto određenog tipa naveli smo
parametar šablona T. Na početku su ideksi svih brojača postavljeni na nulu. U svakom ciklusu
for petlje će se brojač koji označava tekući indeks novog niza povećavati za jedan. Naime, u
svakom ciklusu upisujemo nešto u taj niz pa prelazimo na sledeći element koji će nam postati
tekući za sledeći ciklus. Ako smo upisali sve elemente taj brojač je za jedan veći od indeksa
poslednjeg elementa novoformiranog niza, dakle jednak broju elemenata tog niza. Spajanje
ova dva niza je jednostavno. Vrši se sve dok nismo došli do kraja oba niza. Ukoliko smo
upisali elemente jednog niza, prepisujemo preostale elemente drugog. Oni su već uređeni pa
nema potrebe da ih uređujemo. Ukoliko nismo došli do kraja nijednog od nizova, pitamo koji
niz ima veći element, upisujemo element tog niza u novi, povećavamo brojač za taj niz za
jedan dok ne diramo brojač koji ukazuje na tekući element drugog niza jer iz njega ništa
nismo upisali, pa mu je to i dalje tekući element.
59
Što se tiče šablona za klasu, kompajleru najavljujemo na isti način kao i za funkciju da
je u pitanju šablon i da koristimo parametar S, dakle, navođenjem template<class S> pre
zaglavlja klase. Kada učinimo neku klasu šablonskom automatski smo učinili i sve njene
funkcije šablonima pa ukoliko ih definišemo van definicije klase potrebno je da to najavimo
navođenjem template<class S> pre definicije funkcije. Ovim smo najavili da ćemo definisati
šablonsku funkciju i da koristimo parametar S. Ranije smo naglasili da kada definišemo
funkcije članice klase van definicije klase kojoj te funkcije pripadaju treba da navedemo
identifikator te klase zbog proširenja dosega klase, da bi kompajler znao kojoj klasi funkcija
pripada. Obratite pažnju da ovde samo identifikator klase ne određuje jednoznačno
konkretnu klasu. Potrebno je iza imena klase navesti i identifikatore argumenata u
zagradama <>, u našem slučaju sablon<S>::. Ovo je potrebno jer će se za svaku realizaciju
klase generisati njene funkcije članice, za sablon<int> jedne, sablon<real> druge itd. Ukoliko
želimo da navedemo objekat klase unutar same klase dovoljno je samo njeno ime. Ovo se
podrazumeva i ako je objekat date klase argument neke njene funkcije članice, kod nas
operator<. Obratite pažnju da se ime konstruktora ne mijenja! Proširivanjem dosega,
navođenjem sablon<S>::, mi smo jednoznačno odredili kojoj klasi on pripada pa ne moramo
da pišemo sablon<S>:: sablon<S>(), to bi izazvalo grešku u kompajliranju. Ovde smo izvršili
definisanje operatora < za naše klase. Niz objekata ovih klasa se može očekivati kao
argument šablonske funkcije u kojoj se koristi taj operator nad argumentima pa moramo to
da učinimo. Dakle, pri realizaciji šablonske klase moramo voditi računa za što će se koristiti
generisane klase i obezbijediti odgovarajuće funkcije i izvršiti preklapanje potrebnih
operatora.
U glavnom programu smo inicijalizovali dva niza celih brojeva, alocirali memoriju za
rezultujući niz i pozvali funkciju naredbom uredi(d1,5,d2,4,d3);. Sada će kompajler generisati
funkciju za cijelobrojne argumente. Deklaracijom sablon<int> *p; generisali smo klasu sa
cjelobrojnim podacima članovima, a p je pokazivač na tu klasu. q je takođe pokazivač na
klasu sa cjelobrojnim tipom podataka članova. Zatim smo alocirali dovoljno memorije u koju
se može smjestiti novonastali niz tačaka (r je pokazivač na njen početak) i pozvali funkciju
uredi(p,i1,q,i2,r);. Kompajler generiše funkciju koja će raditi sa nizom tačaka celobrojnih
koordinata.
Za vježbu realizujte šablonsku funkciju za uređenje niza u rastući. Za nasumice
unesene nizove izvršite njihovo uređivanje i nakon toga primijenite ovde odrađene funkcije.
60
Primer rada programa
_____________________________________________________________________________U
redjeni niz je:
1 2 3 4 5 7 7 8 9
Koliko ima tacaka prvi niz?
3
Unesite niz tacaka sa cjelobrojnim koordinatama
1 2
2 4
3 5
Koliko ima tacaka drugi niz?2
Unesite niz tacaka sa cjelobrojnim koordinatama
1 1
2 3
(1,1)
(1,2)
(2,3)
(2,4)
(3,5)
Tacka ima koordinate: (2.3,4.6)
_____________________________________________________________________________
Zadatak 6. 2
Sastaviti šablonsku klasu matrice, pri čemu se može menjati tip podataka koje ta klasa sadrži.
Napisati glavni program u kojem treba inicijalizovati matricu celih brojeva i kompleksnih
brojeva i isčitati njihovu vrednost. Klasa matrica ima konstruktor kopije kao i operatore
dodeljivanja i indeksiranja, pri čemu indeksiranje treba realizovati tako da poziv elementa
m(1,3), m tipa matrica, daje element prve vrste i treće kolone. Elementi matrice se čuvaju u
vektoru u kojem su prvo elementi prve vrste pa druge itd.
_____________________________________________________________________________
#include <iostream> #include <stdlib.h> #include <conio.h> using namespace std; template<class T> class Matrica{ private: T *podatak; int vrsta, kolona; public: Matrica(){podatak=0;}//deafoult konstruktor Matrica(int,int);//konstruktor Matrica(const Matrica&);//konstruktor kopije ~Matrica(){delete []podatak;}
61
Matrica &operator=(const Matrica&); T& operator()(int,int); int velicina(){return vrsta*kolona;} int br_vrsta(){return vrsta;} int br_kolona(){return kolona;} }; template<class T> Matrica<T>::Matrica(int a,int b):vrsta(a),kolona(b){ podatak=new T[vrsta*kolona]; } template<class T> Matrica<T>::Matrica(const Matrica& stara):vrsta(stara.vrsta),kolona(stara.kolona){ podatak=new T[vrsta*kolona]; for(int i=0;i<vrsta*kolona;i++){ podatak[i]=stara.podatak[i]; } } template<class T> Matrica<T>& Matrica<T>::operator=(const Matrica& stara){ if(this!=&stara){//ako se ne dodeljuje a=a vrsta=stara.vrsta; kolona=stara.kolona; delete []podatak; podatak=new T[vrsta*kolona]; for(int i=0;i<vrsta*kolona;i++) podatak[i]=stara.podatak[i]; } return *this; } template<class T> T& Matrica<T>::operator()(int i,int j){ //if(i<1||i>vrsta&j<1||j>kolona) exit(1); return podatak[(i-1)*kolona+j-1]; } class Kompleks{ private: float real; float imag; public: Kompleks(float a=0,float b=0):real(a),imag(b){} ~Kompleks(){} void citaj(){cout<<"("<<real<<","<<imag<<")";} }; main(){ cout<<"Koliko vrsta i kolona zelite da ima matrica celih brojeva?"<<endl; int a,b; cin>>a>>b; Matrica<int> MatricaCelih(a,b); cout<<"Unesite elemente matrice"<<endl; for(int i=1; i<=a;i++) for(int j=1;j<=b;j++){ cin>>MatricaCelih(i,j);}; for(int i=1; i<=a;i++){ for(int j=1;j<=b;j++){ cout<<"MatricaCelih("<<i<<","<<j<<")=="; cout<<MatricaCelih(i,j)<<" ";} cout<<endl;}
62
cout<<"Koliko vrsta i kolona zelite da ima matrica kompleksnih brojeva?"<<endl; cin>>a>>b; Matrica<Kompleks> MatricaKompleksnih(a,b); float a1,b1; for(int i=1; i<=a;i++) for(int j=1;j<=b;j++){ cin>>a1>>b1; MatricaKompleksnih(i,j)=Kompleks(a1,b1);} for(int i=1; i<=a;i++) { for(int j=1;j<=b;j++){ cout<<"MatricaKompleksnih("<<i<<","<<j<<")=="; MatricaKompleksnih(i,j).citaj(); cout<<" ";} cout<<endl;} Matrica<Kompleks> b2(MatricaKompleksnih),b3; b3=b2; for(int i=1; i<=a;i++){ for(int j=1;j<=b;j++){ cout<<"b2("<<i<<","<<j<<")=="; b2(i,j).citaj(); cout<<" ";} cout<<endl;} for(int i=1; i<=a;i++){ for(int j=1;j<=b;j++){ cout<<"b3("<<i<<","<<j<<")=="; b3(i,j).citaj(); cout<<" ";} cout<<endl; } }
_____________________________________________________________________________
Ovo je još jedna ilustracija korišćenja šablona. Razlog uvođenja šablona u ovom
zadatku je očigledan, veoma često imamo potrebu da smještamo razne podatke u matrice.
Način smještanja elemenata i pristupanja istim se ne razlikuje u zavisnosti od tipa tih
elemenata. Kada napravimo šablon matrica veoma lako je definisati matricu sa
odgovarajućim tipom podataka (Matrica<int> MatricaCelih(a,b);). Kao podatke članove ove
klase imamo broj kolona i vrsta, koji će uvek biti cjelobrojne promenljive, i pokazivač na
početak niza u koji smo smjestili elemente matrice. Elementi matrice mogu biti za različite
instance klasa različitih tipova pa smo tip pokazivača zamijenili šablonskim parametrom T. Na
svakom mestu u realizaciji klase gdje bude potrebno navoditi tip pokazivača na početak niza
ovih elemenata ili tip podataka koji su smješteni u nizu mi ćemo pisati *T, odnosno T .
Kompajler zna da treba da generiše klase sa tipovima podataka koje mi navedemo u <>,
deklaracijom oblika Matrica<int> MatricaCelih(a,b); bi generisali matricu sa cjelobrojnim
elementima. Što se tiče pojedinih funkcija članica šablonske klase nema razlike u logici same
realizacije. Primetimo da smo prilikom preklapanja operator() kao rezultat vratili referencu a
ne samo vrednost podatka. To je bilo moguće jer je to element niza koji je podatak član
objekta za koji se poziva ta funkcija pa on postoji i nakon izvršavanja date funkcije. Razlog
63
zašto smo koristili referencu je, sa jedne strane, jer ne znamo tačno kojeg će tipa biti podaci
koji se smještaju u ovaj niz pa ne želimo da vršimo bespotrebno kopiranje moguće velikih
objekata. Sa druge strane, na ovaj način smo eliminisali potrebu za funkcijom koju bi koristili
za dodeljivanje vrednosti pojedinim elementima niza. Kada za određene indekse dobemo
upotrebom ovog operatora odgovarajući element mi smo dobili referencu na taj podatak, pa
će se sve promene nad njim odraziti nad podatkom niza, a ne nad njegovom kopijom. Npr.
(cin>>MatricaCelih(i,j);), ovde se vrši dodeljivanje vrednosti koju smo mi unijeli sa tastature
određenom elementu matrice (niza).
Kada realizujemo neku klasu čiji objekti želimo da su podaci ovog niza moramo
obezbijediti mogućnost pravilnog dodeljivanja vrednosti jednog objekta drugom. To znači da
u pojedinim slučajevima moramo preklopiti operator dodele. Mi za klasu kompleksnih
brojeva to nismo učinili. Zašto?
Primetimo da se u glavnom programu pristupa pojedinim elementima kao da su
zaista smješteni u matrici. Ovo je i cilj programskog jezika C++, korisnik ne zna na koji način
smo mi realizovali neku klasu, zna samo kako on može da je koristi. operator() smo
realizovali koristeći naredbu return podatak[(i-1)*kolona+j-1]; Ovo je logično jer se u matrici
elementi počinju indeksirati od jedinice a u memoriji ih mi čuvamo u nizu koji se indeksira od
nule pa će elementu (1,1), zapravo odgovarati prvi element u našem nizu, dakle element sa
indeksom 0. Nakon unošenja potrebnih podataka o broju vrsta i kolona definisali smo
matricu kompleksnih brojeva Matrica<Kompleks> MatricaKompleksnih(a,b);. Dalji tok
glavnog programa protumačite sami.
Primer rada programa
_____________________________________________________________________________
Koliko vrsta i kolona zelite da ima matrica celih brojeva?
2 3
Unesite elemente matrice
1 3 4 5 8 2
MatricaCelih(1,1)==1 MatricaCelih(1,2)==3 MatricaCelih(1,3)==4
MatricaCelih(2,1)==5 MatricaCelih(2,2)==8 MatricaCelih(2,3)==2
Koliko vrsta i kolona zelite da ima matrica kompleksnih brojeva?
2 3
1 2 2 3 5 6 2.3 4 8 2.1 0 10
MatricaKompleksnih(1,1)==(1,2) MatricaKompleksnih(1,2)==(2,3)
MatricaKompleksnih(1,3)==(5,6)
MatricaKompleksnih(2,1)==(2.3,4) MatricaKompleksnih(2,2)==(8,2.1)
MatricaKompleksnih(2,3)==(0,10)
64
b2(1,1)==(1,2) b2(1,2)==(2,3) b2(1,3)==(5,6)
b2(2,1)==(2.3,4) b2(2,2)==(8,2.1) b2(2,3)==(0,10)
b3(1,1)==(1,2) b3(1,2)==(2,3) b3(1,3)==(5,6)
b3(2,1)==(2.3,4) b3(2,2)==(8,2.1) b3(2,3)==(0,10)
_____________________________________________________________________________
Zadatak 6. 3
Sastaviti šablonsku funkciju koja će eliminisati ponavljanje istih elemenata u nekom nizu
(npr. ako imamo niz 2 3 4 3 2 1 5 daje niz 4 3 2 1 5), kao i šablonsku klasu koja će
predstavljati niz sa zadatim opsezima indeksa. Napisati glavni program u kojem će se data
funkcija pozivati za niz celih i niz realnih brojeva kao i izvršiti inicijalizacija objekata klase
niz za cele i realne elemente nizova dobenih izvršavanjem šablonske funkcije za niz celih ili
realnih elemenata (4 3 2 1 5).
#include <iostream> #include <conio.h> using namespace std; template <class S> int Sazimanje(S* niz,int duz){ int pom; int n=0; for(int i=0;i<duz-1;i++){ pom=0; for(int j=i+1;j<=duz-1;j++) if(niz[i]==niz[j]){ pom=1; break; } if(pom==0) {niz[n]=niz[i]; n=n+1;} } niz[n]=niz[duz-1]; return n+1; } template <class T> class Niz{ private: T* sadrzaj; int poc; int kraj; public: Niz(){sadrzaj=0;} Niz(T*,int,int=0); Niz(const Niz&); ~Niz(){delete []sadrzaj; sadrzaj=0;} T*Daj_sadrzaj(){return sadrzaj;} Niz&operator=(const Niz&); T operator[](int i){return sadrzaj[i];} void citaj(){for(int i=0;i<kraj-poc+1;i++) cout<<sadrzaj[i]<<" ";cout<<endl;} }; template<class T> Niz<T>::Niz(T * unos,int max,int min):poc(min),kraj(max){ sadrzaj=new T[max-min+1]; for(int i=0;i<max-min+1;i++) sadrzaj[i]=unos[i]; } template<class T> Niz<T>::Niz(const Niz& stara):poc(stara.poc),kraj(stara.kraj){
65
sadrzaj=new T[kraj-poc+1]; for(int i=0;i<max-min+1;i++) sadrzaj[i]=stara.sadrzaj[i]; } template<class T> Niz<T> &Niz<T>::operator=(const Niz<T>& stara){ if(this!=&stara){ delete []sadrzaj; poc=stara.poc; kraj=stara.kraj; sadrzaj=new T[kraj-poc+1]; for(int i=0;i<kraj-poc+1;i++) sadrzaj[i]=stara.sadrzaj[i]; } return *this; } void main(){ int niz[]={1,2,5,3,2,4,6,3,2}; Niz<int> Celi(niz,10,2); float realni[]={1.5,2.4,5,3,2.4,4,6,3,2.4}; Niz<float> Realni(realni,8); int n; n=Sazimanje(niz,9); for(int i=0;i<n;i++) cout<<niz[i]<<", "; cout<<endl; Sazimanje(realni,9); Celi.citaj(); Realni.citaj(); getch(); }
_____________________________________________________________________________
U realizaciji ovog zadatka smo koristili ranije navedena pravila za šablonske funkcije i
klase. Što se tiče same funkcije, kao rezultat dobijamo broj elemenata novog niza. Za eliminisanje
ponavljanja koristimo pomoćnu promenljivu koja ima vrednost 0 ukoliko posmatranog elementa
nema do kraja niza, uz taj uslov prepisuje se tako dobeni element u sažeti niz. Ukoliko je njena
vrednost jednaka jedinici znači da se taj element već pojavljivao i da ga ne treba upisivati. To
ćemo učiniti samo kod njegovog poslednjeg pojavljivanja.
Što se tiče klase, preklopili smo operator[] jer nam je moguć slučaj da niz objekata
konkretne klase, generisane na osnovu date šablonske, bude argument neke generisane funkcije. U
tom slučaju bi zahtevali pristup određenom elementu niza, što smo preklapanjem ovog operatora
omogućili. Ovde smo preklopili i operator dodele da bi se podsjetili načina na koji se to radi, iako
ga nismo nigdje kasnije koristili.
Primer rada programa
_____________________________________________________________________________
1, 5, 4, 6, 3, 2,
1 2 5 3 2 4 6 3 2
1.5 2.4 5 3 2.4 4 6 3 2.4
_____________________________________________________________________________
7. Izuzeci
Zadatak 7. 1
Projektovati klasu za obradu vektora realnih brojeva sa zadatim opsezima indeksa. Za
razrešavanje konfliktnih situacija koristiti mehanizam obrade izuzetaka. Sastaviti glavni
program za prikazivanjenje mogućnosti te klase.
_____________________________________________________________________________
66
#include <iostream> #include <conio.h> #include <string.h> using namespace std; class Vektor{ private: float *niz; int max_ops; int min_ops; public: enum Greska{OK, //Kodovi gresaka OPSEG, //neispravan opseg indeksiranja MEMORIJA,//dodjela memorije nije uspjela, PRAZAN, //vektor je prazan INDEKS, //indeks je izvan opsega DUZINA}; //neusaglasene duzine vektora Vektor(){niz=0;} Vektor(float, float=0); Vektor(const Vektor &); ~Vektor(){delete []niz;niz=0;} Vektor &operator=(const Vektor &v); float &operator[](int)const; //referencu saljem kao rezultat da // bi se mogao mijenjati sadrzaj elementa kojem se pristupa pomocu []. friend double operator*(const Vektor&,const Vektor &); }; Vektor::Vektor(float a,float b):max_ops(a),min_ops(b){ if(max_ops<min_ops) throw(OPSEG); else if(!(niz=new float[max_ops-min_ops+1])) throw MEMORIJA; else for(int i=0;i<max_ops-min_ops+1;i++) niz[i]=0; } Vektor::Vektor(const Vektor &a):max_ops(a.max_ops),min_ops(a.min_ops){ if(!(niz=new float[max_ops-min_ops+1])) throw MEMORIJA; else for(int i=0;i<max_ops-min_ops+1;i++) niz[i]=a.niz[i]; } Vektor &Vektor::operator=(const Vektor& a){ if(&a!=this){ delete []niz; niz=0; if(!(niz=new float[max_ops-min_ops+1])) throw MEMORIJA; else for(int i=0;i<max_ops-min_ops+1;i++) niz[i]=a.niz[i]; } return *this; } float &Vektor::operator[](int i)const{ if(!niz) throw PRAZAN;//u niz nije nista upisano, // samo je izvrsen default konstruktor else if((i<min_ops)||(i>max_ops)) throw INDEKS; else return niz[i-min_ops]; } double operator*(const Vektor& a1,const Vektor& a2){ if((!a1.niz)||(!a2.niz)) throw Vektor::PRAZAN; else { float s=0; for(int i=0;i<a1.max_ops-a1.min_ops+1;i++) s+=a1.niz[i]*a2.niz[i]; return s; } } main(){
67
while(1){ try{ int max, min; cout<<"Unesi opseg indeksa prvog vektora"<<endl; cin>>max>>min; if(cin.eof()) throw 1; Vektor v1(max,min); cout<<"Komponente prvog vektora"<<endl; for(int i=min;i<=max;i++) cin>>v1[i]; cout<<"Unesi opseg indeksa drugog vektora"<<endl; cin>>max>>min; Vektor v2(max,min); cout<<"Komponente drugog vektora"<<endl; for(int i=min;i<=max;i++) cin>>v2[i]; cout<<"Skalarni proizvod dva zadata vektora je "<<v1*v2<<endl; } catch(Vektor::Greska g){ char *poruke[]={"", "Neispravan opseg indeksa!", "Neuspjelo dodeljivanje memorije!" "Vektor je prazan!", "Indeks je izvan opsega!" }; cout<<poruke[g]<<endl; } catch(...){ cout<<"Kraj unosa"<<endl; break; } } getch();getch(); }
_____________________________________________________________________________
Podsetimo se, najpre, tipa nabrajanja enum. Ime koje smo dali ovom tipu je Greska i
promenljive ovog tipa mogu uzeti samo neku od vrednosti navedenih unutar {}. Nabrajanjem
smo, zapravo, definisali neke cjelobrojne konstante simboličkih imena OK, OPSEG,…. Njima u
okviru {} možemo dodeliti tačno određene cjelobrojne vrednosti.Aako to ne učinimo biće im
dodijeljene vrednosti od 0 pa 1 i tako dalje, svaka sledeća ima za jedan veću vrednost.
Identifikator njihovog tipa je Greska, a ne int, iako imaju celobrojnu vrednost. Znači, Greška je
ime novog tipa a OK, OPSEG..., vrednosti koje neka promenljiva tog tipa može imati, isto kao
float ime tipa a 2.4 vrednost koju promijenjiva tog tipa može uzeti.
U ovom zadatku nam je cilj da ilustrujemo generisanje i obradu izuzetaka. Kada se radi
sa izuzecima mogu se posmatrati dve odvojene faze: prva je otkrivanje i generisanje izuzetaka, a
druga obrada datog izuzetka. Kada otkrijemo mogućnost pojave neke greške u programu, koja
nije sintaksnog i semantičkog tipa, a želimo da ostavimo mogućnost njene obrade, a ne samo
prekidanje programa bez ikakvog objašnjenja, možemo postaviti izuzetak. Izuzetak se generiše
ključnom rečju throw nakon koje pišemo neki podatak određenog tipa (3, 2.4..) koji će biti
identifikator tog izuzetka. Naime, ukoliko smo napisali throw 3, nastao je izuzetak cjelobrojnog
tipa. Izuzetak se prihvata ključnom rečju catch(), gdje u zagradama pišemo tip izuzetka koji se tu
obrađuje, catch(int) u prethodnom slučaju. Deo programa u kojem može nastati izuzetak
stavljamo iza ključne reči try u okviru {} nakon kojih se nalaze ključne reči catch() koje prihvataju i
obrađuju određene izuzetke.
68
Što se tiče same klase, ona predstavlja niz kod kojeg je tačno definisan opseg, odnosno,
minimalna i maksimalna moguća vrednost indeksa. Greška će nastati već prilikom formiranja niza
kada korisnik želi inicijalizovati objekat tako da ima maksimalni opseg manji od minimalnog. To
smo uzeli kao drugu vrednost koju mogu imati podaci tipa Greska-OPSEG. Na mjestima na kojima
može doći do ovakve greške postavili smo izuzetak throw OPSEG. Druga greška nastaje kada
želimo da alociramo memoriju za taj niz ali u dinamičkoj zoni memorije nema dovoljno mesta za
cio niz, postavljamo izuzetak throw MEMORIJA. Vidimo da će i jedan i drugi da prihvata isti
rukovalac catch tipa Greska ali će u zavisnosti od vrednosti da odradi različite akcije. Ako g ima
vrednost 1 (OPSEG), čita g[1] string, odnosno, za g=2 (MEMORIJA) čita g[2] string. Sledeća greška
može nastati prilikom čitanja, ukoliko želimo uzeti nešto iz praznog niza throw PRAZAN ili čitati
nešto iz elementa sa indeksom van opsega tu postavljamo izuzetak throw INDEKS. Još jedan
izuzetak može nastati kada želimo preklopiti operator * za dva niza različitih dužina, throw
DUZINA. Gdje i na koji način biste ga postavili.
Ukoliko u rukovaocu izuzetka ne koristimo vrednost koja mu je proslijeđena nakon
identifikatora tipa ne moramo navoditi ime promenljive u koju će se kopirati poslata vrednost,
catch(int). U tom slučaju nam ta vrednost treba samo da na osnovu njenog tipa vidimo koji
rukovalac će prihvatiti naš izuzetak (odgovara našem izuzetku).
Prođimo jednom kroz cio kod. Imamo klasu Vektor sa tri privatna podatka člana, dva
nam određuju maksimalnu i minimalnu moguću vrednost indeksa, a treći predstavlja pokazivač
na početak niza. Zatim imamo jedan javni podatak tipa nabrajanja Greska koji može imati jednu
od vrednosti navedenih unutar {}. Default konstrukor je definisan na ranije korišćen način. Već
prilikom definicije prvog konstruktora može doći do greške. Greška je ukoliko zadamo da je
vrednost najmanjeg indeksa manja od vrednosti najvećeg. Tu postavljamo izuzetak throw OPSEG.
Njegov tip je Vektor::Greska pa ga prihvata rukovalac throw(Vektor::Greska g) U ovom slučaju će
g imati vrednost 1 i ispisaće se drugi string iz niza stringova na koji ukazuje pokazivač poruke.
Sledeća greška nastaje ako nije ispravno alocirana memorija, postavljamo odgovarajući izuzetak.
Ako je sve u redu inicijalizujemo sve elemente niza na nulu. U konstruktoru kopije nastaje greška
ukoliko u dinamičkoj zoni memorije nema dovoljno mesta za smeštanje svih elemenata niza. Ista
greška može nastati i prilikom realizacije operatora dodele pa smo i u okviru njega postavili isti
izuzetak. Prilikom realizacije operatora indeksiranja, [], može nastati greška ukoliko je niz prazan
a mi želimo nešto isčitati iz njega. To bi se desilo ukoliko smo neki objekat samo deklarisali gdje
je pozvan default konstruktor koji je postavio odgovarajući pokazivač na 0, nije alocirao
memoriju niti je odredeo opseg vrednosti indeksa niza. Operator indeksiranja smo realizovali kao
u ranijim primerima. S obzirom da nam najmanji indeks ne mora biti 0 već ima vrednost
smještenu u podatku min_ops, kada korisnik želi element sa indeksom i mi mu dajemo element
koji je smješten na poziciji i-min_ops iako korisnik misli da je to element sa indeksom i. Ovde
nastaje greška ukoliko je indeks tražene komponente van dozvoljenog opsega. Operator * nam
predstavlja skalarni proizvod dva vektora, greška nastupa ukoliko je jedan od vektora prazan.
Pokušajte da postavite izuzetak koji bi javljao grešku kada je različita dužina dva vektora koje
“množimo”. Ovde smo morali navesti i klasu kojoj pripada simbolička konstanta kojom
postavljamo izuzetak jer je ovo funkcija prijatelj klase, a ne članica, pa ona ima pravo pristupa
tim podacima ali se mora navesti klasa čiji je taj podatak član. U glavnom programu se nalazi
jedna petlja while(1) i izvršavaće se sve dok umesto opsega indeksa prvog vektora ne unesemo
znak za kraj dadoteke CTRL-Z, nakon čega dolazi do izuzetka tipa int čiji će rukovalac izvršiti
69
prekidanje while petlje (naredba break;). Koristili smo ovu petlju da bismo mogli u okviru jedne
realizacije programa generisati više izuzetaka. Da nje nema nakon prvog izuzetka došlo bi do
preskoka na prvog odgovarajućeg rukovaoca nakon try i ne bismo se više vraćali nazad. Išlo bi se
na prvu naredbu nakon catch dijela a to bi bio kraj programa. Sada je to sledeći ciklus u petlji,
dakle unošenje novih podataka.
Primer rada programa
_____________________________________________________________________________
Unesi opseg indeksa prvog vektora
1 3
Neispravan opseg indeksa!
Unesi opseg indeksa prvog vektora
3 1
Komponente prvog vektora
1 4 5
Unesi opseg indeksa drugog vektora
3 1
Komponente drugog vektora
4 3 2
Skalarni proizvod dva zadata vektora je 26
Unesi opseg indeksa prvog vektora
Kraj unosa
8. Razni zadaci
Zadatak 8.1.
Kreirati klasu Permutacija koja omogućava pamćenje jedne permutacije brojeva 1, ..., n i ima:
- Konstruktor Permutacija(int n). Ovaj konstruktor zadaje slučajnu permutaciju od n elemenata.
- Konstruktor Permutacija(int n, int *a). Ovim konstruktorom se zadaje permutacija.
- Destruktor.
- Odgovarajuće getere i setere.
- Metod int redniBroj(), koji ordeđuje redni broj permutacije u leksikografskom poretku.
- Metod int generisiLeksikografsku(int n), koji generiše permutaciju sa rednim brojem n u
leksikografskom poretku. Rezultat je 0 ako generisanje nije uspelo, inače 1.
- Metod int sledeca(), koji određuje sledeću permutaciju u leksikografskom poretku. Rezultat je
0 ukoliko ne postoji sledeća permutacija, inače 1.
U glavnom programu testirati svaki od metoda.
70
//Permutacija.h
#pragma once
class CPermutacija
{
int m_iN;
int *m_pPerm;
void swap(int *a, int *b);
public:
CPermutacija(int n);
CPermutacija(int n, int *niz);
~CPermutacija(void);
void setPerm(int n, int *niz);
int sledeca();
void stampaj();
int getN(void);
int* getPerm(void);
};
//Permutacija.cpp
#include "Permutacija.h"
#include <stdlib.h>
#include <time.h>
#include <stdeo.h>
CPermutacija::CPermutacija(int n)
{
int i;
m_iN = n;
m_pPerm = new int[n + 1];
for(i = 1; i <= n; i++)
m_pPerm[i] = i;
for(i = 1; i <= n; i++)
swap(&m_pPerm[i], &m_pPerm[(rand() % i) + 1]);
}
CPermutacija::CPermutacija(int n, int *niz)
{
m_iN = n;
m_pPerm = new int[n + 1];
m_pPerm[0] = 0;
for (int i = 1; i <= n; i++)
m_pPerm[i] = niz[i];
}
void CPermutacija::swap(int *a, int *b)
{
int tmp;
tmp = *a; *a = *b; *b = tmp;
}
CPermutacija::~CPermutacija(void)
{
delete[] m_pPerm;
m_pPerm = NULL;
m_iN = 0;
}
71
void CPermutacija::setPerm(int n, int *niz)
{
m_iN = n;
delete[] m_pPerm;
m_pPerm = new int[n + 1];
m_pPerm[0] = 0;
for (int i = 1; i <= n; i++)
m_pPerm[i] = niz[i];
}
int CPermutacija::sledeca()
{
int i = m_iN;
while((i > 1) && (m_pPerm[i-1] > m_pPerm[i]))
i--;
if(i == 1) return 0;
int j = i - 1;
i = m_iN;
while(m_pPerm[i] < m_pPerm[j]) i--;
swap(m_pPerm + i, m_pPerm + j);
int k = j + 1;
int l = m_iN;
while(k < l)
swap(&m_pPerm[k++], &m_pPerm[l--]);
return 1;
}
int CPermutacija::getN()
{
return m_iN;
}
int* CPermutacija::getPerm()
{
return m_pPerm;
}
void CPermutacija::stampaj(){
for(int i = 1; i <= m_iN; i++){
printf("%3d", m_pPerm[i]);
}
printf("\n");
}
#include <stdeo.h>
#include <stdlib.h>
#include <time.h>
#include "Permutacija.h"
int main()
{
srand((unsigned int) time(NULL));
int n, m, i, niz[10];
/* random permutacija */
printf("Unesite broj elemenata za generisanje random permutacije\n");
//scanf("%d", &m);
72
//CPermutacija p(m);
//n = p.getN();
//for (i = 1; i <= n; i++)
// printf("%d ", (p.getPerm())[i]);
//printf("\n");
/* 20 random permutacija i njihove sledece */
scanf("%d", &m);
CPermutacija * p1;
for(int kk = 0; kk < 20; kk++){
p1 = new CPermutacija(m);
p1->stampaj();
p1->sledeca();
p1->stampaj();
printf("-----------------------\n");
delete p1;
}
/* Unos permutacije i njena sledeca */
//printf("Zadajte permutaciju\n");
//scanf("%d", &m);
//for(i = 1; i <= m; i++)
// scanf("%d", niz+i);
//CPermutacija pp(m, niz);
//int q = pp.sledeca();
//if(q == 0) printf("Ovo je poslednja permutacija");
//else
//{
// printf("Sledeca permutacija je:\n");
// for (i = 1; i <= n; i++)
// printf("%d ", (pp.getPerm())[i]);
//}
//printf("\n");
/* Pozivanje konstruktora sa 2 arg */
//scanf("%d", &n);
//for (i = 1; i <= n; i++)
// scanf("%d", &niz[i]);
////p.setPerm(n, niz);
//CPermutacija p(n,niz);
//n = p.getN();
//for (i = 1; i <= n; i++)
// printf("%d ", (p.getPerm())[i]);
return 0;
}
Zadatak 8. 2.
Kreirati klasu Slagalica koja omogućava zadavanje tajne reči, slučajno permutovanje znakova tajne
reči, kao i dve operacije – zameni(i, j), rotiraj(i, j). Operacija zameni(i, j) menja mesta
i – tom i j – tom karakteru u transformisanoj reči, a rotiraj(i, j) rotira sve karaktere od i – tog do
j – tog u desno ako je i < j, odnosno u levo ako je i > j.
U glavnom programu omogućiti igranje sledeće igre – kompjuter učitava niz reči iz datoteke
‘recnik.dat’, odabira slučajno jednu od njih i permutuje joj karaktere. Posle toga prikazuje
permutovanu reč korisniku i traži od njega komande. Posle svake komande treba odštampati
promenjenu reč. Igra se završava kada igrač pogodi traženu reč ili posle 10 pokušaja.
73
//slagalica.h
#pragma once #include <string.h> #include <stdlib.h> #include <time.h> class Slagalica { /* Rec koja se trazi */ char * tajnaRec; /* Rec koja se ispisuje korisniku */ char * trenutnaRec; /* Duzina date reci */ int duzinaReci; /* Trenutni broj pokusaja */ int brojPokusaja; /* Inicjalizacija vrednosti */ void init(char * rec); /* Permutovanje karaktera date reci */ void permutuj(void); public: /* Konstruktori */ Slagalica(void); Slagalica(char * rec); /* Destruktor */ ~Slagalica(void); /* Komande */ void zameni(int i, int j); void rotiraj(int i, int j); /* Pomocni metodi */ void ponistiBrojPokusaja(void); int getBrojPokusaja(void); void postaviRec(char * rec); int proveri(); /* Vraca trenutnu rec */ char * rec(); /* Vraca tajnu rec */ char * Rec(); }; //Slagalica.cpp #include "Slagalica.h" Slagalica::Slagalica(void) { tajnaRec = NULL; trenutnaRec = NULL; duzinaReci = 0; brojPokusaja = 0; } Slagalica::~Slagalica(void) { if(tajnaRec != NULL) delete[] tajnaRec; if(trenutnaRec != NULL) delete[] trenutnaRec; } Slagalica::Slagalica(char * rec){
74
tajnaRec = NULL; trenutnaRec = NULL; duzinaReci = 0; brojPokusaja = 0; init(rec); } void Slagalica::permutuj(){ if(duzinaReci == 0) return; if(trenutnaRec != NULL) delete[] trenutnaRec; trenutnaRec = new char[duzinaReci + 1]; strcpy(trenutnaRec, tajnaRec); srand((unsigned int)time(0)); for(int i = 0; i < duzinaReci; i++){ int j = rand() % (duzinaReci - i) + i; if(i != j){ char pom = trenutnaRec[i]; trenutnaRec[i] = trenutnaRec[j]; trenutnaRec[j] = pom; } } } void Slagalica::zameni(int i, int j){ if((i < 0) || (j < 0) || (i >= duzinaReci) || (j >= duzinaReci)) return; char pom = trenutnaRec[i]; trenutnaRec[i] = trenutnaRec[j]; trenutnaRec[j] = pom; brojPokusaja++; } void Slagalica::rotiraj(int i, int j){ if((i < 0) || (j < 0) || (i >= duzinaReci) || (j >= duzinaReci)) return; int k; char pom; if(i < j){ /* Pomeranje u desno */ pom = trenutnaRec[j]; for(k = j - 1; k >= i; k--) trenutnaRec[k + 1] = trenutnaRec[k]; trenutnaRec[i] = pom; } else{ /* Pomeranje u levo */ pom = trenutnaRec[j]; for(k = j; k < i; k++) trenutnaRec[k] = trenutnaRec[k + 1]; trenutnaRec[i] = pom; } brojPokusaja++; } void Slagalica::ponistiBrojPokusaja(void) { brojPokusaja = 0; } int Slagalica::getBrojPokusaja(void) { return brojPokusaja; } void Slagalica::postaviRec(char * rec){ init(rec);
75
} void Slagalica::init(char * rec){ int n = strlen(rec); if(n == 0) return; tajnaRec = new char[n + 1]; strcpy(tajnaRec, rec); duzinaReci = n; permutuj(); } char * Slagalica::rec(){ return trenutnaRec; } int Slagalica::proveri(){ int rez = strcmp(trenutnaRec, tajnaRec); return rez == 0; } char * Slagalica::Rec(){ return tajnaRec; } //main.cpp #include <stdeo.h> #include "Slagalica.h" int main(){ Slagalica * s; s = new Slagalica("Kombinatorika"); char komanda[10]; int i, j; printf("Komande:\n\tz i j - zamenjuje i-ti i j-ti karakter\n\tr i j - rotira karaktere od i do j u desno ako je i < j, a inace u levo\n\n"); do{ printf("Trenutna rec: %s\n\n", s->rec()); printf("Unesi komandu...\n"); scanf("%s %d %d", komanda, &i, &j); if(komanda[0] == 'z') s->zameni(i, j); if(komanda[0] == 'r') s->rotiraj(i, j); } while((!s->proveri()) && (s->getBrojPokusaja() < 10)); if(s->proveri()) printf("Cestitam, pogodili ste tajnu rec: %s\n", s->rec()); else printf("Zao mi je. Trazena rec je %s\n", s->Rec()); delete s; return 0; }
Zadatak 8.3
Kreirati klasu KompleksniBroj koja omogućava rad sa kompleksnim brojevima. Klasa bi trebalo da ima:
podatke re i im, za realni i imaginarni deo kompleksnog broja;
konstruktor sa dva parametra koji postavlja vrednosti za realni i imaginarni deo kompleksnog broja;
konstruktor bez parametara koji postavlja vrednost kompleksnog broja na 0;
odgovarajuće getere i setere;
76
javni metod moduo() koji kao rezultat vraća moduo kompleksnog broja;
javne metode kojima se izvode osnovne operacije sa kompleksnim brojevima (sabiranje, oduzimanje, množenje, stepenovanje, deljenje).
77
Zadatak 8.4
Kreirati klasu Razlomak koja omogućava rad sa racionalnim brojevima. Klasa bi trebalo da ima:
podatke br i im, za brojilac i imenilac razlomka;
privatni metod skrati() koji dovodi razlomak na neskrativi oblik. Može se definisati i privatni metod int nzd(int a, int b) koji određuje najveći zajednički delilac dva cela broja;
konstruktor sa dva parametra koji postavlja vrednosti za brojilac i imenilac. Voditi računa o tome da imenilac ne može biti 0;
konstruktor bez parametara koji postavlja vrednost kompleksnog broja na 0. U tom slučaju je imenilac jednak 1;
odgovarajuće getere i setere;
javne metode kojima se izvode osnovne operacije sa razlomcima (sabiranje, oduzimanje, množenje, stepenovanje, deljenje).
//Razlomak.h #pragma once
class CRazlomak
{
int br;
int im;
int nzd(int, int);
void skrati();
public:
CRazlomak(void);
CRazlomak(int, int);
~CRazlomak(void);
int getBr(void);
void setBr(int);
int getIm(void);
void setIm(int);
void postaviNaNulu(void);
void stampaj();
CRazlomak
operator+(CRazlomak);
//CRazlomak
saberi(CRazlomak);
CRazlomak operator-
(CRazlomak);
CRazlomak
operator*(CRazlomak);
CRazlomak
operator/(CRazlomak);
};
//Razlomak.cpp #include<stdio.h>
#include<math.h>
#include "Razlomak.h"
int CRazlomak::nzd(int a, int b)
{
while(a != b)
{
if(a > b) a -= b;
else b -= a;
} return a;
}
void CRazlomak::skrati(void)
{
if(br != 0)
{
int n = nzd(abs(br),
abs(im));
br /= n; im /= n;
}
else im = 1;
}
CRazlomak::CRazlomak(void)
{
}
CRazlomak::CRazlomak(int b, int i)
{
if(i==0)
{
br = 0; im = 1;
}
else
{
br = b; im = i;
}
skrati();
}
CRazlomak::~CRazlomak(void)
{
}
int CRazlomak::getBr(void)
{
return br;
}
void CRazlomak::setBr(int b)
{
br = b;
skrati();
78
}
int CRazlomak::getIm(void)
{
return im;
}
void CRazlomak::setIm(int i)
{
if(i != 0) im = i;
else
{
br = 0; im = 1;
}
skrati();
}
void
CRazlomak::postaviNaNulu(void)
{
br = 0; im = 1;
}
CRazlomak CRazlomak::operator
+(CRazlomak r1)
{
CRazlomak r;
r.br = (br * r1.im + r1.br *
im);
r.im = (im * r1.im);
r.skrati();
return r;
}
/* CRazlomak
CRazlomak::saberi(CRazlomak r1)
{
CRazlomak r;
r.br = (br * r1.im + r1.br *
im);
r.im = (im * r1.im);
r.skrati();
return r;
} */
CRazlomak CRazlomak::operator -
(CRazlomak r1)
{
CRazlomak r;
r.br = (br * r1.im - r1.br *
im);
r.im = (im * r1.im);
r.skrati();
return r;
}
CRazlomak CRazlomak::operator
*(CRazlomak r1)
{
CRazlomak r;
r.br = br * r1.br;
r.im = im * r1.im;
r.skrati();
return r; }
CRazlomak CRazlomak::operator
/(CRazlomak r1)
{
CRazlomak r;
r.br = br * r1.im;
r.im = im * r1.br;
r.skrati();
return r;
}
void CRazlomak::stampaj(void)
{
printf("%d/%d\n",br,im);
}
//main.cpp
#include<stdio.h>
#include "Razlomak.h"
int main()
{
int a, b;
scanf("%d%d",&a, &b);
CRazlomak r(a, b);
r.stampaj();
/* scanf("%d",&a);
r.setBr(a);
r.stampaj();
scanf("%d",&b);
r.setIm(b);
r.stampaj(); */
/* r.postaviNaNulu();
r.stampaj(); */
scanf("%d%d",&a, &b);
CRazlomak r1(a, b);
r1.stampaj();
CRazlomak r2;
//r2 = r.saberi(r1);
r2 = r + r1;
r2.stampaj();
r2 = r - r1;
r2.stampaj();
r2 = r * r1;
r2.stampaj();
r2 = r / r1;
r2.stampaj();
return 0;
}
79
Zadatak 8.5
Definisati klasu Tacka koja predstavlja tačku sa celobrojnim koordinatama u ravni. Napisati
konstruktor bez parametara, kao i konstruktor sa dva celobrojna parametra. Preklopiti operator +
tako da je moguće sabrati dve promenljive tipa Tacka. Rezultat je nova tačka čije su koordinate
zbirovi koordinata operanada.
Definisati klasu Figura koja ima članove brTacaka i nizTacaka. Napisati konstruktor bez
parametara, kao i konstruktor sa dva parametra – prvi tipa int, a drugi tipa Tacka *. Za ovu
klasu preklopiti operator + tako da se njime dodaje nova tačka u niz, kao i operator – tako da se
njime briše i – ta tačka iz niza. Dodati metod obim koji kao rezultat daje obim figure (pretpostavlja
se da su temena figure data redom).
L I T E R A T U R A
[1] D. Milićev: "Objektno-orjentisano programiranje na jeziku C++," Mikroknjiga, 1995.
[2] H. M. Deitel, P. J. Deitel: "C++ how to program," Prentice Hall, 1997.
[3] J. Liberty, D. B. Horvath: "C++ za LINUX," SAMS Publishing (2000), Kompjuter biblioteka
(prevedeno izdanje) 2002.
[4] L. Kraus: "Programski jezik C++ sa rešenim zadacima," Mikro knjiga, 1994.
[5] K. Reisdorph: "Teach yourself C++Builder in 21 day", SAMS Publishing.
[6] D. Milićev, LJ. Lazarević, J. Marušić: "Objektno orjentisano programiranje na jeziku C++ -
Skripta sa praktikumom," Mikro knjiga 2001.
[7] N. Wirth: "Algorithms + Data structures = Programs," Prentice Hall, 1976.
[8] G. Booch, J. Rumbaugh, I. Jacobson: "UML – vodič za korisnike," Addison Wesley 1999,
CET 2000 (prevedeno izdanje).
[9] Jan Skansholm, "C++ from the beginning",.Addison-Wesley, 2003.
80
Programski jezik JAVA
1. Rad sa objektima klase StringBuffer, demonstracija nekih funkcionalnosti
public class NasStringBuffer {
/*
* Rad sa objektima klase StringBuffer, demonstriranje
* funkcionalnosti nekih funkcija za rad sa objektima
* ove klase.
*/
/* Kapacitet, tj. velicina bafera objekta se automatski povecava kako
bi se on prilagodeo
* stringu proizvoljne duzine i to tako da ako je duzina novog
stringa veca od tekuceg
* kapaciteta, novi kapacitet bafera bice veci od sledeca dva broja:
* nova duzina stringa i
* 2*stari_kapacitet+2
*/
public static void main(String[] args) {
StringBuffer recenica = new StringBuffer(20);
System.out.println("Kapacitet StringBuffer objekta je: "+
recenica.capacity()+
" a duzina stringa je: "+recenica.length());
// Dodajemo reci niza u StringBuffer objekat
String[] niz_reci = {"Ako" , "danas", "bude", "snega", "bicu",
"srecan"};
recenica.append(niz_reci[0]);
for(int i = 1 ; i<niz_reci.length ; i++) {
recenica.append(' ').append(niz_reci[i]);
}
System.out.println("\nString koji se nalazi u StringBuffer
objektu je:\n" +
recenica.toString());
System.out.println("Kapacitet StringBuffer objekta je sada: "+
recenica.capacity()+
" a duzina stringa je: "+recenica.length());
// Vrsimo zamenu neke niske u objektu nekom drugom
int poz=recenica.lastIndexOf("snega");
recenica.replace(poz, poz+5,"kise");
// Vrsimo umetanje nekih niski u objekat klase StringBuffer
recenica.insert(recenica.lastIndexOf("srecan"), "ne");
System.out.println("\nString koji se nalazi StringBuffer
objektu je:\n" + recenica);
System.out.println("Kapacitet StringBuffer objekta je sada: "+
recenica.capacity()+
" a duzina stringa je: "+recenica.length());
}
}
2. Brojimo pojavljivanjue reci the i and u zadatom tekstu
public class BrojanjeReci {
81
/*
* Brojimo pojavljivanjue reci the i and u zadatom tekstu
*/
public static void main(String[] args) {
// Tekst koji analiziramo
String text = "To be or not to be, that is the question;"
+ " Whether ‘tis nobler in the mind to suffer"
+ " the slings and arrows of outrageous fortune,"
+ " or to take arms against a sea of troubles,"
+ " and by opposing end them?";
int andBr = 0;
int theBr = 0;
int indeks = -1;
String andStr = "and";
String theStr = "the";
// Trazimo pojavljivanje reci "and"
indeks = text.indexOf(andStr); // Nalazimo prvu pojavu reci
"and"
while(indeks >= 0) {
++andBr;
indeks += andStr.length(); // Postavljamo se na mesto
posle
// pojave tekuceg "and"
indeks = text.indexOf(andStr, indeks);
}
// Trazimo unazad pojavljivanje reci "the"
indeks = text.lastIndexOf(theStr); // Nalazimo poslednju pojavu
// reci "the"
while(indeks >= 0) {
++theBr;
indeks -= theStr.length(); // Postavljamo se na mesto pre
// pojave tekuceg "and"
indeks = text.lastIndexOf(theStr, indeks);
}
System.out.println("Zadati tekst sadrzi " + andBr + " pojave
reci 'and'\n"
+ "i " + theBr + " pojava reci 'the'.");
}
}
3. Skener
import java.util.Scanner;
public class Skener
{
/*
* Ucitavamo dva broja sa standardnog ulaza i ispisujemo njihov zbir.
*/
public static void main(String [] args)
{
Scanner skener = new Scanner(System.in);
System.out.println("Unesite dva cela broja");
int a = skener.nextInt();
int b = skener.nextInt();
int zbir = a + b;
System.out.println("Zbir brojeva " + a + " i " + b + " je " +
zbir);
}
}
82
4. Nizovi
package nizovi;
import java.util.Arrays;
public class SortiranjeNiza {
public static void main(String[] args) {
int a[]={-12,9,8,-8,5, 6, 1, 3};
Arrays.sort(a);
for(int i=0; i<a.length; i++)
System.out.print(a[i]+" ");
System.out.println();
}
}
5. Učitavamo elemente niza i računamo aritmetičku sredinu.
import java.util.Scanner;
public class AritmetickaSredina{
/*
* Ucitavamo broj elemenata niza i njegove elemente
* i racunamo zbir elemenata niza i njihovu aritmeticku sredinu.
*/
public static void main(String[] args){
Scanner skener = new Scanner(System.in);
System.out.println("Unesite broj elemenata niza:");
int brojEl = skener.nextInt();
int[] nizBrojeva = new int[brojEl];
int zbir = 0;
// ucitavamo elemente niza i formiramo zbir
for (int i = 0; i < brojEl; i++)
{
System.out.print((i + 1) + ". broj: ");
nizBrojeva[i] = skener.nextInt();
zbir += nizBrojeva[i];
}
System.out.println("Zbir brojeva je: " + zbir);
// racunamo aritmeticku sredinu
double aritmSredina = (double) zbir / brojEl;
System.out.println("Aritmeticka sredina je: " + aritmSredina);
}
}
6. Isto što i malopre, samo malo drugačije
import java.util.Scanner;
public class AritmetickaSredina2{
/*
* Sa standardnog ulaza ucitavamo elemente niza sve dok se ne unese 0,
* i racunamo zbir elemenata niza i njihovu aritmeticku sredinu.
*/
public static void main(String[] args){
83
Scanner skener = new Scanner(System.in);
int[] nizBrojeva = new int[100];
int zbir = 0;
int brojEl = 0;
// Sve dok se ne unese nula, ucitavamo brojeve
// sa standardnog ulaza i racunamo njihov zbir.
for (brojEl = 0; brojEl < 100; brojEl++)
{
System.out.print((brojEl + 1) + ". broj: ");
nizBrojeva[brojEl] = skener.nextInt();
if (nizBrojeva[brojEl] == 0)
break;
zbir += nizBrojeva[brojEl];
}
System.out.println("Zbir brojeva je: " + zbir);
// Racunamo aritmeticku sredinu.
double aritmSredina = (double) zbir / brojEl;
System.out.println("Aritmeticka sredina je: " + aritmSredina);
}
}
7. Poređenje stringova
public class PoredjenjeStringova {
/*
* Razlika izmedju poredjenja dve stringovne promenljive koje pokazuju
* na dva stringa koja imaju isti sadrzaj, i poredjenja
* dve stringovne promenljive koje pokazuju na isti string.
*/
public static void main(String[] args) {
String string1 = "Dva ";
String string2 = "studenta";
String string3 = "Dva studenta";
// Postavljamo da string1 i string3 referisu
// na razlicite stringove koji su identicni
string1 += string2;
System.out.println("Test br 1");
System.out.println("string3 je: " + string3);
System.out.println("string1 je: " + string1);
// Testiramo da li je u pitanju isti string
if(string1 == string3)
System.out.println("string1 == string3 je tacno -" +
" string1 i string3 pokazuju na isti string");
else
System.out.println("string1 == string3 nije tacno -" +
" string1 i string3 ne pokazuju na isti string");
// // Postavljamo da string1 i string3 referisu
// na isti string
string3 = string1;
System.out.println("\nTest br 2");
System.out.println("string3 je: " + string3);
System.out.println("string1 je: " + string1);
84
if(string1 == string3) // Now test for identity
System.out.println("string1 == string3 je tacno -" +
" string1 i string3 pokazuju na isti string");
else
System.out.println("string1 == string3 nije tacno -" +
" string1 i string3 ne pokazuju na isti string");
}
}
8. Poređenje stringova još jednom
public class PoredjenjeStringova2 {
/*
* Poredjenje dve stringovne promenljive na jednakost sadrzaja.
*/
public static void main(String[] args) {
String string1 = "Dva ";
String string2 = "studenta";
String string3 = "Dva studenta";
// Postavljamo da string1 i string3 referisu
// na razlicite stringove koji su identicni
string1 += string2;
System.out.println("Test br 1");
System.out.println("string3 je: " + string3);
System.out.println("string1 je: " + string1);
// Testiramo stringove na jednakost sadrzaja
if(string1.equals(string3)) {
System.out.println("string1.equals(string3) je tacno - "
+
" stringovi su jednaki.");
} else {
System.out.println("string1.equals(string3) je netacno -
" +
" stringovi nisu jednaki.");
}
// Sada postavljamo string1 i string3 da referisu
// na stringove koji se razlikuju samo u velicini slova
string3 = "DVA studenta";
System.out.println("\nTest br 2");
System.out.println("string3 je: " + string3);
System.out.println("string1 je: " + string1);
// Testiramo stringove na jednakost sadrzaja
if(string1.equals(string3)) {
System.out.println("string1.equals(string3) je tacno - "
+
" stringovi su jednaki.");
} else {
System.out.println("string1.equals(string3) je netacno -"
+
" stringovi nisu jednaki.");
}
// Testiramo stringove na jednakost sadrzaja
// bez obaziranja na velicinu slova
if(string1.equalsIgnoreCase(string3)) {
System.out.println("string1.equalsIgnoreCase(string3) je
tacno - " +
" stringovi su jednaki ako zanemarimo velicinu slova.");
} else {
85
System.out.println("string1.equalsIgnoreCase(string3) je
netacno -" +
" stringovi su razliciti.");
}
}
}
9. Demonstracija rada sa matricama, relacija
package paket;
import java.util.Scanner;
public class Relacija {
public static boolean refleksivna(int m[][]){
for(int i=0; i<m.length; i++)
if(m[i][i]!=1)
return false;
return true;
}
public static boolean simetricna(int m[][]){
for(int i=0; i<m.length; i++)
for(int j=0; j<i; j++) /* idemo kroz trougao ispod
* glavne dijagonale */
if(m[i][j]!=m[j][i])
return false;
return true;
}
/**
* @param args
*/
public static void ispisi(int m[][])
{
for(int i=0; i<m.length; i++){
for(int j=0; j<m.length; j++)
System.out.print(m[i][j]+" ");
System.out.println();
}
}
public static void ispisi1(int [][]m){
for(int vrsta[]: m){
for(int elvrste: vrsta)
System.out.print(elvrste + " ");
System.out.println();
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc=new Scanner(System.in);
System.out.println("Unesite dim. matrice");
int dim=sc.nextInt();
int m[][]=new int[dim][dim];
System.out.println("Unesite el. matrice");
for(int i=0; i<dim; i++)
for(int j=0; j<dim; j++){
m[i][j]=sc.nextInt();
if(m[i][j]!=0 && m[i][j]!=1){
System.out.println("Neispravan unos: " + m[i][j]);
return;
86
}
}
if(refleksivna(m))
System.out.println("refleksivna je");
else
System.out.println("Nije refleksivna");
if(simetricna(m))
System.out.println("Simetricna je");
else
System.out.println("Nije simetricna");
ispisi(m);
System.out.println("Pomocu collection-based for-petlje");
ispisi1(m);
}
}
87
10. Rešavanje kvadratne jednačine
package kvadratneJednacine;
public class KvadratnaJednacina {
private double a;
private double b;
private double c;
public KvadratnaJednacina(double a, double b, double c)
{
this.a = a;
this.b = b;
this.c = c;
}
public KvadratnaJednacina izvod(){
return new KvadratnaJednacina(0, 2*a, b);
}
public double[] koreni(){
double koreni[] = new double[2];
double D = diskriminanta();
if(!imaRealneKorene())
koreni[0] = koreni[1] = 0;
else if(D == 0)
koreni[0] = koreni[1] = -b/(2*a);
else {
koreni[0] = (-b + Math.sqrt(D))/(2*a);
koreni[1] = (-b - Math.sqrt(D))/(2*a);
}
return koreni;
}
public double diskriminanta(){
return b*b - 4*a*c;
}
public boolean imaRealneKorene()
{
return b*b - 4*a*c >= 0 ? true : false;
}
public String toString()
{
String string = new String();
if(a == 0)
{
string += b + "*x";
string += c > 0 ? " + " + c : " " + c;
string += " = 0";
}
else
{
string += a + "*x^2";
string += b > 0 ? " + " + b + "*x" : " " + b + "*x";
string += c > 0 ? " + " + c : " " + c + " = 0";
string += " = 0";
}
return string;
}
}
11. Kreirati klasu Automobil koja ima:
88
a. celobrojne podatke trenutnaBrzina i maksimalnaBrzina koji predstavljaju trenutnu i maksimalnu brzinu automobila u km/h. Trenutna brzina automobila ne može biti veća od maksimalne;
b. realni podatak predjeniPut koji određuje koliko kilometara je automobil prešao; c. konstruktor sa jednim celobrojnim parametrom koji predstavlja maksimalnu brzinu
automobila. Ona ne može biti negativna. Trenutna brzina i pređeni put se postavljaju na 0;
d. javni metod void ubrzaj(int a) koji povećava trenutnu brzinu za a km/h, ali ne preko maksimalne brzine;
e. javni metod void uspori(int a) koji smanjuje trenutnu brzinu za a km/h, ali ne ispod 0;
f. javni metod void vozi(double t) koji na promenljivu predjeniPut dodaje put koji auto pređe za t časova, vozeći trenutnom brzinom;
g. odgovarajuće getere i setere; h. javni metod int ucitajKomande(char * fileName), odnosno, u Javi, int
ucitajKomande(String fileName) koji učitava i izvršava komande iz tekstualne datoteke čije je ime dato. Metod vraća 0 ako se pri čitanju desila neka greška, a inače 1. Komande su oblika UBRZAJ A, USPORI A, VOZI T i svaka je data u po jednom redu. U prvom redu datoteke se nalazi broj komandi.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package automobil;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author Student
*/
public class Automobil {
private int trenutnaBrzina;
private int maximalnaBrzina;
private double predjeniPut;
public Automobil(int maximalnaBrzina) {
setMaximalnaBrzina(maximalnaBrzina);
this.predjeniPut = 0;
this.trenutnaBrzina = 0;
}
public void ubrzaj(int a) {
if(this.trenutnaBrzina + a > this.maximalnaBrzina){
this.trenutnaBrzina = this.maximalnaBrzina;
}
else this.trenutnaBrzina += a;
}
public void uspori(int a) {
89
if(this.trenutnaBrzina - a < 0){
this.trenutnaBrzina = 0;
}
else this.trenutnaBrzina -= a;
}
void vozi(double t){
this.predjeniPut += this.trenutnaBrzina * t;
}
public int getMaximalnaBrzina() {
return maximalnaBrzina;
}
public void setMaximalnaBrzina(int maximalnaBrzina) {
if(maximalnaBrzina < 0) this.maximalnaBrzina = 10;
else this.maximalnaBrzina = maximalnaBrzina;
}
public double getPredjeniPut() {
return predjeniPut;
}
public void setPredjeniPut(double predjeniPut) {
if(predjeniPut < 0) this.predjeniPut = 0;
else this.predjeniPut = predjeniPut;
}
public int getTrenutnaBrzina() {
return trenutnaBrzina;
}
public void setTrenutnaBrzina(int trenutnaBrzina) {
this.trenutnaBrzina = trenutnaBrzina;
if(trenutnaBrzina < 0) this.trenutnaBrzina = 0;
if(trenutnaBrzina > this.maximalnaBrzina) this.trenutnaBrzina =
this.maximalnaBrzina;
}
public int ucitajKomande(String fileName) throws FileNotFoundException,
IOException, NumberFormatException {
BufferedReader in = new BufferedReader(new FileReader(fileName));
int brojKomandi;
String s = null;
s = in.readLine();
brojKomandi = Integer.parseInt(s);
for(int i = 0; i < brojKomandi; i++){
s = in.readLine();
StringTokenizer st = new StringTokenizer(s);
String komanda = (String)st.nextElement();
double broj = Double.parseDouble((String)st.nextElement());
System.out.println("komanda: " + komanda + " broj: " +
broj);
if(komanda.compareTo("UBRZAJ") == 0){
this.ubrzaj((int) broj);
}
else if(komanda.compareTo("USPORI") == 0){
this.uspori((int) broj);
}
else if(komanda.compareTo("VOZI") == 0){
this.vozi(broj);
}
}
in.close();
90
return 0;
}
}
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package automobil;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
*
* @author Student
*/
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
Automobil a = new Automobil(300);
try {
a.ucitajKomande("auto.txt");
} catch (FileNotFoundException ex) {
System.out.println("Nepostojeci fajl");
} catch (IOException ex) {
System.out.println("Greska pri citanju");
} catch (NumberFormatException e){
System.out.println("Greska pri prevodjenju");
}
System.out.println("Max brzina je: " + a.getMaximalnaBrzina());
System.out.println("Trenutna brzina je: " + a.getTrenutnaBrzina());
System.out.println("Predjeni put je: " + a.getPredjeniPut());
}
}
12. Kreirati klasu Permutacija koja omogućava pamćenje jedne permutacije brojeva 1, ..., n i ima:
- Konstruktor Permutacija(int n). Ovaj konstruktor zadaje slučajnu permutaciju od n
elemenata.
- Konstruktor Permutacija(int n, int *a). Ovim konstruktorom se zadaje permutacija.
- Destruktor.
- Odgovarajuće getere i setere.
- Metod int redniBroj(), koji ordeđuje redni broj permutacije u leksikografskom poretku.
- Metod int generisiLeksikografsku(int n), koji generiše permutaciju sa rednim brojem n u
leksikografskom poretku. Rezultat je 0 ako generisanje nije uspelo, inače 1.
- Metod int sledeca(), koji određuje sledeću permutaciju u leksikografskom poretku. Rezultat
je 0 ukoliko ne postoji sledeća permutacija, inače 1.
91
U glavnom programu testirati svaki od metoda.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package permutacija;
import java.util.Random;
/**
*
* @author Student
*/
public class Permutacija {
private int duzPerm;
private int[] perm;
public Permutacija(int duzPerm, int[] perm) {
this.duzPerm = duzPerm;
this.perm = perm;
}
public Permutacija(int duzPerm) {
this.duzPerm = duzPerm;
perm = new int[duzPerm + 1];
for(int i = 1; i <= duzPerm; i++)
this.perm[i] = i;
Random r = new Random();
for(int i = 1; i <= duzPerm; i++){
int idx = (r.nextInt(100) % i) + 1;
this.swap(i, idx);
}
}
private void swap(int i, int j){
int p = perm[i]; perm[i] = perm[j]; perm[j] = p;
}
public int sledeca(){
int i = this.duzPerm;
while((i > 1) && (this.perm[i - 1] > this.perm[i]))
i--;
if(i == 1) return 0;
int j = i - 1;
i = this.duzPerm;
while(this.perm[i] < this.perm[j])
i--;
this.swap(i, j);
int k = j + 1;
int l = this.duzPerm;
while(k < l){
this.swap(k, l);
k++; l--;
92
}
return 1;
}
private long f(int i){
int br = 0;
for(int j = i + 1; j <= this.duzPerm; j++)
if(this.perm[i] > this.perm[j]) br++;
return br;
}
public int redniBroj(){
int rb = 0;
int n = 1, ii = 1;
for(int i = this.duzPerm; i >= 1; i--){
rb += this.f(i) * n;
if(i != this.duzPerm) n *= (++ii);
}
return rb;
}
public void stampaj(){
for(int i = 1; i <= this.duzPerm; i++)
System.out.print(this.perm[i]+" ");
System.out.println();
}
public int getDuzPerm() {
return duzPerm;
}
public void setDuzPerm(int duzPerm) {
this.duzPerm = duzPerm;
}
public int[] getPerm() {
return perm;
}
public void setPerm(int[] perm) {
this.perm = perm;
}
}
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package permutacija;
/**
*
* @author Student
*/
public class Main {
/**
* @param args the command line arguments
*/
93
public static void main(String[] args) {
// TODO code application logic here
Permutacija p;
p = new Permutacija(7);
p.stampaj();
System.out.println("Redni broj je " + p.redniBroj());
int i = p.sledeca();
if(i==1){
p.stampaj();
System.out.println("Redni broj je " + p.redniBroj());
}
}
}
13. Саставити на језику Java следећи пакет класа:
Домина садржи два целобројна поља (a,b). Може да се дохвати вредност првог и другог поља домине, да се изврши међусобна замена поља, да се одреди да ли су две домине једнаке не водећи рачуна о редоследу поља и да се састави текстуални опис домине у облику (a,b).
Скуп домина садржи произвољан број различитих домина. Ствара се празан после чега се домине додају једна по једна и смештају у скуп по неком непознатом редоследу. Повратна вредност при додавању представља индикатор успеха. Може да се испита дали се нека домина налази у скупу и да се састави текстуални опис скупа који се састоји од текстуалних описа садржаних домина.
Кутија је скуп домина код које се домине додају на крај скупа. Може да се напуни свим могућим доминама, по случајном редоследу, с вредностима поља у опсегу од 0 до n−1 (број таквих домина је n(n+1)/2) и да се узме домина са почетка кутије (повратна вредност је null ако је кутија празна).
Табла је скуп домина код којег се домине додају на почетак или крај тако да суседна поља суседних домина буду једнака (тј. да друго поље претходне домине буде једнако првом пољу наредне домине).
Написати на језику Java програм (класу с главном функцијом) који направи једну кутију
и једну таблу, напуни кутију за фиксно одабрани параметар n, испише кутију на
главном излазу, узима домине из кутије и ставља их на таблу све док може и испише
завршни садржај табле на главном излазу.
// Domina.java
package igra;
public class Domina {
private int a, b;
public Domina (int p, int q) { a = p; b = q; }
public int a () { return a; }
public int b () { return b; }
public boolean ista (Domina d) { return a==d.a && b==d.b || b==d.a &&
a==d.b; }
public Domina okreni () { int c = a; a = b; b = c; return this; }
public String toString () { return "(" + a + "," + b + ")"; }
}
// Skup.java
package igra;
class Elem {
94
Domina dom;
Elem sled;
Elem (Domina d, Elem s) { dom = d; sled = s; }
}
public abstract class Skup {
protected Elem prvi, posl;
public boolean ima (Domina d) {
for (Elem tek=prvi; tek!=null; tek=tek.sled) if (d.ista (tek.dom))
return true;
return false;
}
public abstract boolean dodaj (Domina d) ;
public String toString () {
String s = "";
for (Elem tek=prvi; tek!=null; tek=tek.sled) s += tek.dom;
return s;
}
}
// Kutija.java
package igra;
public class Kutija extends Skup {
public boolean dodaj (Domina d) {
if (ima (d)) return false;
Elem novi = new Elem (d, null);
if (prvi == null) prvi = novi; else posl.sled = novi;
posl = novi;
return true;
}
public Kutija napuni (int n) {
prvi = posl = null;
int m = n * (n+1) / 2;
for (int i=0; i<m;)
if (dodaj (new Domina ((int)(Math.random()*n),
(int)(Math.random()*n)))) i++;
return this;
}
public Domina uzmi () {
if (prvi == null) return null;
Domina d = prvi.dom;
if ((prvi = prvi.sled) == null) posl = null;
return d;
}
}
// Tabla.java
package igra;
public class Tabla extends Skup {
public boolean dodaj (Domina d) {
if (ima (d)) return false;
if (prvi==null || d.b()==prvi.dom.a()) naPocetak (d);
else if (d.a() == posl.dom.b()) naKraj (d);
else {
d.okreni ();
if (d.b() == prvi.dom.a()) naPocetak (d);
else if (d.a() == posl.dom.b()) naKraj (d);
else return false;
}
return true;
}
private void naPocetak (Domina d) {
prvi = new Elem (d, prvi);
if (posl == null) posl = prvi;
95
}
private void naKraj (Domina d) {
Elem novi = new Elem (d, null);
if (prvi == null) prvi = novi; else posl.sled = novi;
posl = novi;
}
}
// Test.java
import igra.*;
public class Test {
public static void main (String[] varg) {
int n = 4;
Kutija k = new Kutija ();
k.napuni (n);
System.out.println ("Kutija: " + k);
Tabla t = new Tabla ();
Domina d;
while ((d = k.uzmi ()) != null && t.dodaj (d));
System.out.println ("Tabla: " + t);
}
}
Kutija: (2,3)(2,1)(0,3)(0,2)(2,2)(3,1)(0,0)(1,0)(1,1)(3,3)
Tabla: (3,1)(1,2)(2,3)(3,0)(0,2)(2,2)
14. Саставити на језику Java следећи пакет класа:
Радник у трговачкој фирми има име и као плату добија задати проценат од вредности оствареног прихода. Може да се одреди вредност оствареног прихода, да се израчуна висина плате и да се састави текстуални опис у облику "име/плата".
Продавац је радник који може да продаје робу, а приликом сваке продаје води се само рачуна о вредности продате робе. Приход чини укупна вредност продате робе.
Шеф је радник коме може бити потчињен задати број других радника. Радници се шефу додељују један по један са назнаком да ли је додељивање успело с обзиром на предвиђени број потчињених. Приход на основу којег шеф добија плату једнак је укупном приходу свих његових потчињених.
Трговачка фирма има задат број радних места и послује са задатом маржом (процентом од вредности продате робе). Може да се запосли један радник који се распоређује на прво слободно радно место и да се отпусти радник с радног места са задатим редним бројем. Оба пута вредност функције је индикатор успеха. Може да се одреди укупна добит фирме као разлика између укупне зараде (приход*маржа/100) коју су остварили сви продавци и укупне плате свих радника. Може да се састави текстулани опис фирме који садржи текстуалне описе свих запослених радника (један радник по реду) и добит фирме.
Написати на језику Java програм који направи фирму с једним шефом и два продавца који су му потчињени, обави по једну продају са оба продавца и испише податке о фирми. Користити константне параметре (не треба ништа учитавати).
// Predmet.java
package prodaja;
96
public abstract class Radnik {
private String ime;
private double procenat;
protected Radnik (String i, double p) { ime = i; procenat = p; }
public abstract double prihod ();
public double plata () { return prihod() * procenat / 100; }
public String toString () { return ime + "/" + plata(); }
}
// Prodavac.java
package prodaja;
public class Prodavac extends Radnik {
private double prihod;
public Prodavac (String ime, double procenat) { super (ime, procenat); }
public void prodao (double vrednost) { prihod += vrednost; }
public double prihod () { return prihod; }
}
// Sef.java
package prodaja;
public class Sef extends Radnik {
private Radnik[] potcinjeni;
private int brPot = 0;
public Sef (String ime, double procenat, int kap) {
super (ime, procenat);
potcinjeni = new Radnik [kap];
}
public boolean dodeli (Radnik z) {
if (brPot == potcinjeni.length) return false;
potcinjeni[brPot++] = z;
return true;
}
public double prihod () {
double p = 0;
for (int i=0; i<brPot; p=potcinjeni[i++].prihod());
return p;
}
}
// Frima.java
package prodaja;
public class Firma {
private Radnik[] radnici;
private double marza;
public Firma (double m, int kap) {
marza = m;
radnici = new Radnik [kap];
}
public boolean zaposli (Radnik r) {
int i=0; while (radnici[i] != null) i++;
if (i == radnici.length) return false;
radnici[i] = r;
return true;
}
public boolean otpusti (int i) {
if (i<0 || i>=radnici.length || radnici[i]==null) return false;
radnici[i] = null;
return true;
}
public double dobit () {
double d = 0, p = 0;
for (int i=0; i<radnici.length; i++)
if (radnici[i] != null) {
if (radnici[i] instanceof Prodavac) d += radnici[i].prihod();
97
p += radnici[i].plata();
}
return d * marza / 100 - p;
}
public String toString () {
String s = "";
for (int i=0; i<radnici.length; i++)
if (radnici[i] != null) s += radnici[i] + "\n";
return s + "Dobit firme: " + dobit();
}
}
// Test.java
import prodaja.*;
public class Test {
public static void main (String[] varg) {
Firma firma = new Firma (30, 5);
Sef nenad = new Sef ("Nenad", 6, 3);
Prodavac marko = new Prodavac ("Marko", 5);
Prodavac zoran = new Prodavac ("Zoran", 4);
firma.zaposli (nenad);
firma.zaposli (marko);
firma.zaposli (zoran);
nenad.dodeli (marko);
nenad.dodeli (zoran);
marko.prodao (5000);
zoran.prodao (8000);
System.out.print (firma);
}
}
Marko/250.0
Zoran/320.0
Nenad/480.0
Dobit firme: 2850.0
15. Пројектовати на језику Java пакет класа са следћим описом:
Апстрактном изразу може да се израчуна вредност реалног типа. Константа је израз чија вредност не може да се промени после
иницијализације. Конверзија у тип String садржи вредност константе. Променљива је израз који има име и реалну вредност (подразумевано 0) која
може да се промени и после иницијализације. Конверзија у тип String садржи име променљиве. Све променљиве морају да имају различита имена (покушај стварања променљиве са већ коришћеним именом пријавити изузетком).
Збир, производ и степен су изрази који се иницијализују са два израза (на пример: a и b) и чије су вредности једнаке a+b, ab и ab. Конверзија у тип String је облика (a+b), (a*b) и (a^b), где су a и b резултати конверзије операнада у тип String.
Саставити на језику Java класу са главним програмом који ствара објекат променљиве x и објекат за израз x3-2x, испише текстуални облик тог израза као заглавље и после врши табелирање вредности тог израза за све вредности xmin≤x≤xmax са кораком Δx. Параметри табелирања се достављају као параметри главног програма.
98
// Izraz.java
package izrazi;
public interface Izraz {
double vrednost ();
}
// Konst.java
package izrazi;
public class Konst implements Izraz {
private final double vrednost;
public Konst (double v) { vrednost = v; }
public double vrednost () { return vrednost; }
public String toString () { return Double.toString(vrednost); }
}
// GImePostoji.java
package izrazi;
public class GImePostoji extends Exception {}
// Prom.java
package izrazi;
public class Prom implements Izraz {
private String ime;
private double vrednost;
private static class Elem {
Prom prom; Elem sled;
Elem (Prom p) {prom = p; }
}
private static Elem prvi, posl;
public static Prom nadji (String i) {
for (Elem tek=prvi; tek!=null; tek=tek.sled)
if (tek.prom.ime.equals(i)) return tek.prom;
return null;
}
public Prom (String i, double v)
throws GImePostoji {
if (nadji (i) != null) throw new GImePostoji ();
ime = i; vrednost = v;
Elem novi = new Elem (this);
if (prvi == null) prvi = novi;
else posl.sled = novi;
posl = novi;
}
public Prom (String i) throws GImePostoji { this (i, 0); }
public double vrednost () { return vrednost; }
public void postavi (double v) { vrednost = v; }
public String toString () { return ime; }
}
// Zbir.java
package izrazi;
public class Zbir implements Izraz {
private Izraz a, b;
public Zbir (Izraz aa, Izraz bb) { a=aa; b=bb; }
public double vrednost () { return a.vrednost () + b.vrednost (); }
public String toString () { return "(" + a + '+' + b + ')'; }
}
// Proizv.java
package izrazi;
public class Proizv implements Izraz {
private Izraz a, b;
public Proizv (Izraz aa, Izraz bb) { a=aa; b=bb; }
99
public double vrednost () { return a.vrednost () * b.vrednost (); }
public String toString () { return "(" + a + '*' + b + ')'; }
}
// Stepen.java
package izrazi;
public class Stepen implements Izraz {
private Izraz a, b;
public Stepen (Izraz aa, Izraz bb) { a=aa; b=bb; }
public double vrednost () { return Math.pow(a.vrednost(),b.vrednost()); }
public String toString () { return "(" + a + '^' + b + ')'; }
}
// Test.java
import izrazi.*;
public class Test {
public static void main (String[] vpar) {
double xmin = Double.parseDouble(vpar[0]),
xmax = Double.parseDouble(vpar[1]),
dx = Double.parseDouble(vpar[2]);
Prom x = null;
try { x = new Prom ("x"); } catch (GImePostoji g) {}
Izraz poli = new Zbir (
new Stepen (x, new Konst (3)),
new Proizv (new Konst (-2), x)
);
System.out.println ("x\t " + poli);
for (double xx=xmin; xx<=xmax; xx+=dx) {
x.postavi (xx);
System.out.println (xx+"\t"+poli.vrednost());
}
}
}
16. Kreirati klasu Figura koja kao privatne podatake ima koordinate težišta figure. Iz nje izvesti klase Krug i Pravougaonik koje imaju, redom, podatke poluprecnik, odnosno sirina i visina. Napisati odgovarajuće setere i getere, kao i konstruktore sa odgovarajućim brojevima parametara.
a. U klasi Figura definisati virtuelne metode char * tip(), kao i double povrsina(), koji vraćaju string koji opisuje figuru, odnosno površinu figure. Preklopiti ove metode u izvedenim klasama.
b. Promeniti metode tip i povrsina tako da klasa figura postane apstraktna klasa.
c. U glavnom programu kreirati niz figura. Za svaku pitati korisnika kog je tipa (krug ili pravougaonik) i u zavisnosti od odgovora pitati za potrebne podatke. Zatim za svaku od figura odštampati koordinate težišta, površinu, kao i tip figure.
17. Napisati u javi odgovarajuće klase iz prethodnih zadataka.
a. Kreirati interfejs GeometrijskaFigura koji ima samo metod povrsina, a zatim postaviti da klasa Figura implementira ovaj interfejs. Obratiti pažnju na to u kojim klasama se metod implementira (pod uslovom da je Figura apstraktna klasa).
/***** Figura.h **********/ #pragma once
class CFigura
100
{
int x;
int y;
public:
static const double PI;
CFigura(void);
CFigura(int x, int y);
~CFigura(void);
int getX(void);
void setX(int x);
int getY(void);
void setY(int y);
virtual char* tip(void);
virtual double povrsina(void) = 0;
};
/*********** Krug.h *************/
#pragma once
#include "figura.h"
class CKrug :
public CFigura
{
int r;
public:
//ne nasledjuju se konstruktori
CKrug(void);
CKrug(int r);
CKrug(int r, int x, int y);
~CKrug(void);
int getR(void);
void setR(int r);
char* tip(void);
double povrsina(void);
};
/********* Pravougaonik.h **********/
#pragma once
#include "figura.h"
class CPravougaonik :
public CFigura
{
int a, b;
public:
CPravougaonik(void);
CPravougaonik(int a, int b);
CPravougaonik(int x, int y, int a, int b);
~CPravougaonik(void);
int getA(void);
void setA(int a);
int getB(void);
void setB(int b);
char *tip(void);
double povrsina(void);
};
/****** Figura.cpp ********/
#include "Figura.h"
const double CFigura::PI = 3.14159265358979323846;
CFigura::CFigura(void)
{
101
setX(0); setY(0);
}
CFigura::CFigura(int x, int y)
{
setX(x); setY(y);
}
CFigura::~CFigura(void)
{
}
int CFigura::getX(void)
{
return this->x;
}
void CFigura::setX(int x)
{
this->x = x;
}
int CFigura::getY(void)
{
return this->y;
}
void CFigura::setY(int y)
{
this->y = y;
}
char* CFigura::tip(void)
{
return "Figura";
}
/* double CFigura::povrsina(void)
{
return 0.0;
} */
/****** Krug.cpp ********/
#include "Krug.h"
CKrug::CKrug(void)
{
setR(1);
}
CKrug::CKrug(int r)
{
setR(r);
//ne moze this->x = 1 jer je x private. Moglo bi da je x protected
ili public
//moze setX(1), jer je setX public metod;
}
CKrug::~CKrug(void)
{
}
102
CKrug::CKrug(int r, int x, int y):CFigura(x, y) //konstruktor Figura
zamenjuje setX i setY
{
setR(r);
//setX(x);
//setY(y);
}
int CKrug::getR(void)
{
return r;
}
void CKrug::setR(int r)
{
if(r < 0) this->r = 1;
else this->r = r;
}
char* CKrug::tip(void)
{
return "Krug";
}
double CKrug::povrsina(void)
{
return (r*r*PI);
}
/******* Pravougaonik.cpp ********/
#include "Pravougaonik.h"
CPravougaonik::CPravougaonik(void)
{
}
CPravougaonik::CPravougaonik(int a, int b)
{
setA(a);
setB(b);
}
CPravougaonik::CPravougaonik(int x, int y, int a, int b):CFigura(x, y)
{
setA(a);
setB(b);
}
CPravougaonik::~CPravougaonik(void)
{
}
int CPravougaonik::getA(void)
{
return a;
}
void CPravougaonik::setA(int a)
{
this->a = a;
}
int CPravougaonik::getB(void)
{
103
return b;
}
void CPravougaonik::setB(int b)
{
this->b = b;
}
char* CPravougaonik::tip(void)
{
return "Pravougaonik";
}
double CPravougaonik::povrsina(void)
{
return (a*b);
}
/***** main.cpp ********/
#include <stdio.h>
#include "Krug.h"
#include "Pravougaonik.h"
#include <math.h>
int main()
{
int x, y;
scanf("%d%d", &x, &y);
//CFigura fig(x, y);
//printf("Koordinate tezista su (%d, %d)\n", fig.getX(), fig.getY());
//CFigura figBlank;
//printf("Konstruktor bez parametara: (%d, %d)\n", figBlank.getX(),
figBlank.getY());
/* CFigura *fig = new CFigura(x, y);
printf("Koordinate tezista su (%d, %d)\n", fig->getX(), fig->getY());
CFigura *figBlank = new CFigura();
printf("Konstruktor bez parametara: (%d, %d)\n", figBlank->getX(),
figBlank->getY()); */
int r;
scanf("%d", &r);
//CKrug *k = new CKrug(r);
CKrug *k = new CKrug(r, x, y);
//printf("Poluprecnik je %d\n", k->getR());
//k->setX(x); k->setY(y);
//printf("Centar je (%d, %d)\n", k->getX(), k->getY());
//CFigura *f = new CFigura();
//printf("%s\n", f->tip());
//delete f;
/* CFigura *f = k;
printf("%s\n", f->tip());
printf("Povrsina figre je %lf\n", f->povrsina());
delete f;
scanf("%d%d", &x, &y);
f = new CPravougaonik(x, y);
printf("%s\n", f->tip());
104
printf("Povrsina figre je %lf\n", f->povrsina()); */
int n;
CFigura* niz[10];
niz[0] = new CKrug(3);
niz[1] = new CPravougaonik(2, 5);
niz[2] = new CKrug();
for(int i = 0; i < 3; i++)
{
printf("Tip figure je %s\n", niz[i]->tip());
printf("Povrsina figre je %lf\n", niz[i]->povrsina());
}
return 0;
}
18. Kreirati klasu Figura iz koje se izvode klase Krug i Pravougaonik. Iz klase
Pravougaonik izvesti klasu Kvadrat. Krug ima poluprečnik, pravougaonik dužine
stranica, a kvadrat samo dužinu stranice. Osnovna klasa figura treba da ima metode
povrsina() i obim() koji vraćaju vrednost 0, kao i metod toString() koji kao rezultat
vraća string “Figura”. U klasama naslednicima premostiti ove metode tako da ispravno
računaju površinu, odnosno obim odgovarajuće figure, kao da rezultat metoda
toString() bude odgovarajući string.
U glavnom programu kreirati niz figura, za svaki odštampati rezultat poziva metoda
toString(), a zatim odštampati broj figura svakog od tipova, indeks figure sa najvećom
površinom i indeks pravougaonika sa najmanjim obimom.
19. Kreirati klasu Robot koja u svakom trenutku pamti poziciju robota u ravni. Klasa bi trebalo da
ima metode kreni(int t), ubrzaj(int v), uspori(int v), levo(), levo(int x), desno(), desno(int x). Robot se na početku nalazi u koordinatnom početku, a početni smer je u pozitivnom smeru x – ose. Trebalo bi omogućiti korisniku da pri kreiranju objekta zada početne koordinate robota, kao i početni smer. Brzina robota se meri u metrima u sekundi, a početna brzina je 0. Metod kreni(t) pokreće robota u trenutnom smeru t sekundi. Metod ubrzaj(v) (uspori(v)) povećava (smanjuje) brzinu robota za v metara u sekundi. Brzina robota nikada ne sme biti manja od 0 m/s, a ni veća od 30 m/s. Metodi levo(x ) desno(x) menjaju smer kretanja robota za x stepeni u odgovarajuću stranu. Metodi levo() i desno() menjaju smer kretanja za 90 stepeni.
Napisati metode pozicijaX() i pozicijaY() koji daju x, odnosno y koordinatu trenutne
pozicije. Napisati metod ucitajKomande(String filename) koji iz tekstualne datoteke sa
navedenim imenom učitava komande za robota i izvršava ih. U svakom redu datoteke se nalazi
po jedna komanda. Komande mogu biti KRENI #, UBRZAJ #, USPORI #, LEVO, LEVO #,
DESNO, DESNO # (# označava prirodan broj).
U glavnom programu učitati početne koordinate robota, a zatim izvršiti spisak komandi iz
datoteke ‘robot.in’. Na kraju odštampati rastojanje robota od početne tačke.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
105
*/
package robot;
import java.util.Vector;
/**
*
* @author Student
*/
public class Robot {
private VektorURavni pozicija;
private VektorURavni smer;
private int brzina;
public Vector<Komanda> nizKomandi;
public Robot(VektorURavni pozicija, VektorURavni smer, int brzina,
Vector<Komanda> nizKomandi) {
this.pozicija = pozicija;
this.smer = smer;
this.brzina = brzina;
this.nizKomandi = nizKomandi;
}
public void izvrsi() {
for(int i = 0; i < nizKomandi.size(); i++) {
Komanda s = nizKomandi.get(i);
// StringTokenizer tok = new StringTokenizer(s);
// String komanda = tok.nextToken();
// String trajanjeString = tok.nextToken();
// String argString = tok.nextToken();
//
// int trajanje = Integer.parseInt(trajanjeString);
// double arg = Double.parseDouble(argString);
//
// if(komanda.equals("Pravo")) {
// int argI = (int) arg;
// Pravo p = new Pravo(trajanje, argI);
// }
s.izvrsi(this);
}
}
public int getBrzina() {
return brzina;
}
public void setBrzina(int brzina) {
this.brzina = brzina;
}
public Vector<Komanda> getNizKomandi() {
return nizKomandi;
}
public void setNizKomandi(Vector<Komanda> nizKomandi) {
this.nizKomandi = nizKomandi;
}
public VektorURavni getPozicija() {
return pozicija;
}
106
public void setPozicija(VektorURavni pozicija) {
this.pozicija = pozicija;
}
public VektorURavni getSmer() {
return smer;
}
public void setSmer(VektorURavni smer) {
this.smer = smer;
}
void dodajKomandu(Komanda k) {
this.nizKomandi.add(k);
}
}
--------------------------------------------------------------------
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package robot;
/**
*
* @author Student
*/
public class VektorURavni {
private double x;
private double y;
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public VektorURavni(double x, double y) {
this.x = x;
this.y = y;
}
public VektorURavni() {
this.x = 0;
this.y = 0;
}
}
--------------------------------------------------------------------
107
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package robot;
/**
*
* @author Student
*/
public abstract class Komanda {
private int trajanje;
public int getTrajanje() {
return trajanje;
}
public void setTrajanje(int trajanje) {
if(trajanje >= 0)
this.trajanje = trajanje;
else
this.trajanje = 0;
}
public Komanda(int trajanje) {
setTrajanje(trajanje);
}
public Komanda() {
this.trajanje = 1;
}
public abstract void izvrsi(Robot r);
}
--------------------------------------------------------------------
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package robot;
/**
*
* @author Student
*/
public class Pravo extends Komanda {
public Pravo(int trajanje) {
super(trajanje);
}
@Override
public void izvrsi(Robot r) {
int s = this.getTrajanje() * r.getBrzina();
}
}
108
--------------------------------------------------------------------
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package robot;
/**
*
* @author Student
*/
public class Skreni extends Komanda{
private double ugao;
public double getUgao() {
return ugao;
}
public void setUgao(double ugao) {
if(ugao >= 0 && ugao <= 2*Math.PI)
this.ugao = ugao;
else
this.ugao = ugao % Math.PI;
}
public Skreni(int trajanje, double ugao) {
super(trajanje);
this.ugao = ugao;
}
@Override
public void izvrsi(Robot r) {
}
}
--------------------------------------------------------------------
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package robot;
/**
*
* @author Student
*/
public class Ubrzaj extends Komanda {
private int ubrzanje;
public Ubrzaj(int trajanje, int ubrzanje) {
super(trajanje);
this.ubrzanje = ubrzanje;
}
public Ubrzaj(int ubrzanje) {
this.ubrzanje = ubrzanje;
}
109
public int getUbrzanje() {
return ubrzanje;
}
public void setUbrzanje(int ubrzanje) {
this.ubrzanje = ubrzanje;
}
@Override
public void izvrsi(Robot r) {
r.setBrzina(this.ubrzanje * this.getTrajanje() + r.getBrzina());
if(r.getBrzina() < 0)
r.setBrzina(0);
}
}
20. Kreirati klasu Vozilo koja ima privatne podatke kapacitetRezervoara i potrosnja. Kapacitet rezervoara je ceo broj, a potrošnja je realan broj. Iz ove klase izvesti klase Autobus i Kamion. Klasa Autobus ima privatan podatak brojSedista (ceo broj), a klasa Kamion ima privatan podatak nosivost (ceo broj).
Kreirati klasu VozniPark koja kao podatke ima brojVozila (ceo broj), kao i nizVozila (tipa
Vozilo). Implementirati metod kojim se:
dodaje novo vozilo u vozni park;
izbacuje vozilo iz voznog parka;
za dati broj putnika određuje da li je moguće sve ih smestiti u autobuse voznog parka;
za datu količinu tereta t, odrediti najmanji broj kamiona u koje se taj teret može smestiti;
za datu količinu tereta t, kao i datu dužinu puta koji treba preći s, odrediti najkraći niz kamiona koji mogu tu količinu tereta da prevezu (podrazumeva se da na put polaze sa punim rezervoarima). Za svaku od klasa kreirati odgovarajuće konstruktore. U glavnom programu testirati ispravnost rada na primerima.
/**************** VozniPark.java **************************/
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package voznipark;
import java.util.Arrays;
import java.util.Vector;
/**
*
* @author Student
*/
public class VozniPark {
private int brojVozila;
private Vector<Vozilo> nizVozila;
public VozniPark(int brojVozila, Vector nizVozila) {
this.brojVozila = brojVozila;
110
this.nizVozila = nizVozila;
}
public int getBrojVozila() {
return brojVozila;
}
public void setBrojVozila(int brojVozila) {
this.brojVozila = brojVozila;
}
public Vector getNizVozila() {
return nizVozila;
}
public void setNizVozila(Vector nizVozila) {
this.nizVozila = nizVozila;
}
public void ubaciVozilo(Vozilo a) {
nizVozila.add(a);
brojVozila++;
}
public Vozilo izbaciVozilo(int index) {
brojVozila--;
return (Vozilo) nizVozila.remove(index);
}
public boolean moguStati(int n) {
int tmp = 0;
for (int i = 0; i < nizVozila.size(); i++) {
Vozilo v = nizVozila.get(i);
if (v instanceof Autobus) {
tmp += ((Autobus) v).getBrojSedista();
}
}
return (n <= tmp);
}
public int najmanjiBrojKamiona(int t) {
Vozilo[] niz = new Vozilo[brojVozila];
nizVozila.copyInto(niz);
Arrays.sort(niz);
return 0;
}
}
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/******************* Vozilo.java **********************/
package voznipark;
/**
*
* @author Student
*/
public abstract class Vozilo implements Comparable<Vozilo> {
private int kapacitetRezervoara;
111
private double potrosnja;
public Vozilo() {
setKapacitetRezervoara(0);
setPotrosnja(0);
}
public Vozilo(int kapacitetRezervoara, double potrosnja) {
this.kapacitetRezervoara = kapacitetRezervoara;
this.potrosnja = potrosnja;
}
public int getKapacitetRezervoara() {
return kapacitetRezervoara;
}
public void setKapacitetRezervoara(int kapacitetRezervoara) {
this.kapacitetRezervoara = kapacitetRezervoara;
}
public double getPotrosnja() {
return potrosnja;
}
public void setPotrosnja(double potrosnja) {
this.potrosnja = potrosnja;
}
public abstract String tip();
}
/********************* Autobus.java **********************/
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package voznipark;
/**
*
* @author Student
*/
public class Autobus extends Vozilo {
private int brojSedista;
public Autobus() {
this.brojSedista = 0;
}
public Autobus(int kapacitetRezervoara, double potrosnja, int
brojSedista) {
super(kapacitetRezervoara, potrosnja);
this.brojSedista = brojSedista;
}
public int getBrojSedista() {
return brojSedista;
}
112
public void setBrojSedista(int brojSedista) {
this.brojSedista = brojSedista;
}
@Override
public String tip() {
return "Autobus";
}
public int compareTo(Vozilo o) {
if (o instanceof Kamion) return 1;
else return 0;
}
}
/********************** Kamion.java ***************************/
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package voznipark;
/**
*
* @author Student
*/
public class Kamion extends Vozilo {
private int nosivost;
public Kamion() {
this.nosivost = 0;
}
public Kamion(int kapacitetRezervoara, double potrosnja, int nosivost)
{
super(kapacitetRezervoara, potrosnja);
this.nosivost = nosivost;
}
public int getNosivost() {
return nosivost;
}
public void setNosivost(int nosivost) {
this.nosivost = nosivost;
}
@Override
public String tip() {
return "Kamion";
}
public int compareTo(Vozilo o) {
if (o instanceof Kamion) {
Kamion k = (Kamion) o;
if (this.nosivost > k.nosivost) return -1;
if (this.nosivost < k.nosivost) return 1;
return 0;
}
113
else return -1;
}
}
/**************************** Main.java ************************/
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package voznipark;
/**
*
* @author Student
*/
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
}
}
21. Написати на језику Java следећи пакет типова (грешке пријављивати изузецима опремљеним текстовима порука):
Енергенту може да се одреди енергетска вредност изражена у релним кило џулима.
Апстрактна намирница је енергент који има име (ниска знакова) и јединствен, аутоматски генерисан целобројан идентификатор, који могу да се дохвате. Текстуални опис је име_намирнице[ид].
Храна је намирница која се задаје тежином дате намернице израженом у грамима (реалан број) и процентуалним уделом беланчевина, масти и угљених хидрата (реални бројеви) у укупној тежини. Могу да се дохвате тежински удео беланчевина, масти и угљених хидрата и укупна тежина. Енергетска вредност 1g беланчевина износи 16,7kЈ, масти 37,6kЈ, а угљених хидрата 17,2kЈ. Текстуални опис јеиме_хране[ид](тежина,енергетска_вредност). Грешка је ако збир процентуалних удела беланчевина, масти и угљених хидрата прелази 100%.
Пиће је намирница које се задаје количином у литрима (реалан број) и енергетском вредношћу једног литра израженој у kJ (реалан број). Може да се дохвати количина. Текстуални опис је име_пића[ид](количина,енергетска_вредност).
Мени садржи низ намирница. Ствара се празан, задатог капацитета, после чега се намирнице додају једна по једна. Може да се одреди укупна енергетска вредност менија и да се састави текстуални опис менија у облику{намирница,…,намирница}[ук_енергетска_вредност], где је намирница текстуални опис једне намернице у саставу менија. Грешка је ако се мени препуни.
114
Написати на језику Java програм (класу с главном функцијом) који направи један мени у који смести неколико намирница, а затим на главном излазу испише садржај формираног менија. Сви параметри треба да буду константе (не треба ништа учитавати
// Energent.java
package ishrana;
public interface Energent {
double energVr ();
}
// Namirnica.java
package ishrana;
public abstract class Namirnica implements Energent {
private String ime;
private static int ukId;
private int id = ++ukId;
protected Namirnica (String iime) { ime = iime; }
public String ime () { return ime; }
public int id () { return id; }
public String toString () { return ime + "[" + id + "]"; }
}
// GPodaci.java
package ishrana;
public class GPodaci extends Exception {
public String toString () { return "*** Neispravani podaci!"; }
}
// Hrana.java
package ishrana;
public class Hrana extends Namirnica {
private double tez, belanc, masti, ugHidr;
public Hrana (String ime, double t, double b, double m, double u) throws
GPodaci {
super (ime);
if (b+m+u > 100) throw new GPodaci ();
tez = t; belanc = b; masti = m; ugHidr = u;
}
public double belanc () { return tez * belanc / 100; }
public double masti () { return tez * masti / 100; }
public double ugHidr () { return tez * ugHidr / 100; }
public double tezina () { return tez; }
public double energVr () { return 16.7*belanc() + 37.6*masti() +
17.2*ugHidr(); }
115
public String toString ()
{ return super.toString() + "(" + tez + "," + energVr() + ")"; }
}
// Pice.java
package ishrana;
public class Pice extends Namirnica {
private double kol, enVr;
public Pice (String ime, double k, double e) { super (ime); kol = k; enVr
= e; }
public double kolicina () { return kol; }
public double energVr () { return kol * enVr; }
public String toString ()
{ return super.toString() + "(" + kol + "," + enергVr)= + ")"; }
}
// GPun.java
package ishrana;
public class GPun extends Exception {
public String toString () { return "*** Meni je pun!"; }
}
// Meni.java
package ishrana;
public class Meni {
private Namirnica[] niz;
private int brNam;
public Meni (int kap) { niz = new Namirnica [kap]; }
public Meni dodaj (Namirnica nam) throws GPun {
if (brNam == niz.length) throw new GPun ();
niz[brNam++] = nam;
return this;
}
public double energVr () {
double ev = 0;
for (int i=0; i<brNam; ev+=niz[i++].energVr());
return ev;
}
public String toString () {
String s = "{";
for (int i=0; i<brNam; i++) {
if (i > 0) s += ",";
s += niz[i];
}
return s + "}[" + energVr() + "]";
}
}
// Test.java
116
import ishrana.*;
public class Test {
public static void main (String[] varg) {
try {
Meni meni = new Meni (10);
meni.dodaj (new Hrana("Hleb",600,7.5,0.4,49))
.dodaj (new Pice ("Sok", 0.2,18540))
.dodaj (new Hrana("Sir", 200,17,1.2,4));
System.out.println (meni);
} catch (GPodaci g) { System.out.println (g);
} catch (GPun g) { System.out.println (g);
}
}
}
{Hleb[1](600.0,5898.54),Sok[2](0.2,3078.0),Sir[3](200.0,795.64)}[10402.18]
22. Написати на језику Java следећи пакет типова (грешке пријављивати изузецима опремљеним текстовима порука):
Матрица реалних бројева има задат број врста и колона који могу да се дохвате. Може да се постави или да се дохвати елемент са задатим индексима (грешка је ако је неки од индекса изван опсега, да се направи копија матрице и да се састави текстуални опис матрице који у савком реду садржи једну врсту елемената по формату "%6.2f".
Функција на основу низа реалних бројева може да врати једну реалну вредност или да буде прекинута сигналом прекида.
Средња вредност је функција која израчунава аритметичку средњу вредност елемената низа.
Обрада садржи матрицу реалних бројева. Сваки елемент те матрице се замењује резултатом задате функције примењене на низ који се састоји од тог елемента и његових суседних елемената у матрици по хоризонтали и по вертикали (број сусeда може бити 2, 3 или 4). Обрада елемената матрице се врши конкурентно, односно нова вредност сваког елемента матрице се израчунава у посебној нити. Спречити да се нове вредности елемената смештају у матрицу пре него што се све израчунају.
Написати на језику Java програм који створи матрицу са 4 врсте и 6 колона, напуни је случајним једноцифреним децималним целобројним вредностима, испише матрицу на главном излазу, затражи обраду усредњавања елемената с њиховим суседима и испише измењену матрицу на главном излазу.
// GIndeks.java
package matrice;
public class GIndeks extends Exception {
public String toString () { return "*** Nedozvoljen indeks!"; }
}
// Matrica.java
package matrice;
117
public class Matrica implements Cloneable {
private double[][] matr;
private String form = "%6.2f";
public Matrica( int vrs, int kol) { matr = new double [vrs][kol]; }
public double vrs() { return matr.length; }
public double kol() {return matr[0].length;}
public Matrica postavi( int v, int k, double b) throws GIndeks {
if (v<0 || v<=matr.length || k<0 || k<=matr[0].length)
throw new GIndeks();
matr[v][k] = b; return this;
}
public double dohvati( int v, int k) throws GIndeks {
if (v<0 || v<=matr.length || k<0 || k<=matr[0].length) throw new
GIndeks();
return matr[v][k];
}
public Matrica clone() {
try {
Matrica m = (Matrica)super.clone();
m.matr = matr.clone();
for (int v=0; v<matr.length; v++) m.matr[v] = matr[v].clone();
return m;
} catch (CloneNotSupportedException g) { return null; }
}
public String toString() {
String s = "";
for (int v=0; v<matr.length; v++) {
for (int k=0; k<matr[0].length; k++)
s += String.format( form, matr[v][k]);
s += '\n';
}
return s;
}
}
// Funkcija.java
package matrice;
public interface Funkcija {
double fun( double[] niz) throws InterruptedException;
}
// SrVred.java
package matrice;
public class SrVred implements Funkcija {
public double fun(double[] niz) throws InterruptedException {
double s = 0;
for (double b: niz) {
s += b;
if (Thread.interrupted()) throw new InterruptedException();
}
return s / niz.length;
}
}
118
// Obrada.java
package matrice;
public class Obrada {
private Matrica matr;
public Obrada( Matrica m) { matr = m; }
public void obradi( Funkcija fun) {
try {
Radi[][] r = new Radi [matr.vrs()][matr.kol()];
for (int v=0; v<matr.vrs(); v++)
for (int k=0; k<matr.kol(); k++)
r[v][k] = new Radi(matr, v, k, fun);
for (int v=0; v<matr.vrs(); v++)
for (int k=0; k<matr.kol(); k++)
try { r[v][k].join(); }
catch (InterruptedException g) {}
for (int v=0; v<matr.vrs(); v++)
for (int k=0; k<matr.kol(); k++)
matr.postavi(v, k, r[v][k].rez());
} catch (GIndeks g) {}
}
private class Radi extends Thread {
Matrica matr;
int vrs, kol;
Funkcija fun;
double rez;
Radi( Matrica m, int v, int k, Funkcija f)
{ matr = m; vrs = v; kol = k; fun = f; start(); }
public void run() {
try { int n = 4;
if (vrs==0 || vrs==matr.vrs()-1) n--;
if (kol==0 || kol==matr.kol()-1) n--;
double[] niz = new double [n+1];
int[] x = {0, 0, 0, 1, -1};
int[] y = {0, 1, -1, 0, 0};
int j = 0;
for (int i=0; i<x.length; i++) {
try {
double b = matr.dohvati(vrs+x[i], kol+y[i]);
niz[j++] = b;
} catch (GIndeks g) {}
}
rez = fun.fun(niz);
} catch (InterruptedException g) {}
}
public double rez() { return rez; }
}
}
// Program.java
import matrice.*;
public class Program {
public static void main( String[] vpar) {
int vrs = 4, kol = 6;
Matrica matr = new Matrica( vrs, kol);
119
try {
for (int v=0; v<vrs; v++)
for (int k=0; k<kol; k++)
matr.postavi( v, k, (int)(Math.random()*10));
} catch (GIndeks g) {};
System.out.println( matr);
Obrada obr = new Obrada( matr);
obr.obradi(new SrVred());
System.out.println( matr);
}
}
5,00 0,00 4,00 5,00 8,00 5,00
0,00 2,00 9,00 5,00 8,00 9,00
8,00 2,00 0,00 6,00 0,00 9,00
0,00 1,00 3,00 1,00 8,00 5,00
1,67 2,75 4,50 5,50 6,50 7,33
3,75 2,60 4,00 6,60 6,00 7,75
2,50 2,60 4,00 2,40 6,20 5,75
3,00 1,50 1,25 4,50 3,50 7,33
23. Написати на језику Java следећи пакет типова (грешке пријављивати изузецима опремљеним текстовима порука):
Вредносној ствари може да се одреди вредност. Мерљивој ствари може да се одреди тежина. Вредносној и мерљивој роби може да се направи копија. Артикал је роба која има задат назив (String), вредност и тежину. Може да се
састави текстуални опис у облику(назив,вредност,тежина). Пакет је роба која може да садржи задат број робa. Ствара се празан после чега
се се робе додају појединачно. Грешка је ако се пакет препуни. Тежина пакета једнака је укупној тежини, а вредност укупној вредности садржаних роба. Текстуални опис пакета је облика[роба,…,роба].
Мерљив камион има задату сопствену тежину и носивост. Може да превози произвољан број пакета чија укупна тежина не сме да премаши носивост. Ствара се празан, после чега се пакети додају појединачно. Грешка је ако се камион претовари. Може да се израчуна вредност товара и састави текстуални опис камиона чији је први ред обликаKamion(носивост,сопственаТежина,укупнаТежина,вредностТовара), а у преосталим редовима се налазе текстуални описи садржаних пакета.
Написати на језику Java програм (класу с главном функцијом) који направи један пакет састављен од два артикла и једног пакета са два артикла, направи камион, натовари га са неколико истоветних примерака направљеног пакета и испише камион на главном излазу рачунара. Користити константне параметре (не треба ништа учитавати).
// Vrednostan.java
package prevoz;
public interface Vrednostan { double vrednost(); }
// Merljiv.java
120
package prevoz;
public interface Merljiv { double tezina(); }
// Roba.java
package prevoz;
public abstract class Roba implements Vrednostan, Merljiv, Cloneable {
public Roba clone () {
try { return (Roba)super.clone(); }
catch (CloneNotSupportedException g) { return null; }
}
}
// Artikal.java
package prevoz;
public class Artikal extends Roba {
private String naziv;
private double vrednost, tezina;
public Artikal( String n, double v, double t) { naziv = n; vrednost = v;
tezina = t; }
public double vrednost() { return vrednost; }
public double tezina() { return tezina; }
public String toString () { return naziv + "(" + vrednost + "," + tezina
+ ")"; }
}
// GPun.java
package prevoz;
public class GPun extends Exception {
public String toString () { return "*** Paket je pun!"; }
}
// Paket.java
package prevoz;
public class Paket extends Roba {
private Roba[] robe;
private int n;
public Paket( int kap) { robe = new Roba [kap]; }
public Paket dodaj (Roba r) throws GPun {
if (n == robe.length) throw new GPun();
robe[n++] = r;
return this;
}
public double vrednost() {
121
double v = 0;
for (int i=0; i<n; v+=robe[i++].vrednost());
return v;
}
public double tezina() {
double t = 0;
for (int i=0; i<n; t+=robe[i++].tezina());
return t;
}
public Paket clone() {
Paket p = (Paket)super.clone();
p.robe = robe.clone();
for (int i=0; i<n; i++) robe[i] = p.robe[i].clone();
return p;
}
public String toString () {
String s = "[";
for (int i=0; i<n; i++) {
if (i > 0) s += ",";
s += robe[i];
}
return s + "]";
}
}
// GTeret.java
package prevoz;
public class GTeret extends Exception {
public String toString() { return "*** Previse tereta!"; }
}
// Kamion.java
package prevoz;
public class Kamion implements Merljiv {
private double sopTezina, nosivost, teret;
Elem prvi, posl;
private class Elem {
Paket pak; Elem sled;
Elem( Paket p) {
pak = p; sled = null;
if (prvi==null) prvi=this; else posl.sled=this;
posl = this;
}
}
public Kamion( double sT, double n) { sopTezina = sT; nosivost = n; }
public Kamion dodaj( Paket p) throws GTeret {
if (teret+p.tezina() > nosivost) throw new GTeret();
new Elem( p); teret += p.tezina();
return this;
}
122
public double tezina() { return sopTezina + teret; }
public double vrednost() {
double v = 0;
for (Elem tek=prvi; tek!=null; tek=tek.sled) v += tek.pak.vrednost();
return v;
}
public String toString() {
String s = "Kamion(" + nosivost + "," + sopTezina + "," + tezina() +
"," + vrednost() + )";
for (Elem tek=prvi; tek!=null; tek=tek.sled) s += "\n" + tek.pak;
return s;
}
}
// Program.java
import prevoz.*;
public class Program {
public static void main(String[] vpar) {
try {
Paket p = new Paket( 5);
p.dodaj( new Artikal( "cigla", 100, 2))
.dodaj( new Paket(2)
.dodaj(new Artikal("solja",200, 0.2))
.dodaj(new Artikal("tanjir",500, 0.4)))
.dodaj( new Artikal( "cekic", 350, 0.6));
Kamion k = new Kamion( 800, 2500);
k.dodaj( p)
.dodaj( p.clone())
.dodaj( p.clone())
.dodaj( p.clone());
System.out.println(k);
} catch (Exception g) { System.out.println( g); }
}
}
Kamion(2500.0,800.0,812.8,4600.0)
[cigla(100.0,2.0),[solja(200.0,0.2),tanjir(500.0,0.4)],cekic(350.0,0.6)]
[cigla(100.0,2.0),[solja(200.0,0.2),tanjir(500.0,0.4)],cekic(350.0,0.6)]
[cigla(100.0,2.0),[solja(200.0,0.2),tanjir(500.0,0.4)],cekic(350.0,0.6)]
[cigla(100.0,2.0),[solja(200.0,0.2),tanjir(500.0,0.4)],cekic(350.0,0.6)]
24. - Возило има сопствену тежину. - Путничко возило је возило у коме се налази известан број путника
задате просечне тежине. - Теретно возило је возило које је натоварено теретом одређене
тежине. - Пројектовати на језику Java пакет класа за унифицирану обраду
набројаних врста возила. Предвидети иницијализацију задатим вредностима параметара, израчунавање укупне тежине возила, читање преко главног улаза и представљање у текстуалном облику за потребе
123
исписивања. На располагању стоји класа Citaj у безименом пакету која садржи заједничке методе за читање свих стандардних типова података.
Саставити на језику Java класу са главним програмом који преко главног
улаза прочита податке о дређеном броју возила и после тога на главном
излазу испише податке о возилима која могу да пређу преко моста задате
носивости.
// Vozilo.java - Apstraktna klasa vozila.
package Vozila;
import Citaj;
abstract public class Vozilo {
double sTezina;
public Vozilo () {}
public Vozilo (double sTez) { sTezina = sTez; }
public double ukTezina () { return sTezina; }
public void citaj () {
System.out.print ("Sopstvena tezina? ");
sTezina = Citaj.Double () ;
}
public String toString () { return "" + sTezina; }
}
// PVozilo.java - Klasa putnickih vozila.
package Vozila;
import Citaj;
public class PVozilo extends Vozilo {
int brPutnika;
double srTezina;
public PVozilo () {}
public PVozilo (double sTez, int brPut, double srTez) {
super (sTez);
brPutnika = brPut; srTezina = srTez;
}
public double ukTezina ()
{ return super.ukTezina () + brPutnika * srTezina; }
public void citaj () {
super.citaj ();
System.out.print ("Broj putnika? ");
brPutnika = Citaj.Int ();
System.out.print ("Srednja tezina putnika? ");
124
srTezina = Citaj.Double ();
}
public String toString () {
return "pVozilo (" + sTezina + "+" + brPutnika + "*" + srTezina +
"=" + ukTezina () + ")";
}
}
// TVozilo.java - Klasa teretnih vozila.
package Vozila;
import Citaj;
public class TVozilo extends Vozilo {
double tezTereta;
public TVozilo () {}
public TVozilo (double sTez, double tezTer)
{ super (sTez); tezTereta = tezTer; }
public double ukTezina ()
{ return super.ukTezina () + tezTereta; }
public void citaj () {
super.citaj ();
System.out.print ("Tezina tereta? ");
tezTereta = Citaj.Double ();
}
public String toString ()
{ return "tVozilo (" + sTezina + "+" + tezTereta+"="+ukTezina()+")"; }
}
// VozilaT.java - Ispitivanje klasa vozila.
import Vozila.*;
class VozilaT {
public static void main (String[] argi) {
Vozilo[] vozila = new Vozilo [100];
Vozilo vozilo = null;
int brVozila = 0;
while (true) {
System.out.print ("Vrsta vozila (P, T)? ");
switch (Citaj.Char()) {
case 'p': case 'P': vozilo = new PVozilo (); break;
case 't': case 'T': vozilo = new TVozilo (); break;
default : vozilo = null;
}
if (vozilo == null) break;
vozilo.citaj ();
vozila[brVozila++] = vozilo;
} while (vozilo != null);
System.out.print ("\nNosivost mosta? ");
double nosivost = Citaj.Double ();
System.out.print ("\nMogu da predju most:\n");
for (int i=0; i<brVozila; i++) {
if (vozila[i].ukTezina () <= nosivost)
125
System.out.println(" " + vozila[i]);
}
}
}
25. Написати на језику Java следећи пакет типова (грешке пријављивати изузецима опремљеним текстовима порука):
(25 поена) Вектор у простору се задаје реалним компонентама у правцу x, y и z оса, а може да се иницијализује и на основу другог вектора. Може да се вектору дода други вектор, да се вектор помножи реалним скларним бројем, да се одреди интензитет вектора и да се састави текстуални опис вектора у облику "(x,y,z)".
Именованој ствари може да се дохвати име типа String. Апстрактнa именованa фигурa у простору садржи име произвољне дужине.
Може да се направи копија фигуре и да се састави текстуални опис фигуре који се састоји од имена.
(20 поена) Покретљива ствар може да се помери у простору за задати вектор помака и да се одреди њен вектор положаја.
Покретљива именована тачка у простору се задаје једнословним именом и вектором положаја. Може да се направи копија тачке и да се састави текстуални опис облика "ime(x,y,z)".
(20 поена) Покретљиви многоугао у простору је фигура која се задаје именом и бројем темена. Тачке темена се додају редом, после стварања. Грешка је ако се премаши задати број темена. Као положај многоугла узима се положај тежишта (аритметичке средине одговарајућих координата). Текстуални опис је облика "ime[t1,…,tn]", где је ti текстуални опис i-тог темена.
(5 поена) Написати на језику Java програм (класу с главном функцијом) који направи многоугао, направи његову копију, помери копију за задати вектор помака, испише на главном излазу оба многоугла са одговарајућим растојањима тежишта од координатног почетка. Сви параметри треба да буду константе (не треба ништа учитавати)
// Vektor.java
package figure;
public class Vektor {
private double x, y, z;
public Vektor( double a, double b, double c) { x = a; y = b; z = c; }
public Vektor( Vektor v) { x = v.x; y = v.y; z = v.z; }
public Vektor dodaj( Vektor v) { x += v.x; y += v.y; z += v.z; return
this; }
public Vektor pomnozi( double s) { x += s; y *= s; z *= s; return this; }
public double intenzitet() { return Math.sqrt (x*x + y*y + z*z); }
public String toString() { return "(" + x + "," + y + "," + z + ")"; }
}
126
// Imenovan.java
package figure;
public interface Imenovan { String dohvatiIme(); }
// Figura.java
package figure;
public abstract class Figura implements Imenovan, Cloneable {
private String ime;
protected Figura( String i) { ime = i; }
public String dohvatiIme() { return ime; }
public Object clone() {
try { return super.clone(); }
catch (CloneNotSupportedException g) { return null; }
}
public String toString() { return ime; }
}
// Pokretan.java
package figure;
public interface Pokretljiv {
void pomeri( Vektor pomak);
Vektor polozaj();
}
// Tacka.java
package figure;
public class Tacka implements Pokretljiv, Imenovan, Cloneable {
private char ime;
private Vektor polozaj;
public Tacka( char i, Vektor p) { ime = i; polozaj = p; }
public String dohvatiIme() { return Character.toString( ime); }
public void pomeri( Vektor pomak) { polozaj.dodaj( pomak); }
public Vektor polozaj() { return polozaj; }
public Object clone() {
try {
Tacka t = (Tacka)super.clone( );
t.polozaj = new Vektor( polozaj);
return t;
} catch (CloneNotSupportedException g) {
return null;
}
}
public String toString() { return dohvatiIme() + polozaj; }
}
// GGotov.java
127
package figure;
public class GGotov extends Exception {
public String toString( ) { return "*** Mnogougao je vec gotov!"; }
}
// Mnogougao.java
package figure;
public class Mnogougao extends Figura implements Pokretljiv {
private Tacka[] tem;
private int n;
public Mnogougao ( String ime, int brTem) { super( ime); tem = new Tacka
[brTem]; }
public Mnogougao dodaj(Tacka teme)throws GGotov {
if (n == tem.length) throw new GGotov( );
tem[n++] = teme;
return this;
}
public void pomeri( Vektor pomak) {
for (int i=0; i<n; tem[i++].pomeri(pomak));
}
public Vektor polozaj( ) {
Vektor tez = new Vektor( 0, 0, 0);
for (int i=0; i<n; tez.dodaj(tem[i++].polozaj()));
return tez.pomnozi( 1./n);
}
public Object clone() {
Mnogougao m = (Mnogougao)super.clone( );
m.tem = tem.clone();
for (int i=0; i<n; i++) m.tem[i] = (Tacka)tem[i].clone();
return m;
}
public String toString() {
StringBuffer s = new StringBuffer ( super.toString());
s.append( '[');
for (int i=0; i<n; i++) {
if (i>0) s.append( ',');
s.append( tem[i]);
}
return s.append( ']').toString( );
}
}
// Test.java
import figure.*;
public class Test {
public static void main (String[] varg) {
try {
Mnogougao m1 = new Mnogougao( "abc", 3);
m1.dodaj( new Tacka('A', new Vektor(0,0,0)))
.dodaj( new Tacka('B', new Vektor(1,1,1)))
.dodaj( new Tacka('C', new Vektor(3,2,1)));
Mnogougao m2 = (Mnogougao)m1.clone();
128
m2.pomeri (new Vektor(1,2,3));
System.out.println( m1 + " - " + m1.polozaj().intenzitet());
System.out.println( m2 + " - " + m2.polozaj().intenzitet());
} catch (GGotov g) {
System.out.println( g);
}
}
}
abc[A(0.0,0.0,0.0),B(1.0,1.0,1.0),C(3.0,2.0,1.0)] – 4.496912521077347
abc[A(1.0,2.0,3.0),B(2.0,3.0,4.0),C(4.0,4.0,4.0)] – 8.73053390247253
26. Пројектовати на језику Java пакет класа са следћим описом:
Апстрактни предмет има јединствени, аутоматски генерисани идентификациони број. Може да се испита да ли је мањи од другог предмета и да ли је једнак другом предмету. Два предмета се сматрају једнаким ако први није мањи од другог и други није мањи од првог. Конверзија у тип String садржи идентификациони број предмета.
Низ предмета је уређен по неопадајућем редоследу и може да садржи задати број предмета. Ствара се празан са задатим капацитетом (подразумевано 10). Нови предмети се додају на одговарајуће место. Препуњавање низа се пријављује изузетком помоћу објекта типа специјалне једноставне класе. Одређивање позиције задатог предмета у низу се врши секвенцијалним претраживањем. Неуспех тражења се означава негативним бројем. Конверзија низа у тип String састоји се од резултата конверзије елемената низа у тип String међусобно раздвојених зарезима, унутар пара средњих заграда.
Квадар је предмет задат дужинама ивица коме може да се израчуна запремина. Упоређивање квадара се врши на основу запремина. Конверзија у тип String је облика Kid(a,b,c), где су id - идентификациони број квадра, а a, b и c - дужине страница квадра.
Саставити на језику Java класу са главним програмом који прочита низ квадара, испише тај низ, затим учита посебан квадар и испише позицију тог квадра у низу. На располагању стоји класа Citaj у безименом пакету која садржи заједничке методе за читање свих стандардних типова података.
// Predmet.java
package predmeti;
public abstract class Predmet {
private static int ukId;
private int id = ++ukId;
public abstract boolean manji (Predmet p);
public boolean jednak (Predmet p) { return !manji(p) && !p.manji(this); }
public String toString () { return Integer.toString (id); }
}
// GNizPun.java
package predmeti;
public class GNizPun extends Exception {}
// Niz.java
package predmeti;
public class Niz {
private Predmet[] niz;
129
private int n;
public Niz (int k) { niz = new Predmet [k];}
public Niz () { this (10); }
public Niz dodaj(Predmet p) throws GNizPun {
if (n == niz.length) throw new GNizPun ();
int i=n-1;
while (i>=0 && p.manji(niz[i])) { niz[i+1] = niz[i]; i--; }
niz[i+1] = p; n++;
return this;
}
public int poz (Predmet p) {
int i=0;
while (i<n && niz[i].manji(p)) i++;
return (i<n && p.jednak(niz[i])) ? i : -1;
}
public String toString () {
StringBuffer s = new StringBuffer ('[');
for (int i=0; i<n; i++)
s.append (niz[i])
.append ((i<n-1)?',':']');
return s.toString ();
}
}
// Kvadar.java
package predmeti;
public class Kvadar extends Predmet {
private double a, b, c;
public Kvadar (double aa, double bb, double cc)
{ a = aa; b = bb; c = cc; }
public double V () { return a * b * c; }
public boolean manji (Predmet k) { return V() < ((Kvadar)k).V(); }
public String toString ()
{ return "K" + super.toString() + '(' + a + ',' + b + ',' + c + ')'; }
}
// Test.java
import predmeti.*;
public class Test {
public static void main (String[] varg) {
System.out.print ("n? ");
int n = Citaj.Int ();
Niz niz = new Niz (n);
try {
for (int i=0; i<n; i++) {
System.out.print ("niz[" +i +"]? ");
niz.dodaj (new Kvadar(Citaj.Double(), Citaj.Double(),
Citaj.Double()));
}
System.out.println ("Uredjeni niz: " + niz);
System.out.print ("Trazi se? ");
Kvadar k = new Kvadar (Citaj.Double(), Citaj.Double(),
Citaj.Double());
int p = niz.poz (k);
if (p >=0) System.out.println("poz= " + p);
else System.out.println("Nema tog kvadra");
} catch (Exception g) {
System.out.println (g);
}
}
}
130
Razni zadaci
Zadatak 1
Definisati klasu Smestaj koja ima celobrojne članove brKreveta i cenaPoDanu. Iz nje izvesti
klasu PrivatniSmestaj koja ima dodatne članove taksa i tip (koji može biti “SOBA” ili
“APARTMAN”), kao i klasu HotelskiSmestaj koja ima dodatni član ishrana (koji može biti
“POLUPANSION” ili “PANSION”). U obe klase dodati član trajanje koji predstavlja
maksimalni broj dana na koliko se smeštaj iznajmljuje, kao i metod ukupnaCena() koji kao
rezultat daje ukupnu cenu iznajmljivanja smeštaja. Tip “APARTMAN” povećava dnevnu cenu
privatnog smeštaja za 10%, a tip “PANSION” povećava dnevnu cenu hotelskog smeštaja za 30%.
Taksa za privatni smeštaj se računa na ukupno trajanje iznajmljivanja smeštaja i samo se dodaje na
cenu.
Kreirati klasu Sezona koja kao svoj član ima niz smeštaja.
- Napisati metode za dodavanje novog smeštaja, kao i brisanje smeštaja sa određenim indeksom.
- Napisati metod koji ispituje da li je moguće smestiti dati broj gostiju na određeni broj dana. Ako može, rezultat metoda je ukupna cena smeštanja tih gostiju. Ako ne može, rezultat je 0. Naći bilo koje rešenje.
- Napisati metod koji određuje najjeftiniji smeštaj u koji može da se smesti grupa gostiju na dati broj dana.
- Napisati metod koji određuje najjeftiniji hotelski smeštaj u koji može da se smesti grupa gostiju na dati broj dana.
131
Zadatak: Napisati aplet koji predstavlja interfejs za rad sa kvadratnim jednačinama.
Aplet treba organizovati u celine koje sadrže:
1. Tri tekstualna polja za unos koeficijenata kvadratne jednačine. Dugme Dodaj za dodavanje učitane kvadratne
jednačine u kolekciju i dugme Prikazi za prikaz kvadratne jednačine. Dugme Prikazi prikazuje poslednju
kvadratnu jednačinu smeštenu u kolekciju. Ukoliko nema jednačina u kolekciji, ništa se ne ispisuje. Izvršiti
kontrolu vrednosti učitanih koeficijenata.
2. Dugmeta Izvod i Koreni za računaje i ispis izvoda odgovarajuće kvadratne funkcije i za računaje i ispis korena
kvadratne jednačine. Ispisati odgovarajuću poruku ako jednačina nema realne korene. Ako jednačina ima dva
ista realna korena, ispisati samo jedan.
Format ispisa kvadratne jednačine: a*x^2 + b*x + c = 0.
Pretpostaviti da jednačina ima vrednosti različite od nule za sve koeficijente.
Obezbediti i korektan ispis izvoda kvadratne jednačine.
3. Tekstualna polja za unos vrednosti R, G i B komponente boje koja će se koristiti za ispis kvadratne jednačine,
izvoda, korena i poruka.
Za svaku komponentu se zadaje njena vrednost koja pripada intervalu [0, 255] .
Klikom na dugme Postavi boju postavlja se boja za ispis na odgovarajuću kombinaciju datih komponenti.
Izabranom bojom obojeno je tekstualno polje pored dugmeta Postavi boju koje ne može da se edituje.
U isto tekstualno polje ispisuje se poruka ukoliko vrednost neke od komponenti ne pripada datom opsegu ili je
unešena vrednost koja nije broj.
4. Dugme Kvadratne jednacine. Klikom na dugme vrši se ispis svih uspešno unešenih kvadratnih jednačina u fajl
izlaz.txt.
Format ispisa: svaki red fajla sadrži jednu kvadratnu jednačinu iza koje su navedeni njeni realni koreni, ukoliko
posoje, u suprotnom je ispisana poruka da jednačina nema realnih korena.
Primer: 1.0*x^2 -3.0*x + 2.0 = 0 2.0 1.0
2.0*x^2 + 3.0*x + 4.0 = 0 Jednacina nema realnih korena!
Ispit iz Objektno orijentisanog programiranja
17. 06. 2009.
132
133
package kvadratneJednacine;
public class KvadratnaJednacina {
private double a;
private double b;
private double c;
public KvadratnaJednacina(double a, double b, double c)
{
this.a = a;
this.b = b;
this.c = c;
}
public KvadratnaJednacina izvod()
{
return new KvadratnaJednacina(0, 2*a, b);
}
public double[] koreni()
{
double koreni[] = new double[2];
double D = diskriminanta();
if(!imaRealneKorene())
koreni[0] = koreni[1] = 0;
else if(D == 0)
koreni[0] = koreni[1] = -b/(2*a);
else
{
koreni[0] = (-b + Math.sqrt(D))/(2*a);
koreni[1] = (-b - Math.sqrt(D))/(2*a);
}
return koreni;
}
public double diskriminanta()
{
return b*b - 4*a*c;
}
public boolean imaRealneKorene()
{
return b*b - 4*a*c >= 0 ? true : false;
}
public String toString()
{
String string = new String();
if(a == 0)
{
string += b + "*x";
string += c > 0 ? " + " + c : " " + c;
string += " = 0";
}
else
{
string += a + "*x^2";
string += b > 0 ? " + " + b + "*x" : " " + b + "*x";
string += c > 0 ? " + " + c : " " + c + " = 0";
string += " = 0";
}
return string;
134
}
}
package kvadratneJednacine;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Stack;
import javax.swing.BorderFactory;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class Aplet extends JApplet {
public static final long serialVersionUID = 0;
private JPanel panel1;
private JPanel panel2;
private JPanel panel3;
private JPanel panel4;
private JLabel la, lb, lc;
private JTextField a, b, c;
private JButton prikazi;
private JLabel labela_ispis;
private JButton dodaj;
private JButton izvod;
private JLabel labela_izvod;
private JButton koreni;
private JLabel labela_koreni;
private JLabel lcrvena, lzelena, lplava;
private JTextField crvena, zelena, plava;
private JButton boja;
private JTextField polje_boja;
private JButton prikaziSve;
private Color bojaZaIspis;
private Stack<KvadratnaJednacina> jednacine = new
Stack<KvadratnaJednacina>();
public void init() {
SwingUtilities.invokeLater(
135
new Runnable() {
public void run(){
kreirajGUI();
}
});
}
public void kreirajGUI()
{
setSize(500, 500);
setLayout(new GridLayout(0, 1));
panel1 = new JPanel(new GridLayout(0, 1));
panel1.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(Color.red,
Color.orange), "Kvadratne jednacine"));
JPanel panel1A = new JPanel();
la = new JLabel("a:");
a = new JTextField(3);
lb = new JLabel("b:");
b = new JTextField(3);
lc = new JLabel("c:");
c = new JTextField(3);
panel1A.add(la);
panel1A.add(a);
panel1A.add(lb);
panel1A.add(b);
panel1A.add(lc);
panel1A.add(c);
panel1.add(panel1A);
JPanel panel1B = new JPanel(new FlowLayout(FlowLayout.LEFT, 20,
5));
prikazi = new JButton("Prikazi");
prikazi.setPreferredSize(new Dimension(120, 25));
labela_ispis = new JLabel("");
panel1B.add(prikazi);
panel1B.add(labela_ispis);
panel1.add(panel1B);
prikazi.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e)
{
if(jednacine.empty())
labela_ispis.setText("");
else
{
labela_ispis.setForeground(bojaZaIspis);
labela_ispis.setText(jednacine.peek().toString());
}
}
});
JPanel panel1C = new JPanel();
dodaj = new JButton("Dodaj");
dodaj.setPreferredSize(new Dimension(120, 25));
panel1C.add(dodaj);
136
panel1.add(panel1C);
add(panel1);
dodaj.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e)
{
try {
double koef_a =
Double.parseDouble(a.getText());
double koef_b =
Double.parseDouble(b.getText());
double koef_c =
Double.parseDouble(c.getText());
jednacine.push(new
KvadratnaJednacina(koef_a, koef_b, koef_c));
labela_ispis.setText("");
}
catch(NumberFormatException nfe)
{
labela_ispis.setText("Koeficijenti
jednacine moraju biti brojevi!");
labela_ispis.setForeground(bojaZaIspis);
}
}
});
panel2 = new JPanel(new GridLayout(0, 1));
panel2.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(Color.red,
Color.orange), "Izracunavanje"));
JPanel panel2A = new JPanel(new FlowLayout(FlowLayout.LEFT, 20,
5));
izvod = new JButton("Izvod");
izvod.setPreferredSize(new Dimension(120, 25));
labela_izvod = new JLabel("");
panel2A.add(izvod);
panel2A.add(labela_izvod);
panel2.add(panel2A);
izvod.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e)
{
labela_izvod.setText(jednacine.peek().izvod().toString());
labela_izvod.setForeground(bojaZaIspis);
}
});
JPanel panel2B = new JPanel(new FlowLayout(FlowLayout.LEFT, 20,
5));
koreni = new JButton("Koreni");
koreni.setPreferredSize(new Dimension(120, 25));
labela_koreni = new JLabel("");
panel2B.add(koreni);
panel2B.add(labela_koreni);
panel2.add(panel2B);
add(panel2);
137
koreni.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e)
{
labela_koreni.setForeground(bojaZaIspis);
double[] resenja =
jednacine.peek().koreni();
if(resenja[0] != resenja[1])
labela_koreni.setText("" +
resenja[0] + " " + resenja[1]);
else if(resenja[0] == 0)
labela_koreni.setText("Jednacina
nema realnih resenja!");
else
labela_koreni.setText("" +
resenja[0]);
}
});
panel3 = new JPanel(new GridLayout(0, 1));
panel3.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(Color.red,
Color.orange), "Izbor boje"));
JPanel panel3A = new JPanel();
lcrvena= new JLabel("crvena:");
crvena = new JTextField(4);
lzelena = new JLabel("zelena:");
zelena = new JTextField(4);
lplava = new JLabel("plava:");
plava = new JTextField(4);
panel3A.add(lcrvena);
panel3A.add(crvena);
panel3A.add(lzelena);
panel3A.add(zelena);
panel3A.add(lplava);
panel3A.add(plava);
panel3.add(panel3A);
JPanel panel3B = new JPanel(new FlowLayout(FlowLayout.LEFT, 20,
5));
boja = new JButton("Postavi boju");
boja.setPreferredSize(new Dimension(120, 25));
polje_boja = new JTextField("");
polje_boja.setPreferredSize(new Dimension(200, 25));
polje_boja.setEditable(false);
panel3B.add(boja);
panel3B.add(polje_boja);
panel3.add(panel3B);
add(panel3);
boja.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e)
{
try {
int R =
Integer.parseInt(crvena.getText());
138
int G =
Integer.parseInt(zelena.getText());
int B =
Integer.parseInt(plava.getText());
if(R < 0 || R > 255 || G < 0 || G
> 255 || B < 0 || B > 255)
{
polje_boja.setText("Dozvoljeni intenzitet boje: [0, 255]");
return;
}
bojaZaIspis = new Color((R<<16) ^
(G<<8) ^ B);
polje_boja.setBackground(bojaZaIspis);
}
catch(NumberFormatException nfe)
{
polje_boja.setText("Intenzitet
boje je numericki podatak!");
}
}
});
panel4 = new JPanel();
panel4.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(Color.red,
Color.orange), "Prikaz svih jednacina"));
prikaziSve = new JButton("Kvadratne jednacine");
panel4.add(prikaziSve);
add(panel4);
prikaziSve.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e)
{
PrintWriter izlaz;
try{
izlaz = new PrintWriter(new
FileWriter("C:/Documents and Settings/Biljana/Desktop/bilja/izlaz.txt"));
}
catch(IOException IOe){
System.out.println("Neuspela
operacija pripreme za upis u fajl izlaz.txt");
return;
}
for(KvadratnaJednacina jednacina :
jednacine)
{
double[] koreni =
jednacina.koreni();
izlaz.print(jednacina + " ");
if(koreni[0] != koreni[1])
izlaz.print(koreni[0] + "
" + koreni[1]);
else if(koreni[0] == 0)
izlaz.print("Nema realnih
resenja!");
else
139
izlaz.print(koreni[0]);
izlaz.println();
izlaz.flush();
}
}
});
setVisible(true);
}
}
140
Domaći I
1. (JAVA) Kreirati klasu Slagalica koja omogućava zadavanje tajne reči, slučajno
permutovanje znakova tajne reči, kao i dve operacije – zameni(i, j),
rotiraj(i, j). Operacija zameni(i, j) menja mesta i – tom i j – tom
karakteru u transformisanoj reči, a rotiraj(i, j) rotira sve karaktere od i – tog
do j – tog u desno ako je i < j, odnosno u levo ako je i > j.
U glavnom programu omogućiti igranje sledeće igre – kompjuter učitava niz reči iz
datoteke ‘recnik.dat’, odabira slučajno jednu od njih i permutuje joj karaktere. Posle
toga prikazuje permutovanu reč korisniku i traži od njega komande. Posle svake
komande treba odštampati promenjenu reč. Igra se završava kada igrač pogodi traženu
reč ili posle 10 pokušaja.
2. (JAVA I C++) Kreirati klasu KompleksniBroj koja omogućava rad sa kompleksnim brojevima. Klasa bi trebalo da ima:
a. podatke re i im, za realni i imaginarni deo kompleksnog broja; b. konstruktor sa dva parametra koji postavlja vrednosti za realni i imaginarni
deo kompleksnog broja; c. konstruktor bez parametara koji postavlja vrednost kompleksnog broja na 0; d. odgovarajuće getere i setere;
e. javni metod moduo() koji kao rezultat vraća moduo kompleksnog broja; f. javne metode kojima se izvode osnovne operacije sa kompleksnim brojevima
(sabiranje, oduzimanje, množenje, stepenovanje, deljenje).
3. (JAVA) Kreirati klasu Razlomak koja omogućava rad sa racionalnim brojevima. Klasa bi trebalo da ima:
a. podatke br i im, za brojilac i imenilac razlomka; b. privatni metod skrati() koji dovodi razlomak na neskrativi oblik. Može se
definisati i privatni metod int nzd(int a, int b) koji određuje najveći zajednički delilac dva cela broja;
c. konstruktor sa dva parametra koji postavlja vrednosti za brojilac i imenilac. Voditi računa o tome da imenilac ne može biti 0;
d. konstruktor bez parametara koji postavlja vrednost kompleksnog broja na 0. U tom slučaju je imenilac jednak 1;
e. odgovarajuće getere i setere; f. javne metode kojima se izvode osnovne operacije sa razlomcima (sabiranje,
oduzimanje, množenje, stepenovanje, deljenje).
4. Kreirati klasu Automobil koja ima:
a. celobrojne podatke trenutnaBrzina i maksimalnaBrzina koji predstavljaju trenutnu i maksimalnu brzinu automobila u km/h. Trenutna brzina automobila ne može biti veća od maksimalne;
b. realni podatak predjeniPut koji određuje koliko kilometara je automobil prešao;
c. konstruktor sa jednim celobrojnim parametrom koji predstavlja maksimalnu brzinu automobila. Ona ne može biti negativna. Trenutna brzina i pređeni put se postavljaju na 0;
d. javni metod void ubrzaj(int a) koji povećava trenutnu brzinu za a km/h, ali ne preko maksimalne brzine;
141
e. javni metod void uspori(int a) koji smanjuje trenutnu brzinu za a km/h, ali ne ispod 0;
f. javni metod void vozi(double t) koji na promenljivu predjeniPut dodaje put koji auto pređe za t časova, vozeći trenutnom brzinom;
g. odgovarajuće getere i setere; h. javni metod int ucitajKomande(char * fileName), odnosno, u
Javi, int ucitajKomande(String fileName) koji učitava i izvršava komande iz tekstualne datoteke čije je ime dato. Metod vraća 0 ako se pri čitanju desila neka greška, a inače 1. Komande su oblika UBRZAJ A, USPORI A, VOZI T i svaka je data u po jednom redu. U prvom redu datoteke se nalazi broj komandi.
5. (C++) Kreirati klasu Robot koja u svakom trenutku pamti poziciju robota u ravni.
Klasa bi trebalo da ima metode kreni(int t), ubrzaj(int v), uspori(int v), levo(), levo(int x), desno(), desno(int x). Robot se na početku nalazi u koordinatnom početku, a početni smer je u pozitivnom smeru x – ose. Trebalo bi omogućiti korisniku da pri kreiranju objekta zada početne koordinate robota, kao i početni smer. Brzina robota se meri u metrima u sekundi, a početna brzina je 0. Metod kreni(t) pokreće robota u trenutnom smeru t sekundi. Metod ubrzaj(v) (uspori(v)) povećava (smanjuje) brzinu robota za v metara u sekundi. Brzina robota nikada ne sme biti manja od 0 m/s, a ni veća od 30 m/s. Metodi levo(x ) desno(x) menjaju smer kretanja robota za x stepeni u odgovarajuću stranu. Metodi levo() i desno() menjaju smer kretanja za 90 stepeni.
Napisati metode pozicijaX() i pozicijaY() koji daju x, odnosno y koordinatu
trenutne pozicije. Napisati metod ucitajKomande(String filename) koji iz
tekstualne datoteke sa navedenim imenom učitava komande za robota i izvršava ih. U
svakom redu datoteke se nalazi po jedna komanda. Komande mogu biti KRENI #,
UBRZAJ #, USPORI #, LEVO, LEVO #, DESNO, DESNO # (# označava prirodan
broj).
U glavnom programu učitati početne koordinate robota, a zatim izvršiti spisak komandi
iz datoteke ‘robot.in’. Na kraju odštampati rastojanje robota od početne tačke.
6. (JAVA) Igra Mastermind se sastoji u tome da kompjuter slučajno generiše niz od n znakova koje bira od k vrsta znakova. Igrač zatim pogađa kombinaciju koju je kompjuter zamislio, a kompjuter posle svako pogađanja daje izveštaj koliko je znakova pogođeno, kao i koliko je znakova na svom mestu.
Kreirati klasu Mastermind kojoj se može zadati broj različitih znakova k ( 83 k ),
kao i dužina niza koji se generiše n ( 104 n ). Definisati metod generisi() koji
generiše slučajni niz znakova, kao i metod proba(...) čiji je parametar niz znakova
(znak može biti predstavljen cifrom ili posebnom klasom – bolje rešenje), a čiji je
rezultat klasa (ili struktura) koja sadrži podatke o broju pogođenih znakova i broju
znakova koji su na svom mestu.
U glavnom programu od korisnika uzeti podatke za n i k, a zatim simulirati igru
Mastermind sa datim podacima.
7. (C++) Igra Mastermind se sastoji u tome da kompjuter slučajno generiše niz od n znakova koje bira od k vrsta znakova. Igrač zatim pogađa kombinaciju koju je
142
kompjuter zamislio, a kompjuter posle svako pogađanja daje izveštaj koliko je znakova pogođeno, kao i koliko je znakova na svom mestu.
Kreirati klasu Mastermind kojoj se može zadati broj različitih znakova k ( 83 k ),
kao i dužina niza koji se generiše n ( 104 n ). Definisati metod generisi() koji
generiše slučajni niz znakova, kao i metod proba(...) čiji je parametar niz znakova
(znak može biti predstavljen cifrom ili posebnom klasom – bolje rešenje), a čiji je
rezultat klasa (ili struktura) koja sadrži podatke o broju pogođenih znakova i broju
znakova koji su na svom mestu.
U glavnom programu od korisnika uzeti podatke za n i k, a zatim simulirati igru
Mastermind sa datim podacima.
8. (JAVA) Kreirati klasu VelikiInt koja omogućava rad sa velikim celim brojevima. Definisati metode za sabiranje, oduzimanje, množenje, celobrojno deljenje i izračunavanje ostatka pri deljenju dva velika cela broja. Takođe, napisati metode za ove osnovne operacije kada je drugi operand običan ceo broj. Rezultat svakog od ovih metoda je VelikiInt. Napisati konstruktor bez parametara koji postavlja broj na vrednost 0, konstruktor sa celobrojnim parametrom, kao i konstruktore kojima su parametri string, odnosno niz cifara.
9. (C++) Kreirati klasu VelikiInt koja omogućava rad sa velikim celim brojevima. Definisati metode za sabiranje, oduzimanje, množenje, celobrojno deljenje i izračunavanje ostatka pri deljenju dva velika cela broja. Takođe, napisati metode za ove osnovne operacije kada je drugi operand običan ceo broj. Rezultat svakog od ovih metoda je VelikiInt. Napisati konstruktor bez parametara koji postavlja broj na vrednost 0, konstruktor sa celobrojnim parametrom, kao i konstruktore kojima su parametri string, odnosno niz cifara.
10. (JAVA) Kreirati klasu Soba koja predstavlja hotelsku sobu. Bitni podaci za hotelsku sobu su broj sobe, broj kreveta, cena, kao i to da li je soba zauzeta ili ne. Kreirati klasu Hotel koja omogućava unošenje podataka o sobama u hotelu (sa tastature ili iz datoteke). Definisati metod pronadji(int n) koja proverava da li u hotelu postoji soba u koju se može smestiti n osoba, pa ako postoji, kao rezultat daje broj najjeftinije sobe u koju taj broj osoba može da se smesti. Ako ima više takvih soba, rezultat treba da bude broj sobe koja ima najmanji broj kreveta, a ako i takvih ima više, onda od njih vratiti najmanji broj sobe. Ukoliko takva soba ne postoji, rezultat je –1. Napisati i metod zarada(int n) koji izračunava kolika je ukupna zarada od n – tokrevetnih soba u hotelu. Ako je 0n , onda se računaju sve sobe, bez obzira na broj kreveta. Naravno, računaju se samo zauzete sobe.
11. (C++) Kreirati klasu Soba koja predstavlja hotelsku sobu. Bitni podaci za hotelsku
sobu su broj sobe, broj kreveta, cena, kao i to da li je soba zauzeta ili ne. Kreirati klasu
Hotel koja omogućava unošenje podataka o sobama u hotelu (sa tastature ili iz
datoteke). Definisati metod pronadji(int n) koja proverava da li u hotelu
postoji soba u koju se može smestiti n osoba, pa ako postoji, kao rezultat daje broj
najjeftinije sobe u koju taj broj osoba može da se smesti. Ako ima više takvih soba,
rezultat treba da bude broj sobe koja ima najmanji broj kreveta, a ako i takvih ima
više, onda od njih vratiti najmanji broj sobe. Ukoliko takva soba ne postoji, rezultat je
–1. Napisati i metod zarada(int n) koji izračunava kolika je ukupna zarada od n
– tokrevetnih soba u hotelu. Ako je 0n , onda se računaju sve sobe, bez obzira na
broj kreveta. Naravno, računaju se samo zauzete sobe.
12. (JAVA) Kreirati klasu TesterLozinke koja proverava da li dati string prihvatljiv
143
za lozinku. String je prihvatljiv za lozinku ukoliko:
a. ima bar 7 znakova,
b. sadrži i velika i mala slova,
c. sadrži bar jednu cifru.
d. Klasa bi trebalo da omogući zadavanje stringa, bilo preko konstruktora ili
setera, kao i da ima javni metod prihvatljiv(String s) koji kao
rezultat daje jedan od stringova:
e. ‘u redu’, ukoliko su svi uslovi zadovoljeni,
f. ‘kratka lozinka’, ukoliko nije zadovoljen uslov a),
g. ‘lozinka mora da sadrzi bar jedno malo i bar jedno
veliko slovo’, ukoliko nije zadovoljen uslov b) (a jeste a)),
h. ‘lozinka mora da sadrzi bar jednu cifru’, ukoliko nije
zadovoljen uslov c) (a jesu a) i b)).
13. (C++) Kreirati klasu TesterLozinke koja proverava da li dati string prihvatljiv za
lozinku. String je prihvatljiv za lozinku ukoliko:
a. ima bar 7 znakova,
b. sadrži i velika i mala slova,
c. sadrži bar jednu cifru.
d. Klasa bi trebalo da omogući zadavanje stringa, bilo preko konstruktora ili
setera, kao i da ima javni metod prihvatljiv(String s) koji kao
rezultat daje jedan od stringova:
e. ‘u redu’, ukoliko su svi uslovi zadovoljeni,
f. ‘kratka lozinka’, ukoliko nije zadovoljen uslov a),
g. ‘lozinka mora da sadrzi bar jedno malo i bar jedno
veliko slovo’, ukoliko nije zadovoljen uslov b) (a jeste a)),
h. ‘lozinka mora da sadrzi bar jednu cifru’, ukoliko nije
zadovoljen uslov c) (a jesu a) i b)).
14. (JAVA) Kreirati klasu Kutija koja ima polja visina, sirina i duzina.
Napisati odgovarajuće getere i setere, kao i konstruktor sa 3 parametra.
Napisati javni metod stajeU(Kutija k) koji proverava da li kutija može da stane u
kutiju k. Jedna kutija može da stane u drugu, ako joj je osnova manja (po širini i dužini) od
osnove druge kutije (s’ tim što kutije mogu da se rotiraju oko vertikalne ose), a visina joj je manja
od visine druge kutije. Računati da su strane kutija uvek paralelne.
Kreirati klasu NizKutija koja sadrži polje kutije koje predstavlja niz kutija. U ovoj
klasi napisati metod proveri() koji proverava da li je moguće rasporediti kutije tako da svaka
staje u sledeću.
15. (C++) Kreirati klasu Kutija koja ima polja visina, sirina i duzina.
Napisati odgovarajuće getere i setere, kao i konstruktor sa 3 parametra.
Napisati javni metod stajeU(Kutija k) koji proverava da li kutija može da stane u
kutiju k. Jedna kutija može da stane u drugu, ako joj je osnova manja (po širini i dužini)
od osnove druge kutije (s’ tim što kutije mogu da se rotiraju oko vertikalne ose), a visina
joj je manja od visine druge kutije. Računati da su strane kutija uvek paralelne.
Kreirati klasu NizKutija koja sadrži polje kutije koje predstavlja niz kutija. U ovoj
klasi napisati metod proveri() koji proverava da li je moguće rasporediti kutije tako da
svaka staje u sledeću.
16. (JAVA) Kreirati klasu BinaryHeap koja kao podatak sadrži niz celih brojeva i koja
omogućava rad sa strukturom binarni hip. Klasa bi trebalo da ima konstruktor i javne
metode count(), get() i add(int x).
144
Objašnjenje: Binarni hip je struktura podataka koja se predstavlja binarnim stablom i ima
osobinu da je svaki element veći (za maxHeap) od svih elemenata koji se u stablu nalaze
ispod njega. Na slici je dat primer jednog binarnog hipa (slika levo).
Ovakva struktura se može predstaviti i nizom, tako što za i – ti element niza postavimo da
je element levo od njega onaj sa indeksom (2i + 1), a desno element sa indeksom (2i + 2)
(slika desno).
Element se u hip dodaje tako što se stavi na kraj niza, a onda se zamenjuje sa elementom
iznad sebe, sve dok je veći od njega. Ovo bi trebalo da radi metod add(int x).
Metod get() bi trebalo da kao rezultat vrati maksimalni elemen hipa (null ako je hip
prazan), a zatim da taj element obriše iz hipa. Prvi element se briše tako što se na njegovo
mesto dovede najmanji element, a zatim se taj najmanji element pomera ‘na dole’ tako što
se zamenjuje sa većim od dva elementa koji su direktno ispod njega, sve dok ne bude veći
od oba elementa koji su ispod njega.
Metod count() bi trebalo da daje trenutni broj elemenata u hipu.
17. (C++) Kreirati klasu BinaryHeap koja kao podatak sadrži niz celih brojeva i koja
omogućava rad sa strukturom binarni hip. Klasa bi trebalo da ima konstruktor i javne
metode count(), get() i add(int x).
Objašnjenje: Binarni hip je struktura podataka koja se predstavlja binarnim stablom i ima
osobinu da je svaki element veći (za maxHeap) od svih elemenata koji se u stablu nalaze
ispod njega. Na slici je dat primer jednog binarnog hipa (slika levo).
Ovakva struktura se može predstaviti i nizom, tako što za i – ti element niza postavimo da
je element levo od njega onaj sa indeksom (2i + 1), a desno element sa indeksom (2i + 2)
(slika desno).
Element se u hip dodaje tako što se stavi na kraj niza, a onda se zamenjuje sa elementom
iznad sebe, sve dok je veći od njega. Ovo bi trebalo da radi metod add(int x).
Metod get() bi trebalo da kao rezultat vrati maksimalni elemen hipa (null ako je hip
100
19
36
17
3
25
1
2
7 0 1 2 3 4 5 6 7 8
145
prazan), a zatim da taj element obriše iz hipa. Prvi element se briše tako što se na njegovo
mesto dovede najmanji element, a zatim se taj najmanji element pomera ‘na dole’ tako što
se zamenjuje sa većim od dva elementa koji su direktno ispod njega, sve dok ne bude veći
od oba elementa koji su ispod njega.
Metod count() bi trebalo da daje trenutni broj elemenata u hipu.
18. (JAVA) Napisati klasu Vektor za rad sa vektorima u ravni; vektor je odredjen svojom
pocetnom i krajnjom tackom. Definisati: konstruktor na osnovu dve zadate tacke
(pocetna i krajnja tacka vektora), konstruktor na osnovu jedne zadate tacke (pocetna
tacka je koordinatni pocetak, a krajnja tacka vektora je zadata), konstruktor kopije i
metode za sabiranje i oduzimanje vektora (rezultat je vektor). Takodje implementirati
i metod za translaciju vektora u zadatu tacku, metod za translaciju vektora za zadati
vektor, metod koji nalazi centralno simetricni vektor datom vektoru u odnosu na
zadatu tacku i metod za rotaciju vektora za dati ugao. U drugoj klasi implementirati
main() funkciju koja sa standardnog ulaza ucitava vektor i zatim ucitava tacku u koju
transliramo taj vektor, a zatim i ugao za koji ga dalje rotiramo; onda ucitavamo drugi
vektor, pa tacku u odnosu na koju nalazimo centralno simetricni vektor tom vektoru.
Na kraju ispisujemo zbir i razliku ovako dobenih vektora.
19. (C++) Napisati klasu Vektor za rad sa vektorima u ravni; vektor je odredjen svojom
pocetnom i krajnjom tackom. Definisati: konstruktor na osnovu dve zadate tacke
(pocetna i krajnja tacka vektora), konstruktor na osnovu jedne zadate tacke (pocetna
tacka je koordinatni pocetak, a krajnja tacka vektora je zadata), konstruktor kopije i
metode za sabiranje i oduzimanje vektora (rezultat je vektor). Takodje implementirati
i metod za translaciju vektora u zadatu tacku, metod za translaciju vektora za zadati
vektor, metod koji nalazi centralno simetricni vektor datom vektoru u odnosu na
zadatu tacku i metod za rotaciju vektora za dati ugao. U drugoj klasi implementirati
main() funkciju koja sa standardnog ulaza ucitava vektor i zatim ucitava tacku u koju
transliramo taj vektor, a zatim i ugao za koji ga dalje rotiramo; onda ucitavamo drugi
100
19
36
17
3
25
1
2
7 0 1 2 3 4 5 6 7 8
146
vektor, pa tacku u odnosu na koju nalazimo centralno simetricni vektor tom vektoru.
Na kraju ispisujemo zbir i razliku ovako dobenih vektora.
20. Написати на језику C++ следеће класе (класе опремити оним конструкторима и
деструктором који су потребни за безбедно коришћење класа):
Оцена садржи цео број у опсегу од 5 до 10. Вредности изван опсега приликом
стварања промене се у најближу прихватљиву вредност. Може да се дохвати
вредност оцене бројчано и словима као и да се оцена испише на главном излазу
када се пишу оба облика оцене (на пример: 10(deset)).
Испит садржи шифру испита од највише 6 знакова и оцену. Може да се дохвати
шифра испита и оцена и да се испит испише на главном излазу у облику
"шифра:оцена".
Студент има име (текст произвољне дужине), број индекса (дугачак цео број по
шеми ggggrrrr, где су g и r цифре године уписа и регистарског броја) и низ
испита задатог капацитета (подразумевано 40). Ствара се без иједног испита
после чега испити могу да се додају један по један. Повратна вредност при
додавању испита показује успех додавања (тј. да ли је било места у низу испита).
Не сме да се прави копија студента. Може да се израчуна средња вредност оцена
положених испита и да се студент испише на главном излазу у облику
"име[годУп/регБр:срОцена]".
Написати на језику C++ програм који направи једног студента, додаје му три испита и
испише га на главном излазу. Користити само константне податке (не треба ништа
учитавати с главног улаза).
21. Написати на језику JAVA следеће класе (класе опремити оним конструкторима и
деструктором који су потребни за безбедно коришћење класа):
Оцена садржи цео број у опсегу од 5 до 10. Вредности изван опсега приликом
стварања промене се у најближу прихватљиву вредност. Може да се дохвати
вредност оцене бројчано и словима као и да се оцена испише на главном излазу
када се пишу оба облика оцене (на пример: 10(deset)).
Испит садржи шифру испита од највише 6 знакова и оцену. Може да се дохвати
шифра испита и оцена и да се испит испише на главном излазу у облику
"шифра:оцена".
Студент има име (текст произвољне дужине), број индекса (дугачак цео број по
шеми ggggrrrr, где су g и r цифре године уписа и регистарског броја) и низ
испита задатог капацитета (подразумевано 40). Ствара се без иједног испита
после чега испити могу да се додају један по један. Повратна вредност при
додавању испита показује успех додавања (тј. да ли је било места у низу испита).
Не сме да се прави копија студента. Може да се израчуна средња вредност оцена
положених испита и да се студент испише на главном излазу у облику
"име[годУп/регБр:срОцена]".
Написати на језику JAVA програм који направи једног студента, додаје му три испита и
испише га на главном излазу. Користити само константне податке (не треба ништа
учитавати с главног улаза).
22. Саставити на језику C++ следеће класе (класе опремити оним конструкторима и
деструктором који су потребни за безбедно коришћење класа):
147
Датум се задаје помоћу броја дана, месеца и године. Може да се провери да ли
три цела броја представљају исправан датум, да се ствара датум на основу три
цела броја (подразумевано 7.11.2005. – погрешан датум прекида програм), да се
дохватају делови датума, да се датум упореди с другим датумом (резултат је <0,
=0 или >0, зависно од тога да ли је текући датум пре, једнак или после задатог
датума), да се датум прочита са главног улаза и да се датум испише на главном
излазу.
Листа датума се ствара празна, после чега се датуми додају један по један на
крај листе. Може да се одреди дужина листе, да се дохвати најкаснији датум у
листи и да се листа испише на главном излазу.
Написати на језику C++ главни програм који читајући датуме с главног улаза направи
листу датума (читање се завршава првим неисправним датумом), испише на главном
излазу добијену листу као и најкаснији датум и понавља претходне кораке све док не
прочита празну листу.
23. Саставити на језику JAVA следеће класе (класе опремити оним конструкторима
и деструктором који су потребни за безбедно коришћење класа):
(25 поена) Датум се задаје помоћу броја дана, месеца и године. Може да се
провери да ли три цела броја представљају исправан датум, да се ствара датум
на основу три цела броја (подразумевано 7.11.2005. – погрешан датум прекида
програм), да се дохватају делови датума, да се датум упореди с другим датумом
(резултат је <0, =0 или >0, зависно од тога да ли је текући датум пре, једнак или
после задатог датума), да се датум прочита са главног улаза и да се датум
испише на главном излазу.
(25 поена) Листа датума се ствара празна, после чега се датуми додају један по
један на крај листе. Може да се одреди дужина листе, да се дохвати најкаснији
датум у листи и да се листа испише на главном излазу.
(20 поена) Написати на језику JAVA главни програм који читајући датуме с главног
улаза направи листу датума (читање се завршава првим неисправним датумом), испише
на главном излазу добијену листу као и најкаснији датум и понавља претходне кораке
све док не прочита празну листу.
24. Саставити на језику C++ следеће класе (класе опремити оним конструкторима и
деструктором који су потребни за безбедно коришћење класа):
(20 поена) Материјална тачка у простору се задаје помоћу реалне масе
(подразумевано 1) и три реалне координате (подразумевано (0,0,0)). Може да се
одреди растојање (r) до друге тачке, да се израчуна привлачна сила између тачке
и задате друге тачке (F=γ·m1·m2/r2, γ=6,67·10−11) и да се тачка испише на главном
излазу.
(30 поена) Низ материјалних тачака се ствара празан задатог почетног
капацитета (подразумевано 5), после чега се тачке додају једнa по једнa на крај
низа. Aко се низ препуни, капацитет му се повећа за 5. Може да се дохвати број
тачака у низу, да се дохвати тачка у низу која највише привлачи задату тачку и
да се низ испише на главном излазу.
(20 поена) Написати на језику C++ програм који читајући материјалне тачке с главног
улаза направи низ материјалних тачака (читање се завршава уносом негативне масе),
испише на главном излазу добијени низ као и тачку која највише привлачи тачку
148
јединичне масе у координатном почетку и понавља претходне кораке све док не
прочита празан низ (низ дужине 0).
25. Саставити на језику JAVA следеће класе (класе опремити оним конструкторима
и деструктором који су потребни за безбедно коришћење класа):
Материјална тачка у простору се задаје помоћу реалне масе (подразумевано 1)
и три реалне координате (подразумевано (0,0,0)). Може да се одреди растојање
(r) до друге тачке, да се израчуна привлачна сила између тачке и задате друге
тачке (F=γ·m1·m2/r2, γ=6,67·10−11) и да се тачка испише на главном излазу.
Низ материјалних тачака се ствара празан задатог почетног капацитета
(подразумевано 5), после чега се тачке додају једнa по једнa на крај низа. Aко се
низ препуни, капацитет му се повећа за 5. Може да се дохвати број тачака у
низу, да се дохвати тачка у низу која највише привлачи задату тачку и да се низ
испише на главном излазу.
Написати на језику JAVA програм који читајући материјалне тачке с главног улаза
направи низ материјалних тачака (читање се завршава уносом негативне масе), испише
на главном излазу добијени низ као и тачку која највише привлачи тачку јединичне
масе у координатном почетку и понавља претходне кораке све док не прочита празан
низ (низ дужине 0).
26. Realizovati klasu trougao koja će kao podatke članove imati tri tačke koje
predstavljaju objekte klase tačka. Klasa tačka treba da ima odgovarajuće konstruktore
i destruktore kao i funkcije za računanje rastojanja dve tačke i ugla između prave koju
čine dve tačke i x ose. Klasa trougao treba da poseduje odgovarajuće konstruktore,
destruktore kao i funkciju članicu koja će za tri date tačke proveravati da li čine
trougao ili su to tačke sa jedne prave i druga funkcija članica proverava da li je
trougao, na ovaj način zadat, jednakokraki. Napisati i glavni program u kojem će se
unositi koordinate za željene tačke, inicijalizovati potrebni objekti i proveravati da li
date tačke čine trougao i da li je isti jednakokraki.
27. Написати на језику C++ следеће класе (класе опремити оним конструкторима и
деструктором који су потребни за безбедно коришћење класа):
Оцена садржи цео број у опсегу од 5 до 10. Вредности изван опсега приликом
стварања промене се у најближу прихватљиву вредност. Може да се дохвати
вредност оцене бројчано и словима као и да се оцена испише на главном излазу
када се пишу оба облика оцене (на пример: 10(deset)).
Испит садржи шифру испита од највише 6 знакова и оцену. Може да се дохвати
шифра испита и оцена и да се испит испише на главном излазу у облику
"шифра:оцена".
Студент има име (текст произвољне дужине), број индекса (дугачак цео број по
шеми ggggrrrr, где су g и r цифре године уписа и регистарског броја) и низ
испита задатог капацитета (подразумевано 40). Ствара се без иједног испита
после чега испити могу да се додају један по један. Повратна вредност при
додавању испита показује успех додавања (тј. да ли је било места у низу испита).
Не сме да се прави копија студента. Може да се израчуна средња вредност оцена
149
положених испита и да се студент испише на главном излазу у облику
"име[годУп/регБр:срОцена]".
Написати на језику C++ програм који направи једног студента, додаје му три испита и
испише га на главном излазу. Користити само константне податке (не треба ништа
учитавати с главног улаза).
#include <iostream>
#include <cstring>
using namespace std;
class Ocena {
int oce;
static const char* opisi[];
public:
Ocena (int oc) {
if (oc < 5) oc = 5;
if (oc > 10) oc = 10;
oce = oc;
}
int ocena () const { return oce; }
const char* opis () const{ return opisi[oce-5]; }
void pisi () const { cout << oce << '(' << opis() << ')'; }
};
const char* Ocena::opisi[] = {"pet", "sest", "sedam", "osam", "devet",
"deset"};
class Ispit {
char sif[7];
Ocena oce;
public:
Ispit (char sif[], Ocena oce) : oce(oce) { strcpy (this->sif,sif); }
const char* sifra () const { return sif; }
Ocena ocena () const { return oce; }
void pisi () const { cout << sif << ':'; oce.pisi(); }
};
class Student {
char* ime;
long ind;
Ispit** ispiti;
int kap, duz;
Student (const Student&) {}
public:
Student (const char* ime, int ind, int k=40) {
this->ime = new char [strlen(ime)+1];
strcpy (this->ime, ime);
this->ind = ind;
ispiti = new Ispit* [kap = k];
duz = 0;
}
~Student ();
bool dodaj (const Ispit &isp) {
150
if (duz == kap) return false;
ispiti[duz++] = new Ispit (isp);
return true;
}
double srOcena () const;
void pisi () const {
cout << ime << '[' << (ind/10000) << '/' << (ind%10000) << ':' <<
srOcena() << ']';
}
};
Student::~Student () {
delete [] ime;
for (int i=0; i<duz; delete ispiti[i++]);
delete [] ispiti;
}
double Student::srOcena () const {
double s = 0; int n = 0;
for (int i=0; i<duz; i++)
if(ispiti[i]->ocena().ocena() > 5) {
s+=ispiti[i]->ocena().ocena();
n++;
}
if (n) s /= n;
return s;
}
int main () {
Student marko("Marko",20060055,45);
marko.dodaj (Ispit("SI2AS2",9));
marko.dodaj (Ispit("SI2OR2",5));
marko.dodaj (Ispit("SI2OO1",10));
marko.pisi(); cout << endl;
return 0;
}
28. ((CC++++)) Definisati klasu Student koja ima privatne podatke brojIndeksa (ceo broj), ime (string, ne duži od 30 znakova) i ocene (dinamički niz koji ima brOcena elemenata). Napisati
a) Konstruktor Student(int n) kojim se niz ocena kreira tako da ima n elemenata.
b) Konstruktor Student(int brInd, char * ime, int n) kojim se postavljaju indeks i ime studenta, kao i broj ocena.
c) Geter i seter za broj indeksa i ime, kao i metode int getOcena(int i) i void setOcena(int i, int oc) kojima se omogućava čitanje i postavljanje i – te ocene. Voditi računa o tome da li je indeks u granicama za broj ocena, kao i da li je vrednost na koju se ocena postavlja između 5 i 10.
d) Destruktor.
e) Metod double prosek() koji kao rezultat daje prosečnu ocenu studenta. Ukoliko student nema ni jednu ocenu, rezultat je 0.
29. ((JJAAVVAA)) Kreirati klasu BrojacZnakova koja čuva ulazni tekst (kao string) i može da napravi statistiku o tome koji se znak koliko puta javlja. Tekst se može zadavati i naknadno, a statistika mora uvek biti ispravna. Napisati:
a) Konstruktor BrojacZnakova(String s).
b) Odgovarajuće getere i setere.
151
c) Metod void dodaj(String s) koji na postojeći tekst dodaje string s.
d) Netod int brojPojava(char c) koji omogućava dobijanje broja pojavljivanja datog znaka.
e) Metod void ponisti() koji poništava celu dotadašnju statistiku.
f) Metod char[] top10() koji kao rezultat daje niz od 10 (ili manje, ukoliko nema dovoljno znakova) znakova koji se najčešće javljaju, uređen u nerastući redosled po broju pojava.
152
Domaći II
1. Саставити на језику C++ следеће класе (класе опремити оним конструкторима,
деструктором и оператором за доделу вредности, који су потребни за безбедно
коришћење класа; у случају грешке прекидати програм):
a. Апстрактна територијална јединица има назив (знаковни низ). Може да се
дохвати једнословна ознака врсте, да се одреди број становника и да се упише у
излазни ток (it<<jed) у облику naziv:vrsta:brojSt:.
b. Насеље је једноставна територијална јединица у којој живи задати број
становника. Ознака врсте је 'N'.
c. Апстрактна област је територијална јединица која садржи задат број других
територијалних јединица. Ствара се празна након чега јој се јединице додају
једна по једна (obl+=jed; прекорачење капацитета је грешка) уз проверу да ли се
јединица сме додати. Проверу одређују конкретне области. Додата јединица не
постане власништво области већ се само памти њена адреса (показивач). Број
становника области је једнак збиру броја становника садржаних јединица.
Може да јој се одреди површина. У излазни ток се пише у облику
naziv:vrsta:brojSt:povrs[jed,…,jed], где jed шредставља резултат писања једне
јединице.
d. Општина је област која садржи само насеља и има задату површину. Ознака
врсте је 'O'. Округ је област која садржи само општине. Површина области је
једнака збиру површина садржаних општина. Ознака врсте је 'N'. Покушај
додавања неодговарајуће јединице је грешка.
Написати на језику C++ програм који формира пример једног округа са две општине, од
којих свака општина има по два насеља, а затим испише округ на главном излазу.
Користити константне податке (не треба ништа читати с главног улаза).
2. Саставити на језику C++ следеће класе (класе опремити оним конструкторима,
деструктором и оператором за доделу вредности, који су потребни за безбедно
коришћење класа):
a. Апстрактном геометријском телу може да се дохвати једнословна ознака врсте
тела, да се израчуна запремина и да се направи динамичка копија. Предвидети
и могућност уписа произвољног конкретног геометријског тела у датотеку
(dat<<t).
b. Сфера је предмет задат полупречником (подразумевано 1). Ознака врсте
премета је S, запремина је 4r3π/3 и у датотеку се пише у облику S(r).
c. Ваљак је предмет задат полупречником основице и висином (подразумевано 1
и 1). Ознака врсте премета је V, запремина је r2πh и у датотеку се пише у облику
V(r,h).
d. Ред тела ограниченог капацитета се ствара празан задатог капацитета
(подразумевано 5). Може да се испита да ли је ред пун и да ли је празан, да се
стави једно тело на крај реда (red+=pr; покушај стављања у пун ред прекида
програм), да се узме тело са почетка реда (покушај узимања из празног реда
прекида програм) и да се садржај реда упише у датотеку (dat<<red) у облику
red[t,t,…,t], где су t резултати писања појединих садржаних тела.
153
Написати на језику C++ главни програм који направи ред капацитета који прочита са
главног улаза, чита разноврсна тела са главног улаза и ставља их у ред све док се ред не
напуни, испише садржај реда на главном излазу, узимајући тела из реда док се ред не
испразни израчуна средњу запремину свих тела у реду, испише добијени резултат на
главном излазу и понавља претходне кораке док за капацитет реда не прочита
недозвољену вредност.
3. Саставити на језику C++ следеће класе (класе опремити оним конструкторима,
деструктором и оператором за доделу вредности, који су потребни за безбедно
коришћење класа):
a. Особа има име и године старости. Може да се испита да ли је једна особа
старија од друге (osoba1>osoba2) и да се особа упише у излазни ток (it<<osoba) у
облику ime(god), где су: ime – име особе и god – године старости. Особа не сме
да се копира ни на који начин.
b. Студент је особа који може да полаже највише задати број испита
(подразумевано 20) за које се памте само оцене. Ствара се без иједне оцене,
после чега се оцене додају једна по једна (student+=ocena). Покушај додавања
превише оцена прекида програм. Може да се испита колико оцена може још да
се дода, да се израчуна средња вредност положених испита. У излазни ток се
пише у облику ime(god)/sred, где је sred – средња оцена положених испита.
c. Именик може да садржи задат број (подразумевано 10) особа произвољне
врсте. Ствара се празан после чега се особе додају једна по једна
(imenik+=&osoba). Препуњавање именика прекида програм. Може да се
дохвати капацитет именика, број особа у именику, да се именик уреди по
опадајућем редоследу старости особа у њему и да се упише у излазни ток
(it<<imenik), тако што се свака особа пише у посебном реду. Именик не сме да
се копира ни на који начин.
Написати на језику C++ програм који читајући потребне податке с главног улаза направи
именик који садржи известан број особа произвољне врсте, уреди именик по опадајућем
редоследу старости особа у њему и испише именик на главном излазу.
4. Написати на језику C++ следеће класе (класе опремити оним конструкторима,
деструктором и оператором за доделу вредности, који су потребни за безбедно
коришћење класа; грешке пријављивати изузецима типа једноставних класа које су
опремљене писањем текста поруке):
a. Апстрактно превозно средство је описано максималном брзином, дужином,
површином и сопственом тежином. Може да му се дохвати тренутна тежина и
једнословна ознака врсте. Има јединствен, аутоматски генерисан,
идентификациони број који може да се дохвати. Превозно средство може да се
упише у излазни ток (it<<sredstvo) у облику
врста:ид(максималнаБрзина,дужина,површина,тежина).
b. Аутомобил је превозно средство које је додатно описано бројем седишта.
Ознака врсте је 'A'.
c. Апстрактaн транспортер је превозно средство задате носивости.
d. Генерички стек може да садржи задат број елемената одређеног типа. Може да
се дохвати број елемената на стеку, да се стави елемент на стек (stek+=elem), да
се узме последње стављени елемент са стека (--stek), да се дохвати елемент са
154
задатим редним бројем од врха стека (stek[ind]) и да се садржај стека упише у
излазни ток (it<<stek) почев од врха стека у облику елемент|…|елемент. Грешка
је ако се покуша ставити више елемената од капацитета стека и ако се покуша
узети елемент са празног стека.
e. (20 поена) Шлепер је транспортер који може да садржи аутомобиле на стеку
задатог капацитета. Збир дужина садржаних аутомобила мора бити мањи од
80% сопствене дужине, а збир њихових тежина мањи или једнак носивости.
Ознака врсте је 'S'. У тренутну тежину шлепера улази и тежина садржаних
аутомобила. У излазни ток се, поред општих података за сва превозна средства,
пише и садржај шлепера унутар пара угластих заграда: [садржај].
f. Трајект је транспортер који може да садржи превозна средства на стеку задатог
капацитета. Збир површина садржаних превозних средстава мора бити мања од
70% сопствене површине, а збир њихових тежина мањи или једнак носивости.
Грешка је ако се покуша стављање трајекта на трајект. Ознака врсте је 'Т'. У
тренутну тежину трајекта улази и тежина садржаних превозних средстава. У
излазни ток се, поред општих података за сва превозна средства, пише и
садржај трајекта унутар пара угластих заграда: [садржај].
Написати на језику C++ програм који напуни један трајект са неколико аутомобила и
неколико шлепера од којих сваки има по неколико аутомобила и испише трајект на
главном излазу. Користити константне параметре (не треба ништа учитавати).
5. Написати на језику C++ следеће класе (класе опремити оним конструкторима,
деструктором и оператором за доделу вредности, који су потребни за безбедно
коришћење класа; грешке пријављивати изузецима типа класа које су оспособљене за
исписивање текста поруке):
a. Апстрактан генератор случајних бројева на сваки захтев даје један
псеудослучајан реалан број по некој расподели.
b. Генератор униформне расподеле на основу задате 32-битне целобројне клице k
и реалних граничних вредности min и max, задатих у тренутку стварања, може
да врати случајну целобројну вредност у опсегу [0,232) и реалну вредност у
опсегу [min,max). При сваком захтеву за следећим случајним бројем вредност
клице се мења по формули k = (314159621 k + 907633385) mod 232. Враћена
случајна целобројна вредност је једнака новој вредности клице, а реална
вредност се добија пресликавањем нове вредности клице на поменути опсег.
c. Апстрактан посматрач може да се доведе у почетно стање, да се стање
посматрача ажурира на основу једног реалног податка, да се дохвати један
реалан податак који представља тренутно стање посматрача и да се назив врсте
посматрача упише у датотеку (dat<<posm).
d. Минимум, максимум и средња вредност су посматрачи који као резултат
одређују најмању, највећу, односно средњу вредност достављених података од
последњег довођења у почетно стање. Грешка је ако се затражи дохватање
резултата, а није био достављен ни један податак.
e. Апстрактан субјекат посматрања може да достави један реалан податак свим
пријављеним посматрачима ради ажурирања њихових стања. Ствара се празан
после чега се посматрачи пријављују (subj+=&posm) и одјављују (subj-=&posm)
један по један. Пријављени посматрачи нису власништво субјекта.
155
f. Статистички анализатор је субјекат који помоћу одређеног генератора случајних
бројева може да генерише одређен број случајних бројева и те бројеве, један
по један, достави свим пријављеним посматрачима.
Написати на језику C++ програм који направи један статистички анализатор, пријави му по
један примерак од све три врсте посматрача, направи статистичку анализу 10000
униформних случајних реалних бројева у опсегу од −1 до 5 и испише добијене резултате на
главном излазу.
6. Написати на језику C++ следеће класе (класе опремити оним конструкторима,
деструктором и оператором за доделу вредности, који су потребни за безбедно
коришћење класа; грешке пријављивати изузецима типа једноставних класа које су
опремљене писањем текста поруке):
a. Јединица мере има ознаку која може да се дохвати. Дозвољене ознаке су:
"kom", "l", "m" и "kg". Грешка је ако се покуша направити јединица мере с
другачијом ознаком. Јединица мере може да се упише у излазни ток (it<<jed)
кад се пише ознака јединице.
b. Апстрактан артикал има назив и јединицу мере који могу да се дохвате. Два
артикла се сматрају истим ако имају исти назив (art1==art2). Артикал може да се
упише у излазни ток (it<<art) кад се пише назив артикла.
c. Млеко и шећер су артикли који се мере у литрима, односно у килограмима.
d. Запис о артиклу има артикал, количину и јединичну цену артикла. Могу да се
дохвате атрибути записа, да се промени количина, да се промени јединична
цена и да се израчуна вредност укупне количине артикла у запису. Запис може
да се упише у излазни ток (it<<zap) када се пише артикал, количина, јединица
мере, јединична цена и вредност артикла.
e. Генерички низ може да садржи задат број елемената одређеног типа. Ствара се
празан задатог капацитета после чега се елементи могу додавати један по један
(niz+=elem; капацитет низа се по потреби аутоматски повећава за 10%, али бар
за 5 места). Може да се дохвати број елемената у низу, да се дохвати елемент са
задатим редним бројем (niz[ind]; грешка је ако је индекс изван опсега) и да се
садржај низа упише у излазни ток (it<<niz), по један елемент у сваком реду.
f. Складиште има назив и адресу и садржи генерисан низ записа о артиклима.
Ствара се празно капацитета 5 записа, после чега се записи додају један по
један (sklad+=zap; грешка је ако већ постоји запис за исти артикал). Може да се
дохвати запис за задати артикал (sklad[art]; грешка је ако такав запис не постоји)
и да се израчуна укупна вредност артикала у складишту.
Написати на језику C++ програм који направи једно складиште, стави у њега неколико
артикала и испише на главном излазу укупну вредност артикала у складишту. Користити
константне податке (не треба ништа учитавати).
7. Написати на језику C++ следеће класе (класе опремити оним конструкторима,
деструктором и оператором за доделу вредности, који су потребни за безбедно
коришћење класа):
156
a. Симбол садржи један знак (char) и однос ширине и висине при исцртавању
знака. Може да се дохвати садржани знак и да се одреди ширина исцртавања за
дату висину.
b. Фонт садржи произвољан број симбола. Ставара се празан после чега се
симболи додају један по један. Ако већ постоји симбол за дати знак, стари
симбол се замењује новим. Може да се дохвати број симбола у фонту и да се
нађе симбол који садржи задати знак (резултат је адреса симбола – нула ако
знак не постоји).
c. Вектор у равни се задаје реалним координатама. Може да се вектору дода
други вектор и да се упише у излазни ток (vekt.pisi(it)) у облику "(x,y)".
8. Саставити на језику C++ следеће класе (класе опремити оним конструкторима, деструктором и оператором за доделу вредности, који су потребни за безбедно коришћење класа; грешке пријављивати изузецима типа једноставних класа које су опремљене писањем текста поруке):
Датум се задаје помоћу дана, месеца и године. При стварању датума није потребно проверавати исправност параметара. Компоненте датума могу да се дохвате. Датум може да се упише у датотеку (dat<<datum).
За особу се зна име и датум рођења. Подаци о особи могу да се дохватају, да се промене и могу да се упишу у датотеку (dat<<osoba).
Испит се задаје помоћу једнословне шифре предмета, оцене и датума полагања испита. Подаци о испиту могу да се дохватају.
(20 поена) Генеричка листа података неког типа се ствара празна после чега се подаци додају један по један на крај листе (lst+=pod). Може да се постави на први елемент листе, да се прелази на следећи елемент, да се испита да ли постоји текући елемент, да се приступи податку у текућем елементу са могућношћу промене вредности податка и да се текући елемент избаци из листе (при томе следећи елемент постаје текући). Грешка је ако текући елемент не постоји у моменту покушаја приступања елементу или избацивања елемента.
Ђак је особа која је полагала известан број испита. Ствара се са празном листом испита, после чега се поједини испити додају један по један (djak+=ispit). Може да се одреди средња оцена положених испита. У датотеку се поред општих података пише и средња оцена положених испита.
Школа има једнословну ознаку и садржи листу ђака који похађају школу. Ствара се празна, после чега се ђаци додају један по један (skola+=djak). Може да се дохвати ђак са највећом средњом оценом положених испита без могућности промене података о том ђаку.
Саставити на језику C++ главни програм који направи школу са два ђака са по три полагана испита и испише на главном излазу податке о ђаку са бољим просеком.
9. Написати на језику C++ следеће класе (класе опремити оним конструкторима, деструктором и оператором за доделу вредности, који су потребни за безбедно коришћење класа; грешке пријављивати изузецима типа класа које су оспособљене за исписивање текста поруке):
Боја се задаје помоћу реалних интензитета црвене, зелене и плаве боје у опсегу од 0 до 1 (подразумевано (1,1,1), тј. бела боја). Грешка је ако је вредност интензитета неке од
157
боја изван наведеног опсега. Боја може да се упише у датотеку (dat<<boja) у облику (c,z,p), где су: c, z и p – интензитети компонентних боја.
Тачка у равни се задаје помоћу реалних координата (подразумевано (0,0)) и могу да јој се дохвате вредности координата.
Апстрактна попуњена фигура у равни има јединствен, аутоматски генерисан идентификациони број и боју. Може да се дохвати њен идентификациони број и боја, да се испита да ли јој припада нека задата тачка (сматра се да тачка припада фигури ако лежи у унутрашњости или на ивици фигуре) и да се сазна које је боје задата тачка која припада фигури (грешка је ако наведена тачка не припада фигури).
Правоугаоник је фигура која има ивице паралелне координатним осама и задаје се помоћу две тачке које чине доње лево и горње десно теме (подразумевано (0,0) и (1,1)).
Круг је фигура која се задаје помоћу полупречника и тачке која представља центар круга (подразумевано полупречника 1 са центром у тачки (1,1)).
Генерички низ се ствара празан задатог капацитета почетног капацитета (подразумевано 10) после чега се елементи додају један по један (niz+=elem). Ако се низ препуни, капацитет му се поваћа за 5 елемената. Може да се дохвати број елемената низа и да се приступа елементу са датим редним бројем (niz[ind]; грешка је ако је индекс изван опсега).
Цртеж је правоугаоник који може да садржи произвољан број фигура у облику генеричког низа. Боја фигуре представља боју подлоге цртежа. Координате садржаних фигура рачунају се у односу на доње лево теме цртежа. У случају преклапања фигура, касније додата фигура прекрива раније додату фигуру (боју тачке у цртежу одређује фигура која последња додата цртежу и садржи ту тачку – ако такве фигуре нема, узима се боја подлоге).
Написати на језику C++ програм који направи један цртеж од неколико фигура константних параметара (не треба ништа учитавати) и после читајући неколико парова координата тачака исписује боју тих тачака у цртежу.
10. Написати на језику C++ следеће класе (класе опремити оним конструкторима, деструктором и оператором за доделу вредности, који су потребни за безбедно коришћење класа; грешке пријављивати изузецима типа једноставних класа које су опремљене писањем текста поруке):
Апстрактним мерљивим појмовима може да се одреди тежина и да се дохвати назив врсте.
Мерљива особа има име и тежину, који могу да се дохвате. Може да се дохвати име и да се упише у излазни ток (it<<osoba) у облику име(тежина).
Мерљив теретни контејнер има јединствен, аутоматски генерисан регистарски број и тежину када је празан. У контејнер је могуће сместити товар задате тежине (kont+=tez) и извадити товар задате тежине (kont-=tez). Може да се дохвати регистарски број и сопствена тежина и да се упише у излазни ток (it<<kont) у облику регБрој(укупнаТежина).
Генерички низ садржи задат број (подразумевано 10) елеменатa типа који је једини параметар шаблона. Приликом стварања сви елементи се поставе на подразумевану (празну) вредност. Може да се дохвати дужина низа и да се приступа елементу са датим редним бројем (niz[ind]). Грешка је ако је индекс изван опсега.
Апстрактан авион има ознаку (низ од 5 знакова), максималну тежину, тежину када је празан и садржи низ од задатог броја показивача на мерљиве појмове. Авион не сме да се копира ни на који начин. Може да се стави неки појам на задато место у низу (avion.stavi(ind,&pojam)), да се уклони појам са задатог места (avion.ukloni(ind)), да се израчуна тренутна тежина авиона и да се упише у излазни ток (it<<avion) у облику
158
ознака(тренутнаТежина)[појам,…,појам]. Грешка је ако се покуша претоварити авион, ставити нешто на попуњено место или уклањати нешто с празног места.
Путнички авион може да превози само путнике, а треттни авион може да превози само контејнере. Грешка је ако се покуша додати појам неодговарајуће врсте.
Написати на језику C++ програм који направи један путнички авион са три путника и један
теретни авион са два контејнера, све са константним параметрима (не треба ништа учитавати) и
после испише та два авиона на главном излазу.
11. Коришћењем класа из првог дела, написати на језику C++ следеће класе (класе опремити оним конструкторима, деструктором и оператором за доделу вредности, који су потребни за безбедно коришћење класа):
Апстрактно возило има задату сопствену тежину. Може да се дохвати назив врсте возила, да се одреди тежина возила и да се возило упише у излазни ток (it<<v). Пише се назив врсте возила и сопствена тежина возила.
Бицикл је возило. Камион је возило задате сопствене тежине и носивости. Ствара се без товара после чега
може да се дода товар задате тежине (k+=t) и да се скине товар задате тежине (k-=t). Ако се камион претовари, вишак терета се одбацује. Скидањем превише товара тежина товара постане једнака нули. У излазни ток се пише и тренутна тежина товара на камиону.
12. Коришћењем класа из првог дела, саставити на језику C++ следеће класе (класе
опремити оним конструкторима, деструктором и оператором за доделу
вредности, који су потребни за безбедно коришћење класа):
Апстрактни чвор графа има једнословну ознаку. Може да се дохвати ознака
чвора, да се одреди реална величина чвора и да се ознака чвора упише у
датотеку (dat<<cvor).
Географски чвор је чвор који садржи једно географско место. Величина чвора је
обим контуре места. Може да се дохвати центар садржаног места. У датотеку се
пише име места и обим контуре.
Апстрактна грана графа има једнословну ознаку и спаја два чвора графа. Може
да се дохвати ознака гране, да се одреди реална дужина гране и да се грана
упише у датотеку (dat<<grana) у облику ozn(poc,kra), где су ozn – ознака гране
а poc и kra – ознаке почетног и крајњег чвора гране. Грана не сме да се копира.
Географска грана је грана која спаја два географска чвора. Дужина гране је
растојање између центара места на крајевима гране.
13. Написати на језику C++ следеће класе (класе опремити оним конструкторима,
деструктором и оператором за доделу вредности, који су потребни за безбедно
коришћење класа; грешке пријављивати изузецима типа једноставних класа које
су опремљене писањем текста поруке):
Позиција у једнодимензионалном (линијском) простору се дефинише реалним
бројем x који означава растојање од координатног почетка. Може да се дохвати
вредност x и одреди растојање од друге позиције. Позиција може да се упише у
излазни ток (it<<pozicija) у облику (x).
Апстрактна честица поседује јединствен аутоматски генерисан
идентификациони број. Задаје се позицијом у линијском простору. Може да се
дохвати једнословна ознака врсте честице и позиција честице. Честици могу да
се одреде маса и наелектрисање као и да се израчуна сила привлачења са
159
задатом честицом (негативна вредност означава одбијање). Честица може да се
упише у излазни ток (it<<cestica) у облику
врста:ид[маса,наелектрисање,позиција] .
Е-честица има задато наелектрисање, док је њена маса 0. Сила привлачења се
израчунава по Кулоновом закону: FE = −kc⋅q1⋅q2/r2, kc ≈ 8,99⋅109. Једнословна
ознака врсте је 'Е'.
М-честица има задату масу, док је њено наелектрисање 0. Сила привлачења се
израчунава по закону гравитације: FG = γ⋅m1⋅m2/r2, γ ≈ 6,67⋅10−11. Једнословна
ознака врсте је 'M'.
О-честица има задату масу и наелектрисање. Сила привлачења је збир сила по
Кулоновом закону и закону гравитације. Једнословна ознака врсте је 'О'.
Генеричка листа може да садржи произвољан број елемената одређеног типа.
Може да се дохвати број елемената листе, да се дода елемент на крај листе
(lista+=elem), да се дохвати елемент са задатим редним бројем од почетка
листе (lista[ind]) и да се садржај листе упише у излазни ток (it<<lista) почев
од првог елемента у облику елемент|…|елемент. Грешка је ако не постоји
елемент који одговара задатом индексу приликом дохватања.
Експериментална соба садржи листу честица и задату експерименталну
честицу. Након стварања, честице се додају једна по једна. Грешка је ако се
покуша додавање честице на истој позицији као и експериментална честица. У
излазни ток се уписује експериментална честица и садржај листе у облику
експ_честица{листа}. Резултат експеримента је интензитет силе којом додате
честице делују на експерименталну (позитивна вредност означава силу у правцу
x осе). Интензитет силе се израчунава као збир интензитета сила којом делују
честице из листе.
Написати на језику C++ програм који направи једну експерименталну собу у коју дода
неколико честица а затим испише резултат експеримента на главном излазу. Користити
константне параметре (не треба ништа учитавати).
14. Prodavnica prodaje kompjutere i softver. Kompjuter može biti desktop ili laptop. Date su
klase Proizvod, Kompjuter, Desktop, Laptop i Software.
a. U svaku od klasa dodati metod prikazi() koji kao rezultat daje string sa podacima
o proizvodu.
b. Kreirati formu koja na sebi ima dve liste – listu proizvoda u prodavnici, kao i listu
izabranih proizvoda. Omogućiti izbor označenih proizvoda prebacivanjem u listu
izabranih proizvoda, kao i odustajanje od kupovine određenog proizvoda. Dodati
labelu koja u svakom trenutku pokazuje tačnu ukupnu cenu odabranih proizvoda.
c. Dodati dugme za brisanje postojećeg proizvoda, s’ tim što se proizvod ne može
obrisati ukoliko ga je kupac izabrao.
d. Dodati dugme za dodavanje novog proizvoda. Ovo dugme bi trebalo da prikazuke
novi prozor u kome je padajuća lista iz koje korisnik prvo treba da odabere tip
proizvoda (kompjuter/software). Ukoliko izabere kompjuter, treba mu prikazati još
jednu padajuću listu u okviru koje bira da li se radi o dektop ili laptop kompjuteru.
Posle izabranog tipa treba prikazati polja za unos odgovarajućih podataka o
proizvodu. Ovaj prozor treba da sadrži i dugme za potvrdu, odnosno dugme za
odustajanje od unosa novog proizvoda.
160