Upload
nadiira-nadira
View
272
Download
2
Embed Size (px)
Citation preview
8/3/2019 Zeljko Juric
1/18
Zeljko Juric: Principi programiranja /kroz program ski jezik C++/
Radni materijali - akademska godina 2009/10
15. Prenos parametara u potprograme
Upotreba globalnih promjenljivih eesto moze dovesti do gresaka u programima koje se tesko
otkrivaju, s obzirom da imje vidokrug izuzetno velik, tako daje velika i mogucnost njihove upotrebe na
pogresan naein. Takoder, upotreba globalnih promjenljivih u vise razlieitih potprograma dovodi do
stvaranja neprirodne zavisnosti izmedu potprograma, koji ovise od imena dijeljenih globalnihpromjenljivih. Medutim, do sada nam je upotreba globalnih promjenljivih bila jedini naein da izvrsimo
razmjenu injormacija izmedu vise razlieitih potprograma. Srecom, C++ nudi mnogo prakticniji i
sigurniji naCin razmjene informacija izmedu potprograma zasnovan na tzv. prenosu parametara, koji ne
dovodi do stvaranja zavisnosti izmedu pojedinih potprograma.
Potrebu za prenosom parametara ilustriraeemo na primjeru sljedeeeg programa koji racuna i stampa
obim i povrsinu kruga:
#include
using namespace std;
const double PI(3.141592654);
double poluprecniki
I I Stampa obim i povrsinu kruga sa poluprecnikom "poluprecnik"
void ProracunajKrug() {
cout "Obim: " 2 * PI * poluprecnik endl "Povrina: ", PI * poluprecnik * poluprecnik endli
int main() {
cin poluprecnik;ProracunajKrug();
return 0;
II Glavni program
lako nema nikakve sumnje da ovaj program radi ispravno, u njemu se mogu uoeiti i brojni problemi,
koji nisu vezani za njegovo junkcioniranje, vee za njegovu strukturu i mogucnost prilagoaavanja. Na
prvom mjestu, komunikacija izmedu glavnog programa i potprograma "Proracunaj Krug" ostvarena je
preko zajednicke globalne promjenljive "poluprecnik". Ukoliko se njeno ime promijeni, potprogram
eeimati gresku, s obzirom da se oslanja na vrijednost nedeklarirane promjenljive. U nekom slozenijem
okruZenju, moglo bi se cak desiti da izmjena imena promjenljive dovede do toga (sto je jos gore) da
program formalno nema sintaksnu gresku, ali da radi pogresno (to se, na primjer, moze desiti ukoliko se
u nekom drugom potprogramu neocekivano upotrebi ista promjenljiva, a da te cinjenice ovaj potprogram
nije svjestan). To nije ono sto zaista zelimo. Nairne, potprogram bi trebao biti neovisna jedinica kOda,
neovisna od ostatka programa koliko god je to moguce. Trebali bismo biti veoma pazljivi, i moZda
bismo morali izmijeniti dio kOda, ukoliko bismo zeljeli da potprogram "Proracunaj Krug" prosto
upotrijebimo u nekom drugom programu. Medutim, dobro napisan potprogram ne bi trebao nista da
"zna" 0 tome sta se nalazi u ostatku programa, niti kako ee i gdje ee on biti upotrebljen u ostatku
programa. Dobro napisan potprogram trebao bi sarno da "radi" posao koji mu je povjeren, bez"razmisljanja" 0tome kome i zasto je taj posao potreban.
Na sliean problem nailazimo i ukoliko zelimo prosiriti nas program koji ispisuje pozdrav na ekranu,
tako da mozemo zadati koliko puta zelimo da se ispiSe rijce "Pozdrav!". Ovo zadavanje trebamo izvrsiti
u glavnom programu, koji poziva potprogram "IspisiPozdrav", prije njegovog poziva. Medutim,
potprogram "IspisiPozdrav" mora imati nacin da sazna ovu vrijednost. Ukoliko se ograniCimo sarno
na one sto smo do sada utvrdili, jedino sto mozemo uraditi je da iskoristimo globalne promjenljive, kao
u sljedeeem primjeru:
XV-I
8/3/2019 Zeljko Juric
2/18
Zeljko Jurie: Principi programiranja !kroz program ski jezik C++/
Radni materijali - akademska godina 2009/10
#include
using namespace std;
int broj_ponavljanja;
void IspisiPozdrav() {
for(int i = 1; i
8/3/2019 Zeljko Juric
3/18
Zeljko Juric: Principi programiranja /kroz programski jezik C++/
Radni materijali - akademska godina 2009/1 0
popis parametara
~void ProracunajKrug(double r)
t formalni parametar
S druge strane, stvami parametri se navode prilikom poziva potprograma sa ciljem da inicijalizirajuodgovarajuce formalne parametre:
ProracunajKrug(poluprecnik)
tstvarni parametar
U navedenom primjeru, stvami parametar "poluprecnik" kopira se u formalni parametar "r". S
obzirom da se stvami parametri navode prilikom poziva, a isti potprogramje moguce pozvati koliko god
puta zelimo, u svakom pozivu moguce je zadati drugaeije vrijednosti stvamih argumenata, odnosno
stvami argumenti nisu jedinstveno odreaeni sarnim potprogramom. U tom slueaju, prilikom svakog
izvrsavanja potprograma, formalni parametri ce imati drugaeije poeetne vrijednosti (odredene
vrijednostima stvamih parametara).
Za razliku od formalnih parametara koji su promjenljive, stvami parametri ne moraju biti
promjenljive, vec mogu biti bilo koje vrijednosti ispavnog tipa, sto ukljueuje promjenljive, konstante iIi
izraze. Stoga su pozivi poput sljedecih sasvim korektni (uz pretpostavku da postoji i promjenljiva
"precnik" tipa "double"):
ProracunajKrug(poluprecnik);
ProracunajKrug(7 * 3.18 - 2.27);ProracunajKrug(3.15);
ProracunajKrug(precnik / 2);
Broj stvamih parametara koji se prenose u potprogram mora bili jednak broju formalnih parametara
(u nasem primjeru jedan), osim u slucaju postojanja tzv. podrazumijevanih parametara, 0 kojima cemo
govoriti nesto kasnije. Svaki stvami parametar mora odgovarati po tipu odgovarajucem formalnomparametru. Na primjer, u prethodnom primjeru, formalni parametar "r" je tipa "double", isto kao i
stvarni parametar "poluprecni k".Neslaganje u tipu parametara je dozvoljeno jedino u slucajevima u
kojima je podrzana automatska konverzija tipa iz tipa stvamog parametra u tip formalnog parametra. Na
primjer, ako je formalni parametar tipa "doub1e", stvami parametar moze biti tipa "int",s obzirom da
postoji automatska konverzija (promotivne prirode) iz tipa "int" u tip "doub1e". Nacelno je moguce i
obmuto, tj. proslijediti stvami parametar tipa "doub1e" potprogramu koji posjeduje formalni parametar
tipa "int", s obzirom da je takoder podrZana i automatska konverzija iz tipa "doub1e" u tip "int".
Medutim, moramo biti svjesni da ce u ovom slucaju doci do odsjecanja decimala prilikom prenosa
stvamog parametra u formalni.
Potprogram "Stampaj PrazneLinij e" u sljedecem primjeru ispisuje "n" praznih linija, gdje je "n"
formalni parametar:
void StampajPrazneLinije(int n) {
for(int i = 1; i
8/3/2019 Zeljko Juric
4/18
Zeljko Jurie: Principi programiranja Ikroz program ski jezik C++/
Radni materijali - akademska godina 2009/1 0
jezik C++ podnava i drugi naein prenosa parametara, nazvan prenos po referenci (engl. passing by
reference), mada u jeziku c++ ova razlika u prenosu nije toliko striktne forme koliko u nekim drugim jezicima kao sto je Pascal. Strogo reeeno, C++ zapravo i ne podrZava prenos po referenci, ali se on moze
simulirati tako sto se kao formalni parametar upotrijebi specijalna vrsta objekata nazvana referenca. 0
ovome cemo govoriti u kasnijim poglavljima.
Pored termina parametar, koristi se i termin argument. Mada su termini parametar i argumentsinonimi, u literaturi se veoma eesto kada se upotrijebi termin "parametar" bez konkretne specifikacije
da li se radi 0formalnom iii stvamom parametru eesce misli naformalni parametar, dokje pri upotrebi
termina argument obmuto (tj. eesce se misli na stvami argument). Na primjer, dosta eesto se govori 0
"parametrima koje potprogram prima", odnosno 0 "argumentima koje prosljedujemo potprogramu".
U slueaju kada potprogram ima vise parametara, i stvami i formalni parametri razdvajaju se
zarezom. Pri tome se pri navodenjuformalnih parametara tip svakog od njih mora navesti odvojeno, kao
u sljede6em primjeru programa:
#include
using namespace std;
void IspisiZbir(int p, int q) {cout p + q;
int main() {
IspisiZbir(3, 2);
return 0;
Ovaj program ce, naravno, ispisati broj "5". S druge strane, sljede6a definicija nije ispravna:
void IspisiZbir(int p, q) {
cout p + q;
Razlog za ovu prividnu nekonzistentnost izmedu naeina deklaracije formalnih parametara i deklaracije
obienih lokalnih promjenljivih lezi u einjenici da se, pod odredenim okolnostima, imena formalnih
parametara mogu izostaviti, tako daje neophodna individualna tipizacija svakog formalnog parametra.
Formalni i stvami parametar u principu mogu imati ista imena, ali treba voditi raeuna da se i u tom
slueaju radi 0 razliCitim objektima, a da su njihova istovjetna imena samo stvar slueaja. Dakle, eak i
ukoliko formalni i stvarni parametar slueajno imaju isto ime (npr. "n"), formalni parametar "n" je
neovisan od stvamog parametra "n", mada se pri pozivu potprograma "IspisiPozdrav" stvami
parametar "n" kopira u formalni parametar "n". Ovo je ilustrirano u sljede6em (potpuino legalnom)
primjeru, koji predstavlja modificirani program za ispis pozdrava zadani broj puta, uz tehniku prenosa
parametara:
#include
using namespace std;
void IspisiPozdrav(int n) {
for(int i = 1; i
8/3/2019 Zeljko Juric
5/18
Zeljko Jurie: Principi programiranja Ikroz program ski jezik CHI
Radni materijali - akademska godina 2009/1 0
Ovdje se prilikom poziva potprograma "IspisiPozdrav" stvarni parametar "n" (koji je lokalna
promjenljiva potprograma "main") kopira u jormalni parametar "n" (koji je lokalna promjenljiva
potprograma "IspisiPozdrav"). Sljedeei primjer ee nas uvjeriti da stvami i formalni parametri
predstavljaju razlicite objekte (iako se stvarni kopira u formalni) cak i kada imaju isto ime:
#include
using namespace std;
void Potprogram(int n)
cout n;n t= 3;cout n;
int main() {
int n(S);
cout n;Potprogram (n);
cout n;return 0;
Svoje rezonovanje provjerite tako sto eete odgovoriti na pitanje sta ispisuje ovaj program. Ispravan
odgovor je "5585". Takoder, radi provjere da li ste shvatili cinjenicu da su formalni i stvarni parametri
neovisni objekti probajte analizirati sta ee ispisati sljedeei (prilicno konfuzan) program. Ispravan
odgovor je "255225":
#include
using namespace std;
void Potprogram(int a, int b) {
cout a b;
int main() (
int a (2), b (S) ;
cout a b;Potprogram(b, a);
cout a b;
return 0;
Prenos parametara po vrijednosti moze se koristiti kada god zelimo prenijeti neku informaciju u
potprogram, pri cemu nas dalje ne zanima sta ee taj potprogram uraditi sa prenesenom informacijom (tj.
da Ii ee prenesena informacija pretrpiti izmjene iIi ne; to je privatna stvar potprograma). Osnovna svrha
parametara je da ucine potprogram generalnijim, tako da se on lakse moze ponovo iskoristiti u drugim
programima. Na primjer, u programu za ispis sahovske table imali smo potprogram nazvan
"Stampaj Zvj ezdice" koji je stampao "sirina_polj a" zvjezdica na ekranu, pri cemu je
"sirina _polj a" bila globalna promj enlj iva:
void StampajZvjezdice() {
for(int i = 1; i
8/3/2019 Zeljko Juric
6/18
Zeljko JO O e: Principi programiranja Ikroz program ski jezik CHI
Radni materijali - akademska godina 2009/1 0
Ovim je potprogram postao mnogo generalniji, jer omogucava stampanje onoliko zvjezdica koliko
zelimo, kada god to zatraZimo. Pri tome, zeljeni broj zvjezdica jednostavno zadajemo prilikom poziva
potprograma. Na primjer,
StampajZvjezdice(5);
Primijetimo da pored toga sto potprogram "Stampaj zvj ezdice" ne mora znati kako se zovupromjenljive u ostatku programa, onaj ko poziva potprogram (glavni program u nasem slucaju) takoder
ne mora nista da zna 0 tome kako se zove formalni parametar potprograma (to je "privatna stvar"
potprograma ).
U istom programu postoji takoder i potprogram nazvan "Stampaj Razmake" koji stampa nekoliko
razmaka. Njegov k6d identican je kao u potprogramu "Stampaj Zvj ezdice", izuzev sto na objekat
"cout" saljemo razmak, a ne zvjezdicu. Dodajuci jos jedan parametar, mozemo postici da isti
potprogram radi oba srodna posla:
II Stampa "br zn" znakova "znak"
void StampajZnakove(int br_zn, char znak) {for(int i = 1; i < = br_zn; i++) cout znak;
Ovo je jos generalniji potprogram, koji stampa proizvoljan broj bilo kojeg znaka koji zelimo. Na primjer,
pozivom
Stampaj Znakove (4, 'A');
odStampacemo cetiri slova 'A'. Isto tako, pozivom
StampajZnakove(sirina_polja, '*');
odstampacemo onoliko zvjezdica koliko iznosi trenutna vrijednost promjenljive "sirina _polj a".
Kada koristimo prototipove potprograma koji imaju parametre, njihova imena nije neophodno
navoditi i u prototipu, jer je kompajleru dovoljno da zna njihov broj i tip u trenutku kada se potprogram
poziva. Tako je sljedeei programpotpuno korektan:
#include
using namespace std;
int main (void) {void IspisiZbir(int, int);IspisiZbir(3, 2);return 0;
void IspisiZbir(int p, int q) {cout p + q;
Nije greska navesti imena parametara i u prototipu, tako da bi u prethodnom programu sasvim legalan
bio i prototip
void IspisiZbir(int p, int q);
U stiStini, kako su imena formalnih parametara potpuno nebitna kada se radi 0prototipu, kompajler
ih potpuno ignorira. Kao posljedica te cinjenice, imena parametara u prototipu potprograma i u stvamoj
defmiciji potprograma uopce se ne moraju slagati. Na primjer, u prethodnom programu bio bi bez
ikakvih problema prihvacen i prototip poput sljedeeeg (bez obzira sto se u definiciji potprograma
formalni parametri zovu "p" i "q"):
XV-6
8/3/2019 Zeljko Juric
7/18
Zeljko Juric: Principi programiranja /kroz programski jezik C++/
Radni materijali - akademska godina 2009/10
void IspisiZbir(int prvi sabirak, int drugi sabirak);
Opisanu osobinu programeri cesto koriste, dajuCi mnogo deskriptivnija imena parametrima u prototipu
nego u samoj realizaciji potprograma. Nairne, ukoliko je prototip dovoljno deskriptivan, njegovim se
posmatranjem cesto moze zakljuCiti sta potprogram radi, bez potrebe za analizom same realizacije
potprograma. S druge strane, upotreba isuvise dugackih naziva parametara u samom potprogramu bila bi
prilicno zamoma.
Sljedeei primjer ilustrira potprogram "Stampaj TablicuMnozenj a" sa dva parametra "m" i"n",
koji stampa tablicu mnozenja za sve proizvode oblika m x i pri cemu i uzima vrijednosti od 1 do n. Na
primjer, ako pozovemo ovaj potprogram pozivom "Stampaj TablicuMnozenj a (3, 5)",bice ispisana
tablica mnozenja za proizvode 3 x 1,3 x 2, 3 x 3, 3 x 4 i 3 x 5:
void StampajTablicuMnozenja(int m, int n)
for(int i 1; i
8/3/2019 Zeljko Juric
8/18
Ze1jko Jurie: Principi programiranja Ikroz programski jezik c++!Radni materijali - akademska godina 2009/10
U ovom programu, uz pomoc "setw" manipulatora ispisujemo odredeni broj praznih mjesta, da
bismo doveli poziciju za ispis ispod slova koje predstavlja odgovarajuci dan. Kako je svaka kolona
siroka 3 znaka, broj dodatnih praznina koje treba ispisati jednak je trostrukoj vrijednosti rednog broja
odgovarajuceg dana (rOOnibrojevi dana poeinju od nule, sto nam upravo odgovara, jer za slueaj da je
poeetni dan ponedjeljak, nikakvi dopunski razmaci nisu potrebni). Nakon toga, "for" petljom
ispisujemo sve brojeve redom od 1 do vrijednosti parametra "broj_dana", aZurirajuci pri tom svaki put
promjenljivu "pocetni _dan" tako da ukazuje na sljedeci dan, osim za slueaj kada je njena vrijednost"Nedj elj a". U tom slueaju, vrijednost promjenljive "pocetni dan" vraeamo nazad na vrijednost
"Ponedj elj ak", uz ispis jednog praznog reda, eime zapravo prelaZirno na ispis novog reda kalendara.
U prethodnom primjeru, deklarirali smo tip "Dani" sa globalnom vidljivoscu, samim tim sto smo ga
deklarirali izvan svih blokova. Stoga je ovaj tip dostupan i u potprogramu "Stampaj Kalendar" i u
glavnoj funkciji "main", sto nam je upravo i potrebno. Da smo tip "Dani" deklarirali unutar
potprograma "Stampaj Kalendar", on ne bi bio vidljiv nigdje izvan tijela tog potprograma, sto znaei
da pobrojana konstanta "Srij eda" ne bi bila vidljiva u "main" funkciji (tako da program ne bi uopee
radio). Treba napomenuti da se, za razliku od deklaracija globalnih promjenljivih, deklaracija tipova sa
globalnom vidljivoseu ne smatra stetnom, i eesto je veoma potrebna. Nairne, dok se informacije mogu
razmjenjivati izmedu potprograma putem prenosa parametara, ne postoji sliean naein kojim bi
potprogrami mogli moousobno razmjenjivatitipove,
stoga je upotreba tipova sa globalnom vidljivoscueesto jedino rjesenje.
Veoma j e vaZno napomenuti da iako j ezik C++ garantira da ee vrij ednosti svih stvamih parametara
biti izraeunate prije nego 8tO njihove vrijednosti budu proslijedene formalnim parametrima (odnosno
prije poeetka izvrSavanja potprograma), jezik C++ ne propisuje redoslijed kojim ce stvami parametri biti
izracunati. Mada redoslijOO izraeunavanja stvarnih parametara u veeini slueajeva uopee nije bitan, on
moze biti znaeajan u slueaju kada stvarni parametri predstavljaju izraze koji sadrZe bocne efekte. Na
primjer, razmotrimo sljedeei jednostavni potprogram koji prosto ispisuje vrijednosti svojih formalnih
parametara:
void Potprogram(int a, int b) {
cout a " " b endl;
Pretpostavimo sada da smo izvrSili sljedeei poziv:
int x (5) ;
Potprogram(x++, x++);
Kakav ee biti ispis nakon izvrsavanja ovog potprograma? Odgovor uopee nije propisan standardom
jezika C++, odnosno ispis se ne moze predvidjeti! Jedino sto je garantirano je da ee oba izraza "x++" biti
izvrSena, tako da ee na kraju promjenljiva "x" sigumo imati vrijednost 7. Medutim, nije definirano da Ii
ee prvo biti izraeunat lijevi ili desni izraz "x++". Ukoliko se prvo izraeuna lijevi izraz, u formalni
parametar "a" ee biti prenesena vrijednost 5 (ne zaboravimo da je vrijednost izraza "x++" vrijednost
promjenljive "x" prije uveeanja), au parametar "b" vrijednost 6, tako da ee ispis biti "5 6". Medutim,
ukoliko se prvo izraeuna desni izraz, dobieemo ispis "65". Ne smijemo pomisliti da problem nastajeiskljueivo zbog upotrebe dva boena efekta u istoj naredbi (8to se svakako smatra nOOozvoljenim). Nairne,
podjednako je problematiean i sljedeei poziv:
int x(5);
Potprogram(x++, x);
Nairne, nije tesko provjeriti da bi, u zavisnosti od redoslijeda izvrSavanja, mogli dobiti ispis "5 6" ili
"55". Sliene probleme bismo imali i u sljOOeeem pozivu, u kojem bismo, zavisno od redoslijeda
izraeunavanja stvarnih parametara, mogli dobiti ispis "46" iIi "7 4":
int x (5) ;Potprogram(x 4, x + 2);
XV-8
8/3/2019 Zeljko Juric
9/18
Zeljko June: Pnncipi programiranja /kroz program ski jezik C++/
Radni materijali - akademska godina 2009/10
Zbog spomenutih nedoumica, standardomjezika C++ je zabran}eno u nekom od stvamih parametara
koristiti promjenljivu nad kojom se u nekom drugom stvamom parametru istog potprograma obavlja
bocni efekat. Ova zabrana nije sintaksne prirode, tako da ee kompajler dopustiti ovakve pozive, ali
njihov efekat nije predvidljiv. Naravno da je standard jezika C++ mogao propisati redoslijed
izracunavanja stvarnih parametara (npr. slijeva nadesno). Meoutim, komitet za standardizaciju je
smatrao da je nametanje redoslijeda nepotrebno ogranicenje za autore kompajlera, s obzirom da postoje
racunarske arhitekture kod kojih je izracunavanje slijeva nadesno ejikasnije od izracunavanja zdesna
nalijevo, dok postoje i racunarske arhitekture kod kojih vrijedi obmuto. Nemojte pokusavati da utvrdite
koju strategiju koristi Vas kompajler, pa da se ubuduee oslanjate na utvroenu strategiju. Na prvom
mjestu, tako napisan program ne mora raditi ukoliko ga probate prevesti nekim drugim kompajlerom.
Sto je jos gore, po standardu kompajleri imaju puno pravo da u zavisnosti od okolnosti izaberu redoslijed
izracunavanja parametara. Drugim rijecima, ukoliko ste eksperimentiranjem utvrdili da kompajler koji
koristite izracunava parametre zdesna nalijevo, ne mora znaciti da on to radi uvijek. Nairne, mnogi
kompajleri mogu promijeniti svoj ustaljeni redoslijed izracunavanja parametara ukoliko u nekoj
konkretnoj situaciji zakljuce da ee promjena redoslijeda dovesti do efikasnijeg prevedenog kOda!
U jeziku C++ je podrzana moguenost da se prilikom pozivanja potprograma navede manji bro}
stvamih parametara nego sto iznosi broj formalnih parametara. Medutim, to je moguee sarno ukoliko se
u definiciji potprograma na neki nacin naznaci kakve ee pocetne vrijednosti dobiti oni formalni parametri
koji nisu inicijalizirani odgovarajueim stvarnim parametrom, s obzirom da formalni parametri uvijekmora}u biti inicijalizirani. Da bismo pokazali kako se ovo postize, razmotrimo sljedeei potprogram sa tri
parametra, koji na ekranu iscrtava pravougaonik od znakova sa visinom i sirinom koje se zadaju kao
prva dva parametra, dok treei parametar predstavlja znak koji ee se koristiti za iscrtavanje
pravougaonika:
void CrtajPravougaonik(int VlSlna, int sirina, char znak) {for(int i = Ii i
8/3/2019 Zeljko Juric
10/18
Zeljko Jurie: Principi programiranja /kroz program ski jezik C++/
Radni materijali - akademska godina 2009/1 0
iskoristena za njegovu inicijalizaciju, u slucaju da se odgovarajuci stvarni parametar izostavi. Stoga ce
prilikom navedenog poziva, formalni pararnetar "znak" dobiti vrijednost '*', tako da ce taj pozivproizvesti isti efekat kao i poziv
CrtajPravougaonik(5, 8, '*');
Treba napomenuti da se podrazumijevana vrijednost formalnog pararnetra koristi za njegovu
inicijalizaciju sarno u slucaju da se izostavi odgovarajuCi stvami argument prilikom poziva
potprograma. Tako ce, ukoliko izvrSimo poziv
CrtajPravougaonik(5, 8, '0');
formalni pararnetar "znak" dobiti vrijednost stvarnog pararnetra '0', odnosno dobieemo pravougaonik
iscrtan od znakova '0'.
VaZno je napomenuti da se pri zadavanju podrazumijevanih vrijednosti mora koristiti sintaksa sa
znakom "=", a ne konstruktorska sintaksa sa zagradama, koja je preporucena pri obicnoj inicijalizaciji
promjenljivih. Stoga se zaglavlje prethodnog programa nije moglo napisati ovako:
void CrtajPravougaonik(int visina, int sirina, char znak('*'))
Moguee je imati i vise pararnetara sa podrazumijevanom vrijednoseu. Medutim, pri tome postoji
ogranicenje da ukoliko neki pararnetar ima podrazumijevanu vrijednost, svi pararnetri koji se u listi
formalnih pararnetara nalaze desno od njega moraju takoaer imati podrazumijevane vrijednosti (razloge
za ovo ogranicenje uvidje6emo uskoro). Odavde slijedi da u slucaju da sarno jedan pararnetar ima
pOdrazumijevanu vrijednost, to moze biti sarno posljednji parametar u listi formalnih parametara.
Sljede6i primjer ilustrira varijantu potprograma "Crtaj Pravougaonik" u kojem se javljaju dva
parametra sa podrazumijevanim vrijednostima:
void CrtajPravougaonik (int visina, int sirina = 10, char znak = '*') {
for(int i = 1; i
8/3/2019 Zeljko Juric
11/18
Zeljko Juric: Principi programiranja /kroz programski jezik C++/
Radni materijali - akademska godina 2009/10
void CrtajPravougaonik(int, int = 10, char = '*');
Vjerovatno je upravo mogucnost ovakvih sintaksnih konstrukcija razlog zbog cega se za inicijalizaciju
podrazumijevanih parametara ne smije koristiti konstruktorska sintaksa.
Teoretski, podrazumijevane vrijednosti je moguce definirati u definiciji potprograma a ne u
prototipu. Medutim, u tom slucaju ukoliko se pri pozivu potprograma izostavi odgovarajuei stvarni
argument, kompajler ce prijaviti gresku ukoliko nije prethodno vidio Citavu definiciju potprograma, jer u
suprotnom nece znati da odgovarajuei argument uopce ima podrazumijevanu vrijednost.
Mogucnost da vise od jednog parametra ima podrazumijevane vrijednosti osnovni je razlog zbog
kojeg nije dozvoljeno da bilo koji parametar ima podrazumijevane vrijednosti, nego sarno parametri koji
cine zavrsni dio liste formalnih parametara. Nairne, razmotrimo sta bi se desHo kada bi bio dozvoljen
potprogram poput sljedeceg, u kojemprvi i treti parametar imaju podrazurnijevane vrijednosti:
void OvoNeRadi(int a 1, int b, int c = 2)cout a " " b " " c endl;
}
Ovakav potprogram bi se ocigledno mogao pozvati sa tri, dva ilijednim stvamim argumentom. Pri tome
su pozivi sa tri iIi jednim argumentom posve nedvosmisleni. Medutim, u slucaju poziva sa dva stvarnaargumenta (odnosno, u slucaju kada je jedan od argumenata izostavljen), javlja se dvosmislica po pitanju
ko}i je argument izostavljen (prvi ili treci). los vece dvosmislice mogle bi nastati u slucaju jos veceg
broja parametara, od kojih neki imaju podrazumijevane vrijednosti, a neki ne. U jeziku C++ ovakve
dvosrnislice su otklonjene striktnim ogranicavanjem koji parametri mogu imati podrazurnijevane
vrijednosti, a koji ne mogu.
U nacelu, podrazurnijevane vrijednosti ne moraju biti konstante vee mogu biti i izrazi. NaZalost,
kako vidokrug formalnih parametara pocinje tek unutar tijela odgovarajueeg potprograma, nije moguce u
podrazurnijevanoj vrijednosti iskoristiti vrijednost nekog drugog parametra koji se nalazi u listi
formalnih parametara, cak i ukoliko se on nalazi lijevo od parametra kojem zadajemo vrijednost. Tako
nije moguee napisati potprogram sa zaglavljem poput sljedeceg, u kojem bi podrazurnijevana vrijednost
formalnog parametra "sirina" trebala da budejednaka vrijednosti formalnog parametra "visina":
void Crtaj Pravougaonik (int visina, int sirina = visina, char znak = '*')
Mozemo reci da zbog opisanog problema sa ogranicenom vidljivoscu, eventualne promjenljive koje bi se
mogle upotrijebiti u izrazima kojim se inicijaliziraju podrazurnijevani parametri mogu biti samo globalne
promjenljive, cija se primjena svakako ne preporucuje. Stoga su podrazurnijevane vrijednosti gotovo
uvijek konstantne velicine. Srecom, u nastavku cemo vidjeti da postoji drugi naCin da se ostvari efekat
koji smo zeljeli postici gore prikazanom (neispravnom) definicijom.
U jeziku C++ je dozvoljeno imati vise potprograma sa istim imenima, pod uvjetom da je iz nacina
kako je potprogram pozvan moguce nedvosmisleno odrediti koji potprogram treba pozvati. Neophodan
uvjet za to je da se potprograrni koji imaju ista imena moraju razlikovati iIi po broju parametara, iIi po
tipu odgovarajueih formalnih parametara, iii i po jednom i po drugom. Ova mogucnost naziva sepreklapanje iii preopterecivanje (eng!. overloading) potprograma (funkcija). Na primjer, u sljedecem
primjeru imamo preklopljena dva potprograma istog imena "P1" koji ne rade nista korisno (sluze sarno
kao demonstracija preklapanja):
void P1(int a) {cout "Jedan parametar: " a endl;
void P1(int a, int b) {cout "Ova parametra: " a " i " b endl;
XV-II
8/3/2019 Zeljko Juric
12/18
Zeljko Jurie: Principi programiranja /kroz program ski jezik C++/
Radni materijali - akademska godina 2009/10
U ovom slueaju se radi 0 preklapanju po broju parametara. Stoga su legalna oba sljedeea poziva (pri
eemu ce u prvom pozivu biti pozvan drugi potprogram, sa dva parametra, a u drugom pozivu prvi
potprogram, sajednim parametrom):
PI (3, 5) i
PI (3) ;
Sljedeei primjer demonstrira preklapanje po tipu parametara. Oba potprograma imaju isto ime "P2"i oba imaju jedan formalni parametar, ali im se tip formalnog parametra razlikuje:
void P2(int a) {
cout "Parametar tipa int: " a endl;
void P2(double a) {
cout "Parametar tipa double: " a endl;
Jasno je da ce od sljedeca eetiri poziva, uz pretpostavku da je "n" cjelobrojna promjenljiva, prva dva
poziva dovesti do poziva prvog potprograma, dok ce treei i eetvrti poziv dovesti do poziva drugog
potprograma:
P2 (3) ;
P2(1 + n / 2);P2 (3. ) ;
P2(3.14 * n / 2.21);
Ovi primjeri jasno ukazuju na znaeaj pojma tipa vrijednosti u jeziku C++, i potrebe za razlikovanjem
podatka "3"(kojije tipa "int") i podatka "3." (kojije tipa "double").
Prilikom odredivanja koji ce potprogram biti pozvan, kompajler prvo pokusava da pronade
potprogram kod kojeg postoji potpuno slaganje po broju i tipu izmedu formalnih i stvarnih parametara.
Ukoliko se takav potprogram ne pronade, tada se pokuSava ustanoviti indirektno slaganje po tipu
parametara, oOOosno slaganje po tipu uz pretpostavku da se izvrSi automatska pretvorba stvarnih
parametara u navedene tipove formalnih parametara (uz pretpostavku da su takve automatske pretvorbe
dozvoljene, poput pretvorbe iz tipa "char" u tip "int"). Ukoliko se ni nakon toga ne uspije uspostaviti
slaganje, prijavljuje se greska.
U slucaju da se potpuno slaganje ne pronade, ada se indirektno slaganje moze uspostaviti sa vise
razliCitih potprograma, daje se prioritet slaganju koje zahtijeva "logicniju" oOO08no "manje drasticnu"
konverziju. Tako se konverzija izjeOOog cjelobrojnog tipa u drugi (npr. iz tipa "char" u tip "int") iii iz
jeOOog realnog tipa u drugi (npr. iz "float" u "double") smatra "logienijom" odnosno "direktnijom"
od konverzije iz cjelobrojnog u realni tip. Stoga ce, za slucaj prethodnog primjera, poziv
P2 (' A');
u kojem je stvarni parametar tipa "char", dovesti do poziva potprograma "P2" sa formalnimparametrom tipa "int", s obzirom da je konverzija iz tipa "char" u tip "int" neposrednija nego
(takoder dozvoljena) konverzija u tip "double". Takoder, konverzije u ugradene tipove podataka
smatraju se logienijim od konverzija u korisnicke tipove podataka, koje cerno upoznati kasnije.
Medutim, moze se desiti da se indirektno slaganje moze uspostaviti sa vise razlicitih potprograma, preko
konverzija koje su medusobno podjednako logicne. U tom slucaju smatra se da je poziv nejasan (engl.
ambiguous), i prijavljuje se greska. Na primjer, ukoliko postoje dva potprograma istog imena od kojih
jedan prima parametar tipa "float" a drugi parametar tipa "double", bice prijavljena greska ukoliko
kao stvarni parametar upotrijebimo podatak tipa "int" (osim ukoliko postoji i treei potprogram istog
tipa koji prima parametar cjelobrojnog tipa). Nairne, obje moguce konverzije iz tipa "int" u tipove
"float" i "double" podjednako su logicne, i kompajler ne moze odluciti koji potprogram treba
pozvati. Na ovakve nejasnoee vee smo ukazivali pri opisu matematickih funkcija iz biblioteke "cmath",
XV -12
8/3/2019 Zeljko Juric
13/18
Zeljko Juric: Principi programiranja /kroz programski jezik C++/
Radni materijali - akademska godina 2009/10
kod kojih nastaju upravo opisane nejasnoee u slucaju da 1m se kao stvarni argumenti proslijede
cjelobrojne vrijednosti.
Uz izvjestan oprez, moguee je rnijesati tehniku koriStenja podrazurnijevanih parametara, preklapanja
po broju parametara i preklapanja po tipu parametara. Oprez je potreban zbog Cinjenice da se
kombiniranjem ovih tehnika poveeava moguenost da nepamjom formiramo definicije koje ee dovesti do
nejasnih poziva. Na primjer, razmotrimo sljedeea dva potprograma istog imena "P3":
void P3(int a) {
cout "Jedan parametar: " a endl;
void P3(int a, int b = 10) {cout "Dva parametra: " a " i " b endl;
Jasno je da je, UZ ovako defmirane potprograme, poziv poput "P3 (10)" nejasan, jer ne postoji
mogucnost razgranicenja da Ii se radi 0 pozivu prvog potprograma, iIi pozivu drugog potprograma sa
izostavljenim drugim argumentom. U oba slucaja ostvaruje se potpuno slaganje tipova. Medutim, uz
neophodnu dozu opreza, moguce je formirati korisne potprograme koji kombiniraju opisane tehnike. Na
primjer, razmotrirno sljedeee potprograme:
void CrtajPravougaonik(int V1Slna, int sirina, char znak
for(int i = 1; i
8/3/2019 Zeljko Juric
14/18
Zeljko Jurie: Principi programiranja /kroz programski jezik C++/
Radni materijali - akademska godina 2009/1 0
razmisliti da li je zaista potrebno koristenje istog imena,jer prevelika upotreba preklapanja moze dovesti
do zbrke. Na primjer, u prethodnom primjeru mozda je parnetnije drugi potprogram nazvati
"CrtajKvadrat", jer on u suStini zaista iscrtava kvadrat (koji doduse jeste specijalan slucaj
pravougaonika, tako da i ime "Crtaj Pravougaoni k" ima opravdanja, u smislu da se drugi potprogram
moze smatrati kao specijalan slueaj prvog). Naroeito su spoma preklapanja po tipu parametara, jer jedosta upitno zbog eega bi trebali imati dva potprograrna istog imena koji prihvataju argumente razlicitog
tipa. Ukoliko ovi potprogrami rade razlicite stvari, trebali bi imati i razlicita imena. Stoga se preklapanjepo tipu obicno koristi u slucaju kada vise potprograrna logicki gledano obavlja isti zadatak, ali se tajzadatak za razlicite tipove izvodi na razlicite nacine (npr. postupak racunanja stepena ab osjetno se
razlikuje za slucaj kada je b cijeli broj i za slucaj kada je b realan). Sa primjerima ispravnoupotrijebljenih preklapanja susretaeemo se u kasnijim poglavljima. Kao opeu preporuku, u slucajevimakada se isti efekat moze postiei preklapanjem i upotrebom parametara sa podrazumijevanimvrijednostima, uvijek je bolje i sigumije koristiti pararnetre sa podrazumijevanim vrijednostima.Preklapanje moze biti veoma korisno, ali sarno pod uvjetom da tacno znarno sta i zasto radimo. Usuprotnom,upotreba preklapanja sarno dovodi do zbrke.
Treba naglasiti da pararnetre sa podrazumijevanim vrijednostima treba koristiti samo ako su svevarijante koje se mogu dobiti izostavljanjem stvarnih parametara smislene. Na primjer, ako irnamo
potprogram "F" koji se moze pozvati ili sa dva cjelobrojna argumenta iIi bez argumenata pri cemu se
tada podrazumijeva da oba argumenta irnaju vrijednosti 0, ali ne i sa sarnojednim argumentom, tada nijedobro definirati ovaj potprograrn kao
void F(int x= 0, int yO) {
jer ee se tako napisan potprograrn moei pozvati i sa jednim argumentom, sto nije predvideno. Umjestotoga, treba koristiti preklapanje po broju argumenata. Da se izbjegne dupliranje kOda, u preklopljenoj
verziji bez argumenata moze se pozvati preklopljena verzija sa argumentima:
void F() {
F (0,0) ;
Kod prograrna koji sadrZe mnostvo potprograrna, neophodno je poznavati strukturu programa,odnosno informaciju 0 tome koje potprograme on sadrZi, i kakva je pozivna hijerarhija (tj. kojipotprogrami zovu koje potprograrne). Pregledan nacin za prikazivanje strukture prograrna predstavljaju
tzv. strukturni dijagrami. Na primjer, sljedeei dijagram oznacava da potprograrn "A" poziva
potprograrne "B" i "c":
Struktumi dijagrami mogu takoder prikazivati i prenos parametara. Na primjer, sljedeei dijagrarnprikazuje da glavni program poziva potprogram "Proracunaj Krug" i da mu pri tome prenosivrijednost promjenljive "pol uprecni k" kao pararnetar:
XV-14
8/3/2019 Zeljko Juric
15/18
Zeljko Jurie: Principi programiranja /kroz program ski jezik C++/
Radni materijali - akademska godina 2009/10
Ne treba mijesati struktume dijagrame sa tzv. dijagramima toka (engl. flowcharts). Naime,
strukturni dijagrami prikazuju samo hijerarhiju potprograma, a ne ialgoritam kako pojedini potprogrami
djeluju (niti kako funkcioniraju).
Mehanizam prenosa parametara je od kljucnog znacaJa za razvoj programa. Naime, u dobro
napisanom programu, niti jedan dio programa ne bi trebao da ima pristup onim promjenljivim koje mu
nisu potrebne (ovaj princip postaje jos izrazajniji u tzv. objektno zasnovanom pristupu programiranju,koji cemo razmatrati kasnije). Na primjer, sa gledista onog ko poziva potprogram, svaki potprogram
treba djelovati kao "cma kutija" (pod ovim pojmom se u tehnici obicno podrazumijeva uredaj za koji
mozemo utvrditi sta radi, ali ne i kako radi, s obzirom da nam je njegova unutrasnjost posve
nedostupna). Naime, onaj ko poziva potprogram treba samo da potprogramu preda ispravne parametre i
da im prepusti da odrade svoj posao, ne ulazeti u to kako ce oni to uraditi. S druge strane, potprogrami
samo primaju parametre od pozivaoca, i ne tiee ih se sta radi ostatak programa. Oni se brinu samo kako
da obave zadatak koji im je povjeren. Takoder, u dobro napisanom programu, ni jedan potprogram ne bi
trebao da radi vise razliCitih poslova (svakom poslu treba dodije1iti poseban potprogram).
Mehanizam koji obezbjeduje ispunjenje ovih principa naziva se sakrivanje informaeija (engl.
information hiding). U jeziku C++ ovaj mehanizam se ostvaruje tako sto svaku promjenljivu definiramo
tako da joj je vidokrug sto je god moguce manji. Da se izbjegne upotreba globalnih promjenljivih u
funkcijama, treba intenzivno koristiti parametre. Ovaj koncept je ilustriran na poboljsanoj verzijimodularnog programa za prikaz sahovske table, u kojem je izbjegnuta upotreba global nih promjenljivih,
koristenjem mehanizma prenosa parametara:
#include
using namespace std;
const char Razmak(' '), Zvjezdica('*');
int main ()
void StampajTablu(int, int);
int m ;
int ni
II Glavni program
II Prototip potprograma "Stampaj Tabl u"
II Sirina kvadratnag palja
II Visina kvadratnog polja
cout "Ovaj program prikazuje ahovsku tablu.\n\n""Unesite irinu svakog kvadrata, u znakovima: ";
cin m;cout "Unesite visinu svakog kvadrata, u linijama: ";cin n;
cout "\n\n";StampajTablu(n, m);
return 0;
II Stampa sahavsku tablu
void StampajTablu(int visina, int sirina)
void StampajNeparniRed(int, int);
void Stampaj ParniRed (int, int);
for (int i = 1; i
8/3/2019 Zeljko Juric
16/18
Zeljko Jurie: Principi programiranja Ikroz program ski jezik C++/
Radni materijali - akademska godina 2009/1 0
II Stampa parni red
void StampajParniRed(int visina, int sirina) {
void StampajLinijuParnogReda(int):
for(int i = 1; i < = visina; i++) StampajLinijuParnogReda(sirina):
II Stampa liniju neparnog reda
void StampajLinijuNeparnogReda(int sirina kvadrata)
void StampajZnakove(int, char);
for(int i = 1; i < = 4; i++) {
StampajZnakove(sirina kvadrata, Razmak);
StampajZnakove(sirina kvadrata, Zvjezdica);}
cout endl:
II Stampa liniju parnog reda
void StampajLinijuParnogReda(int sirina kvadrata)
void StampajZnakove(int, char):
for(int i = 1; i < = 4: i++) {
StampajZnakove (sirina_kvadrata, Zvjezdica):
StampajZnakove (sirina kvadrata, Razmak):
}
cout endl:
II Stampa niz znakova
void StampajZnakove(int broj znakova, char znak) {
for(int i = I: i
8/3/2019 Zeljko Juric
17/18
Ze1jko Jurie: Principi programiranja Ikroz program ski jezik C++I
Radni materijali - akademska godina 2009/10
pseudo-k6da (nacin koji smo vee u vise navrata koristili za opis funkcioniranja pojedinih dijelova
programa). Mada su se nekada za opisivanje algoritama intenzivno koristili dijagrami toka, njihova
upotreba se danas izbgjegava, jer se smatra da oni nisu prilagodeni konceptima modemih programskih
jezika i da njihova upotreba podstice nemodulami pristup u razvoju programa.
Na ovom mjestu je neophodno naglasiti da modulami programi nisu niti najkraCi niti najefikasniji,
ali su sigumo lakSi i za razumijevanje i za odriavarije (tj. za eventualne modifikacije, korekcije, dopune
itd.) od odgovarajueih nemodulamih programa. Na primjer, sljedeei (nemodulami) program sigumo je
dosta kraei od prethodno napisanog modulamog progrdma, ali je tezi za razurnijevanje (koristi cetiri
petlje jedna unutar druge), tezi je za izmjene, a narocito je losa strana sto se ni jedan dio ovog programa
ne moze upotrebiti kao potprogram u nekom drugom programu:
#include
using namespace std;
int m ain() {
int m, n;
cout "Ovaj program prikazuje ahovsku tablu.\n\n""Unesite irinu svakog kvadrata, u znakovima: ";
cin m;
cout "Unesite visinu svakog kvadrata, u linijama: ";cin n;cout "\n\n";for(int i = 1; i
8/3/2019 Zeljko Juric
18/18
Zeljko Jurie: Principi programiranja /kroz program ski jezik C++/
Radni materijali - akademska godina 2009/ 10
specifiene za problem koji se rjesava, sto eini razradeni algoritam potpuno "vezanim" za problem koji se
rjeSava. Pogledajmo, na primjer, sljedecu verziju programa za iscrtavanje sahovske table. On jenesumnjivo izuzetno kratak, i koristi sarno dvije petlje, ali i prilicno tezak za razumijevanje, jer sezasniva na nekim "karakteristienim" svojstvima "sare" koju eini sahovska tabla. Takoder, ovaj program
koristi i prilieno "konfuzni" temarni operator "? :". Nemojte se previse zabrinjavati ukoliko ne uspijete
razumjeti kako radi ovaj program. On je vise "mozgalica" za enigmatieare nego primjer kako bi trebalo
pisati dobre programe:
#include
using namespace std;
int main() {
int ill, n;
cout "Ovaj program prikazuje ahovsku tablu.\n\n""Unesite irinu svakog kvadrata, u znakovima: ";
cin m;cout "Unesite visinu svakog kvadrata, u linijama: ";cin n;
cout "\n\n";for(int i = 0; i < 8 * n; i++) {
for(int k = 0; k < 8 * m; k++)cout ((i / n + k / m) %2 ? "*" "");cout endl;
}
return 0;
Na primjeru ovog programa takoder mozemo vidjeti i da su kratkoca i efikasnost medusobnosukobljeni zahtjevi. Tako je ovaj program vjerovatno "sampion kratkoce", ali je manje efikasan odprethodnog, jer se u ovom programu unutar unutraSnje petlje izvrSavajutri operacije dijeljenja. Kako se
unutrasnja petlja izvrSava 8 m puta a spoljaSnja 8 n puta, slijedi da se u ovom programu izvrSava
ukupno 192 m n dijeljenja (koje je relativno spora operacija), dok se u prethodnom programu neizvrSava niti jedno dijeljenje! Efikasnost ovog programa se moze osjetno popraviti (uz zadrZavanje
kratkoce) upotrebom manipulatora "setw" i "setfill", sto prepuStamo enigmatski nastrojenimeitateljima i eitate1jkamakao korisnu vjezbu.
XV -18