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

Dr.

eljko Juri: Tehnike programiranja /kroz programski jezik C++/


Radna skripta za kurs Tehnike programiranja na Elektrotehnikom fakultetu u Sarajevu

Predavanje 1_a
Akademska godina 2013/14

Predavanje 1_a
Ovi materijali namijenjeni su onima koji su odsluali kurs Osnove raunarstva na Elektrotehnikom
fakultetu u Sarajevu po novom nastavnom programu, kao i svima onima koji ve poznaju osnove
programiranja u programskom jeziku C, a ele da preu na programski jezik C++. S obzirom da je
programski jezik C++ isprva bio zamiljen kao nadgradnja jezika C, gotovo svi programi pisani u jeziku
C ujedno predstavljaju ispravne C++ programe. Ipak, kompatibilnost izmeu C-a i C++-a nije
stoprocentna, tako da postoje neke konstrukcije koje su ispravne u jeziku C, a nisu u jeziku C++. Takvi
primjeri e biti posebno istaknuti. S druge strane, bitno je napomenuti da mnoge konstrukcije iz jezika C,
mada rade u jeziku C++, treba izrazito izbjegavati u jeziku C++, s obzirom da u jeziku C++ postoje
mnogo bolji, efikasniji i sigurniji naini da se ostvari isti efekat. Treba shvatiti da je C++ danas ipak
posve novi programski jezik, bitno drugaiji od C-a, mada u principu visoko kompatibilan sa njim.
Rjeenja koja su dobra (a ponekad i jedina mogua) u C-u, u C++-u mogu biti veoma loa. Stoga biti
vrlo dobar programer u C-u najee ne znai biti dobar C++ programer. Vrijedi ak obrnuto: ispoetka
su nabolji C programeri obino katastrofalno loi C++ programeri, sve dok se ne naviknu na filozofiju
novog jezika i na novi nain razmiljanja. Stoga e uvodna poglavlja uglavnom biti posveena onim
aspektima jezika C koje nije preporuljivo koristiti u C++-u, dok e specifinosti naina razmiljanja u
C++-u doi kasnije.
Verzija (standard) jezika C++ koja e se koristiti u ovim materijalima poznata je kao ISO C++ 2011
ili skraeno kao C++11, koja je objavljena krajem 2011. godine (prije oficijelnog objavljivanja, dugo
vremena koristio se radni naziv C++0x, s obzirom da se oekivalo da e ova verzija biti objavljena
najkasnije do 2009. godine, ali se rad na ovoj verziji neplanirao otegnuo). Posljedica ove injenice da e
se neki primjeri iz ovih materijala moi uspjeno kompajlirati i izvriti samo sa najnovijim kompajlerima,
koji podravaju C++11 standard (nije na odmet napomenuti da je u pripremi i standard C++14, koji
predvia uglavnom neka minorna poboljanja standarda C++11). U nekim kompajlerima, podrka za
C++11 standard je jo uvijek u eksperimentalnoj fazi, tako da je podrku za C++11 potrebno posebno
aktivirati podeavajui opcije kompajlera (recimo, u CodeBlocks razvojnom okruenju, potrebno je
aktivirati odgovarajuu opciju na kartici Compiler u meniju Settings).
Bitno je naglasiti da je u standardu C++11 uvedeno toliko mnogo inovacija u odnosu na prethodni
standard C++03 (iz 2003. godine, koji se sasvim minorno razlikuje od standarda C++98 godine, koji je
bio prije njega), da programi koji koriste mnogo svojstava standarda C++11 neupuenima djeluju kao da
su pisani u potpuno novom programskom jeziku, a ne u C++-u. Zapravo, standard C++11 je jezik C++
uinio gotovo dvostruko obimnijim u odnosu na prethodni standard C++03 (to je zaista mnogo, jer je
ve i standard C++03 bio izuzetno obiman). U ovom kursu, radi nedostatka prostora, nee se koristiti
mnogo inovacija standarda C++11 (zapravo, najbitnije inovacije kao to su podrka vienitnom
programiranju uope se nee ni spomenuti), nego e se uglavnom koristiti inovacije koje su lako
objanjive, a koje omoguavaju znatno elegantnije izvoenje nekih programskih konstrukcija nego to je
to bilo mogue u C++03 odnosno C++98.
Ponimo od prvog C programa iz veine udbenika o C-u tzv. Hello world / Vozdra raja
programa:
#include <stdio.h>
main() {
printf("Vozdra raja!\n");
return 0;
}

ak ni ovaj posve jednostavan C program nije ispravan u C++-u poev od standarda C++98
nadalje. Naime, u ovom primjeru, funkcija main nema povratni tip. U C-u se tada podrazumijeva da je
povratni tip int, dok se u C++-u povratni tip uvijek mora navoditi. Dakle, u C++-u bi ispravna bila
sljedea varijanta:

Dr. eljko Juri: Tehnike programiranja /kroz programski jezik C++/


Radna skripta za kurs Tehnike programiranja na Elektrotehnikom fakultetu u Sarajevu

Predavanje 1_a
Akademska godina 2013/14

#include <stdio.h>
int main() {
printf("Vozdra raja!\n");
return 0;
}

Ovo je, bar za sada, ispravno C++ rjeenje. Meutim, ve pojavom sljedeeg C++ standarda, postoji
velika mogunost da se ni ovo rjeenje nee smatrati ispravnim. Naime, standardna biblioteka funkcija
prilagoena jeziku C, kojoj izmeu ostalog pripada i zaglavlje stdio.h, ne uklapa se dobro u neke
moderne koncepte jezika C++. Stoga e veina kompajlera na gornji program dati upozorenje da je
upotreba zaglavlja stdio.h pokuena odnosno izrazito nepreporuljiva (engl. deprecated) u jeziku
C++ i da vjerovatno u budunosti nee biti podrana (pokuene osobine jezika su one osobine za koje je
ustanovljeno da se ne uklapaju dobro u filozofiju jezika i za koje nema garancije da e ih budui
standardi jezika i dalje podravati, tako da ih treba prestati koristiti). Umjesto toga, poev od C++98
standardu, napravljene su nove verzije zaglavlja standardne biblioteke za one elemente jezika C++ koji
su naslijeeni iz jezika C, a koje su napravljene tako da se bolje uklapaju u koncepte jezika C. Stoga,
ukoliko je pqr.h naziv nekog zaglavlja standardne biblioteke jezika C, u C++-u umjesto njega treba
koristiti zaglavlje imena cpqr, dakle, bez sufiksa .h i sa prefiksom c (npr. cstdio umjesto
stdio.h, cmath umjesto math.h, cctype umjesto ctype.h, itd.). Stoga, potpuno ispravna
verzija gornjeg programa u C++-u (poev od standarda C++98 nadalje) glasi ovako:
#include <cstdio>
int main() {
std::printf("Vozdra raja!\n");
return 0;
}

Ovdje vidimo jo jednu novinu: specifikator std:: ispred imena funkcije printf. Ovo je
jedna od novina zbog koje su uope i uvedena nova zaglavlja tzv. imenici (engl. namespaces). Imenici
su osobina jezika C++ koja je uvedena tek u standardu C++98 (tj. relativno skoro). Naime, pojavom
velikog broja nestandardnih biblioteka razliitih proizvoaa bilo je nemogue sprijeiti konflikte u
imenima koje mogu nastati kada dvije razliite biblioteke upotrijebe isto ime za dva razliita objekta ili
dvije razliite funkcije. Na primjer, biblioteka za rad sa bazama podataka moe imati funkciju nazvanu
update (za auriranje podataka u bazi), dok neki autor biblioteke za rad sa grafikom moe odabrati
isto ime update za auriranje grafikog prikaza na ekranu. Tada mogu nastati problemi ukoliko isti
program koristi obje biblioteke poziv funkcije update postaje nejasan, jer se ne zna na koju se
funkciju update misli. Zbog toga je odlueno da se imena (identifikatori) svih objekata i funkcija
mogu po potrebi razvrstavati u imenike (kao to se datoteke mogu razvrstavati po folderima). Dva
razliita objekta ili funkcije mogu imati isto ime, pod uvjetom da su definirani u razliitim imenicima.
Identifikatoru ime koji se nalazi u imeniku imenik pristupa se pomou konstrukcije imenik::ime.
Dalje, standard propisuje da se svi identifikatori koji spadaju u standardnu biblioteku jezika C++ moraju
nalaziti u imeniku std. Odatle potie specifikacija std::printf. Slinu specifikaciju bismo
morali dodati ispred svih drugih objekata i funkcija iz standardne biblioteke (npr. std::sqrt za
funkciju sqrt iz zaglavlja cmath, koja rauna kvadratni korijen).
Stalno navoenje imena imenika moe biti naporno, stoga je mogue pomou kljune rijei
using navesti da e se podrazumijevati da se neki identifikator uzima iz navedenog imenika, ukoliko
se ime imenika izostavi. Sljedei primjer pokazuje takav pristup:
#include <cstdio>
using std::printf;
int main() {
printf("Vozdra raja!\n");
return 0;
}

Dr. eljko Juri: Tehnike programiranja /kroz programski jezik C++/


Radna skripta za kurs Tehnike programiranja na Elektrotehnikom fakultetu u Sarajevu

Predavanje 1_a
Akademska godina 2013/14

Ovim smo naveli da se podrazumijeva da identifikator printf uzimamo iz imenika std, ako
se drugaije ne kae eksplicitnim navoenjem imenika ispred identifikatora. Moemo takoer rei da se
neki imenik (u naem primjeru std) podrazumijeva ispred bilo kojeg identifikatora ispred kojeg nije
eksplicitno naveden neki drugi imenik, na nain kao u sljedeem primjeru:
#include <cstdio>
using namespace std;
int main() {
printf("Vozdra raja!\n");
return 0;
}

Mada je u kratkim programima ovakvo rjeenje vjerovatno najjednostavnije, ono se ipak ne


preporuuje, jer na taj nain u program uvozimo cijeli imenik, ime naruavamo osnovni razlog zbog
kojeg su imenici uope uvedeni. Imenik std je zaista veoma bogat i uvoenjem cijelog ovog
imenika postaje sasvim mogue da e se neko ime koje postoji u ovom imeniku poklopiti sa imenom
neke promjenljive, objekta ili funkcije koju programer definira u svom programu (pogotovo ako se kao
identifikatori koriste engleska imena), to moe uzrokovati konflikte koji se teko otkrivaju. Zbog toga
emo u nastavku ovih materijala uvijek eksplicitno navoditi prefiks std:: ispred svih identifikatora
koji pripadaju standardnoj biblioteci. Ovo ima dodatnu prednost to e se itaoci na taj nain podsvjesno
pamtiti koji identifikatori pripadaju standardnoj biblioteci. One koje nervira stalno navoenje std::
prefiksa mogu, na sopstveni rizik, koristiti naredbu using, ali mi je u nastavku neemo koristiti.
Mada su prethodni primjeri dotjerani tako da budu u potpunosti po standardu jezika C++, oni jo
uvijek nisu u duhu jezika C++, zbog koritenja biblioteke cstdio i njene funkcije printf, koje
su isuvie niskog nivoa sa aspekta jezika C++, koji je konceptualno jezik vieg nivoa nego C (zapravo,
C++ je hibridni jezik, u kojem se moe programirati na niskom nivou, na visokom nivou, u
proceduralnom, neobjektnom stilu, kao i u objektno-orijentiranom stilu, pa ak i u nekim apstraktnim
stilovima kao to su generiki i funkcionalni stil programiranja, za razliku od jezika kao to je Java, koji
forsira iskljuivo objektno-orijentirani stil, i kad treba, i kad ne treba). Slijedi primjer koji radi isto to i
svi prethodni primjeri, samo to je pisan u duhu jezika C++:
#include <iostream>
int main() {
std::cout << "Vozdra raja!\n";
return 0;
}

U ovom primjeru je umjesto funkcije printf upotrijebljen tzv. objekat izlaznog toka podataka
(engl. output stream) cout, koji je povezan sa standardnim izlaznim ureajem (obino ekranom).
Ovaj objekat definiran je u biblioteci iostream, i kao i svi objekti iz standardnih biblioteka, takoer
se nalazi u imeniku std (ukoliko smo prethodno koristili naredbu using std::cout ili ak
using namespace std koja uvozi itav imenik std, mogli bismo pisati samo cout umjesto
std::cout; u suprotnom, ako bismo napisali samo cout umjesto std::cout, kompajler bi nam
javio da ne poznaje objekat cout, to je vrlo esta greka kod poetnika). Znak << predstavlja
operator umetanja (engl. insertion) u izlazni tok, koji pojednostavljeno moemo itati kao alji na.
Stoga konstrukcija
std::cout << "Vozdra raja!\n";

umee niz znakova (string) "Vozdra raja\n" (podsjetimo se da je \n znak za prelaz u novi red) u
izlazni tok, odnosno alje ga na standardni izlazni ureaj (ekran).
Prvi argument operatora << je obino neki objekat izlaznog toka (postoje i drugi objekti izlaznog
toka osim cout, npr. objekti izlaznih tokova vezani sa datotekama), dok drugi argument moe biti

Dr. eljko Juri: Tehnike programiranja /kroz programski jezik C++/


Radna skripta za kurs Tehnike programiranja na Elektrotehnikom fakultetu u Sarajevu

Predavanje 1_a
Akademska godina 2013/14

proizvoljni ispisivi izraz. Na primjer, sljedee konstrukcije su posve legalne (uz pretpostavku da je
ukljuena biblioteka cmath, u kojoj je deklarirana funkcija sqrt za raunanje kvadratnog korijena):
std::cout << 32 (17 + 14 * 7) / 3;
std::cout << 2.56 - 1.8 * std::sqrt(3.12);

Primijetimo da objekat izlaznog toka cout sam vodi rauna o tipu podataka koji se umeu u
tok, tako da nije potrebno eksplicitno specificirati tip podatka koji se ispisuje, kao to bismo morali
prilikom koritenja funkcije printf, gdje bismo morali pisati neto poput:
std::printf("%d", 32 (17 + 14 * 7) / 3);
std::printf("%f", 2.56 - 1.8 * std::sqrt(3.12));

Vano je napomenuti da konstrukcija poput std::cout << 2 + 3 u jeziku C++ takoer


predstavlja izraz, kao to je izraz i sama konstrukcija 2 + 3. ta je rezultat ovog izraza? U C++-u
rezultat operatora umetanja primjenjen na neki objekat izlaznog toka kao rezultat daje ponovo isti
objekat izlaznog toka (odnosno cout u naem primjeru), to omoguava nadovezivanje operatora
<<, kao u sljedeem primjeru:
std::cout << "2 + 3 = " << 2 + 3 << "\n";

Naime, ovaj izraz se interpretira kao


((std::cout << "2 + 3 = ") << 2 + 3) << "\n";

Drugim rijeima, u izlazni tok se prvo umee niz znakova "2 + 3 = ". Kao rezultat tog umetanja
dobijamo ponovo objekat cout kojem se alje vrijednost izraza 2 + 3 (odnosno 5). Rezultat tog
umetanja je ponovo objekat cout, kojem se alje znak za novi red. Krajnji rezultat je ponovo objekat
cout, ali tu injenicu moemo ignorirati, s obzirom da jezik C++, slino jeziku C, dozvoljava da se
krajnji rezultat ma kakvog izraza ignorira (recimo, i funkcija printf daje kao rezultat broj ispisanih
znakova, ali se taj rezultat gotovo uvijek ignorira). U duhu jezika C, prethodna naredba napisala bi se
ovako:
std::printf("2 + 3 = %d\n", 2 + 3);

U zaglavlju iostream je definiran i objekat za prelazak u novi red endl. Logiki gledano, on
ima slinu ulogu kao i string "\n", mada u izvedbenom smislu postoje znaajne razlike (u efektivnom
smislu, razlika je to upotreba objekta endl uvijek dovodi do pranjenja spremnika izlaznog toka, to
moe biti znaajno kod tokova vezanih za datoteke). Stoga smo mogli pisati i:
std::cout << "2 + 3 = " << 2 + 3 << std::endl;

Na ovom mjestu nije loe napomenuti da standard C++11 predvia da standardna biblioteka jezika
C++ obavezno mora sadravati biblioteke sa sljedeim zaglavljima:
algorithm
cassert
cfenv
ciso646
codecvt
csignal
cstddef
cstring
cwchar
forward_list
initializer_list
iostream
list

array
ccomplex
cfloat
climits
complex
cstdalign
cstdint
ctgmath
cwctype
fstream
iomanip
istream
locale

atomic
cctype
chrono
clocale
condition_variable
cstdbool
cstdio
ctime
deque
functional
ios
iterator
map

bitset
cerrno
cinttypes
cmath
csetjmp
cstdarg
cstdlib
cuchar
exception
future
iosfwd
limits
memory

Dr. eljko Juri: Tehnike programiranja /kroz programski jezik C++/


Radna skripta za kurs Tehnike programiranja na Elektrotehnikom fakultetu u Sarajevu

mutex
queue
scoped_allocator
stack
strstream
type_traits
unordered_set

new
random
set
stdexcept
system_error
typeindex
utility

numeric
ratio
shared_mutex
streambuf
thread
typeinfo
valarray

Predavanje 1_a
Akademska godina 2013/14

ostream
regex
sstream
string
tuple
unordered_map
vector

Pored navedenih 80 standardnih zaglavlja iz standardne biblioteke (30 vie nego u standardu
C++03), mnogi kompajleri za C++ dolaze sa itavim skupom nestandardnih biblioteka, koje ne
predstavljaju propisani standard jezika C++. Tako, na primjer, biblioteka za rad sa grafikom sigurno ne
moe biti unutar standarda jezika C++, s obzirom da C++ uope ne podrazumijeva da raunar na kojem
se program izvrava mora imati ak i ekran, a kamoli da mora biti u stanju da vri grafiki prikaz.
Takoer, biblioteka sa zaglavljem windows.h koja slui za pisanje Windows aplikacija ne moe biti
dio standarda C++ jezika, jer C++ ne predvia da se programi moraju nuno izvravati na Windows
operativnom sistemu. Zaglavlja nestandardnih biblioteka gotovo uvijek imaju i dalje nastavak .h na
imenu, da bi se razlikovala od standardnih biblioteka.
Slino objektu cout, biblioteka iostream definira i objekat ulaznog toka podataka (engl.
input stream) cin, koji je povezan sa standardnim ureajem za unos (tipino tastaturom). Ovaj objekat
se obino koristi zajedno sa operatorom izdvajanja (engl. extraction) iz ulaznog toka >>, koji
pojednostavljeno moemo itati kao alji u. Njegov smisao je suprotan u odnosu na smisao operatora
umetanja << koji se koristi uz objekat izlaznog toka cout. Slijedi primjer programa koji zahtijeva
unos dva cijela broja sa tastature, a koji zatim ispisuje njihov zbir:
#include <iostream>
int main() {
int broj_1, broj_2;
std::cout << "Unesite prvi broj: ";
std::cin >> broj_1;
std::cout << "Unesite drugi broj: ";
std::cin >> broj_2;
std::cout << "Zbir brojeva " << broj_1 << " i " << broj_2
<< " glasi " << broj_1 + broj_2 << endl;
return 0;
}

Isti program, napisan u duhu C-a, mogao bi izgledati recimo ovako:


#include <cstdio>
int main() {
int broj_1, broj_2;
std::printf("Unesite prvi broj: ");
std::scanf("%d", &broj_1);
std::printf("Unesite drugi broj: ");
std::scanf("%d", &broj_2);
std::printf("Zbir brojeva %d i %d glasi %d\n", broj_1, broj_2,
broj_1 + broj_2);
return 0;
}

Primijetimo da nas objekat ulaznog toka takoer oslobaa potrebe da vodimo rauna o tipu
podataka koje unosimo, i da eksplicitno prenosimo adresu odredine promjenljive pomou operatora
&, to moramo raditi pri upotrebi funkcije scanf. Za razliku od operatora umetanja, desni operand
kod operatora izdvajanja ne moe biti proizvoljan izraz, ve samo promjenljiva, ili neki objekat iza kojeg
postoji rezervirani prostor u memoriji, kao to je npr. element niza, dereferencirani pokaziva, itd. (takvi
objekti nazivaju se l-vrijednosti, engl. l-values).

Dr. eljko Juri: Tehnike programiranja /kroz programski jezik C++/


Radna skripta za kurs Tehnike programiranja na Elektrotehnikom fakultetu u Sarajevu

Predavanje 1_a
Akademska godina 2013/14

Promjenljive se u jeziku C++ deklariraju na isti nain kao u jeziku C, i za njihova imenovanja
vrijede ista pravila kao u jeziku C, uz iznimku da C++ posjeduje vie kljunih rijei od jezika C, tako da
postoji vei broj rijei koje ne smiju biti imena promjenljivih (tako, npr. friend i this ne mogu
biti imena promjenljivih u C++-u, a mogu u C-u, jer si u C++-u friend i this kljune rijei).
Prema standardu C++11, kljune rijei u jeziku C++ su sljedee:
alignas
asm
bool
char
compl
continue
do
enum
false
goto
long
noexcept
operator
protected
return
static
switch
throw
typeid
using
wchar_t

alignof
auto
break
char16_t
const
decltype
double
explicit
float
if
mutable
not
or
public
short
static_assert
template
true
typename
virtual
while

and
bitand
case
char32_t
constexpr
default
dynamic_cast
export
for
inline
namespace
not_eq
or_eq
register
signed
static_cast
this
try
union
void
xor

and_eq
bitor
catch
class
const_cast
delete
else
extern
friend
int
new
nullptr
private
reinterpret_cast
sizeof
struct
thread_local
typedef
unsigned
volatile
xor_eq

Radi lakeg uoavanja, rezervirane rijei se u programima obino prikazuju podebljano, to je


uinjeno i u dosadanjim primjerima, i to e biti ubudue primjenjivano u svim primjerima koji slijede.
Slino kao kod operatora umetanja, rezultat operatora izdvajanja primijenjen nad objektom ulaznog
toka daje kao rezultat ponovo sam objekat ulaznog toka, to omoguava ulanavanje i ovog operatora.
Recimo, konstrukcija poput
std::cin >> a >> b >> c;

ima isto dejstvo kao da smo napisali


std::cin >> a;
std::cin >> b;
std::cin >> c;

Ovo je iskoriteno u sljedeem primjeru, u kojem se sa tastature uitavaju tri cijela broja, a zatim
ispisuju na ekran, svaki u novom redu:
#include <iostream>
int main() {
int a, b, c;
std::cin >> a >> b >> c;
std::cout << a << std::endl << b << std::endl << c << std::endl;
return 0;
}

Na ovom mjestu je bitno ukazati na jednu estu poetniku greku. Mada nekome moe izgledati
logina konstrukcija poput
std::cin >> a, b, c;

Dr. eljko Juri: Tehnike programiranja /kroz programski jezik C++/


Radna skripta za kurs Tehnike programiranja na Elektrotehnikom fakultetu u Sarajevu

Predavanje 1_a
Akademska godina 2013/14

ona nee dati oekivani rezultat (iz ulaznog toka e biti uitana samo vrijednost promjenljive a). to je
najgore, kompajler nee prijaviti nikakvu greku s obzirom da je ova konstrukcija sintaksno ispravna u
skladu sa opim sintaksnim pravilima jezika C i C++. Ovo je neeljeni propratni efekat naina na koji se
u jezicima C i C++ interpretira znak zareza u opem sluaju. S obzirom da su greke zbog pogrene
upotrebe zareza u jezicima C i C++ dosta este (pogotovo u C++-u), nije na odmet malo detaljnije
razmotriti o emu je zapravo rije. Naime, ponekad se u jezicima C i C++ javlja potreba da se na nekom
mjestu gdje sintaksa jezika oekuje tano jedan izraz upotrijebi vie izraza. Zbog toga je u jezike C i
C++ uvedena konvencija da se skupina od vie izraza koji su meusobno razdvojeni zarezima logiki
tretira kao jedan jedini izraz (tj. moe se koristiti u kontekstima u kojima se oekuje tano jedan izraz),
ali se samo rezultat onog posljednjeg izraza u nizu posmatra kao rezultat tog sloenog izraza (bez obzira
to e se izvriti sve njegove komponente i to redom slijeva nadesno). Ova mogunost ima raznolike
primjene u jezicima C i C++ (ali naalost i mnoge propratne neeljene efekte). Jedan jednostavan
primjer primjene je sljedei. Neka je potrebno unositi sa tastature cijele brojeve i ispisivati na ekran
njihove kvadrate, sve dok se ne unese negativan broj, nakon ega se dalji unos prekida. Postoji mnogo
naina da se ovo izvede, a ubjedljivo najkrai je sljedei, u kojem se na vjet nain koristi upravo zarez:
int x;
while(std::cin >> x, x >= 0) std::cout << x * x << std::endl;

Kako ovaj primjer radi? Jasno je da se unutar zagrada kod while petlje treba navesti neki izraz,
koji predstavlja neki uvjet (tj. koji moe biti taan ili netaan). Meutim, u ovom primjeru, unutar
zagrada u while petlji navedena su dva izraza od kojih je prvi std::cin >> x a drugi x >= 0.
Meutim, kako su ova dva izraza meusobno povezana zarezom, oni se tretiraju kao jedan sloeni izraz.
U ovom sluaju, zarez nema ulogu razdvojnika (separatora) nego poveznika koji dva ili vie izraza
povezuje u jedan sloeni izraz (zarez upotrijebljen na ovaj nain naziva se zarez-operator, ili engl.
comma operator). Dakle, nailaskom na while petlju, izvravaju se oba podizraza sloenog izraza koji
se nalazi u zagradi. Prvo e se izvriti podizraz std::cin >> x koji e izvriti unos cijelog broja sa
tastature u promjenljivu x, a nakon toga e se izvriti podizraz x >= 0 koji testira da li je unesena
vrijednost vea ili jednaka od nule. Meutim, kao rezultat tog sloenog izraza uzima se samo rezultat
posljednjeg izraza (tj. rezultat obavljenog testiranja), tako da e se na osnovu tog testiranja odluiti da li
emo uope ui u tijelo petlje ili ne (tijelo petlje se ovdje sastoji samo od naredbe koja ispisuje kvadrat
unesenog broja). Nakon eventualno obavljenog ispisa, vraamo se ponovo na uvjet petlje, u kojem se
ponovo vre dvije akcije: unos novog broja i njegovo testiranje na nenegativnost. Ovakve konstrukcije
na prvi pogled nisu posve oigledne, ali se prilino esto koriste, kako u C-u, tako i u C++-u.
Pogledajmo sada kako se u kontekstu ovoga to smo upravo objasnili interpretira izraz
std::cin >> a, b, c;

Osnovni problem kod ovako formiranog izraza to se on zapravo interpretira kao objedinjenje tri izraza
std::cin >> a, b i c u jednu cjelinu. Mada e se sva tri izraza izvriti, samo prvi od njih zaista
obavlja unos sa tastature (drugi i trei izraz, odnosno izrazi b i c nee proizvesti nikakvu akciju).
Posljedica e biti da e samo vrijednost promjenljive a biti unesena sa tastature, dok e promjenljive
b i c ostati kakve su bile i ranije. Jo jednom treba napomenuti da je injenica da zarez u nekim
sluajevima slui kao separator (tj. kao razdvaja vie stavki u nekom popisu, kao recimo prilikom
deklaracije promjenljivih), dok u nekim sluajevima slui kao poveznik, i da treba tano znati kad je u
pitanju jedna a kad druga uloga, dovodi do estih zabuna u C i C++ programima (teta je to za funkciju
povezivanja u ovim jezicima nije upotrijebljen neki drugi znak a ne ba zarez zabune bi tada bile
mnogo rjee).
Razmotrimo sada malo detaljnije ta se tano deava prilikom upotrebe ulaznog toka. Svi znakovi
koje korisnik unese sve do pritiska na taster ENTER uvaju se u spremniku (baferu) ulaznog toka.
Meutim, operator izdvajanja vri izdvajanje iz ulaznog toka samo do prvog razmaka ili do prvog znaka
koji ne odgovara tipu podataka koji se izdvaja (npr. do prvog znaka koji nije cifra u sluaju kada
izdvajamo cjelobrojni podatak). Preostali znakovi i dalje su pohranjeni u spremniku ulaznog toka.

Dr. eljko Juri: Tehnike programiranja /kroz programski jezik C++/


Radna skripta za kurs Tehnike programiranja na Elektrotehnikom fakultetu u Sarajevu

Predavanje 1_a
Akademska godina 2013/14

Sljedea upotreba operatora izdvajanja e nastaviti izdvajanje iz niza znakova zapamenog u spremniku.
Tek kada se ulazni tok isprazni, odnosno kada se istroe svi znakovi pohranjeni u spremniku bie
zatraen novi unos sa ulaznog ureaja. Sljedea slika prikazuje nekoliko moguih scenarija prilikom
izvravanja programa prikazanog u dnu stranice 6. (kurzivom su prikazani podaci koje unosi korisnik
nakon to se program pokrene):
10 20 30 40(ENTER)
10
20
30

10 20(ENTER)
30(ENTER)
10
20
30

10(ENTER)
20(ENTER)
30 40(ENTER)
10
20
30

Ovakvo ponaanje je u nekim sluajevima povoljno, ali u nekim nije. Nekada elimo da smo uvijek
sigurni da e operator izdvajanja proitati svjee unesene podatke, a ne neke podatke koji su od ranije
preostali u spremniku ulaznog toka. Zbog toga je mogue pozivom funkcije ignore nad objektom
ulaznog toka cin isprazniti ulazni tok (vidjeemo kasnije da u jeziku C++ postoje funkcije koje se ne
pozivaju samostalno, nego uvijek nad nekim objektom, pri emu je sintaksa takvog poziva
objekat.funkcija(argumenti). Ova funkcija ima dva argumenta, od kojih je prvi cjelobrojnog tipa, a
drugi znakovnog tipa (tipa char). Ona uklanja znakove iz ulaznog toka, pri emu se uklanjanje
obustavlja ili kada se ukloni onoliko znakova koliko je zadano prvim argumentom, ili dok se ne ukloni
znak zadan drugim argumentom. Na primjer, naredba
std::cin.ignore(50, '.');

uklanja znakove iz ulaznog toka dok se ne ukloni 50 znakova, ili dok se ne ukloni znak '.'. Ova naredba
se najee koristi da kompletno isprazni ulazni tok. Za tu svrhu, treba zadati naredbu poput
std::cin.ignore(10000, '\n');

Ova naredba e uklanjati znakove iz ulaznog toka ili dok se ne ukloni 10000 znakova, ili dok se ne
ukloni oznaka kraja reda '\n' (nakon ega je zapravo ulazni tok prazan). Naravno, kao prvi parametar
smo umjesto 10000 mogli staviti neki drugi veliki broj (na je cilj zapravo samo da uklonimo sve
znakove dok ne uklonimo oznaku kraja reda, ali moramo neto zadati i kao prvi parametar). Opisana
tehnika je iskoritena u sljedeoj sekvenci naredbi:
int broj_1, broj_2;
std::cout << "Unesite prvi broj: ";
std::cin >> broj_1;
std::cin.ignore(10000, '\n');
std::cout << "Unesite drugi broj: ";
std::cin >> broj_2;

Izvrenjem ovih naredbi emo biti sigurni da e se na zahtjev Unesi drugi broj: zaista traiti unos broja
sa tastature, ak i ukoliko je korisnik prilikom zahtjeva Unesi prvi broj: unio odmah dva broja.
U sluaju unosa pogrenih podataka (npr. ukoliko se oekuje broj, a ve prvi uneseni znak nije
cifra), ulazni tok e dospjeti u tzv. neispravno stanje. Da je tok u neispravnom stanju, moemo provjeriti
primjenom operatora negacije ! nad objektom toka. Rezultat je taan ako i samo ako je tok u
neispravnom stanju, to moemo iskoristiti kao uvjet unutar naredbe if, while ili for. Ovo
ilustrira sljedei fragment:
int broj;
std::cout << "Unesite neki broj: ";
std::cin >> broj;
if(!std::cin) std::cout << "Niste unijeli broj!\n";
else std::cout << "Zaista ste unijeli broj!\n";

Dr. eljko Juri: Tehnike programiranja /kroz programski jezik C++/


Radna skripta za kurs Tehnike programiranja na Elektrotehnikom fakultetu u Sarajevu

Predavanje 1_a
Akademska godina 2013/14

Kao uvjet unutar naredbi if, while i for, moe se iskoristiti i sam objekat toka, koji se tada
interpretira kao taan ako i samo ako je u ispravnom stanju. Tako se prethodni fragment mogao napisati i
ovako:
int broj;
std::cout << "Unesite neki broj: ";
std::cin >> broj;
if(std::cin) std::cout << "Zaista ste unijeli broj!\n";
else std::cout << "Niste unijeli broj!\n";

Kada ulazni tok jednom dospije u neispravno stanje, on u takvom stanju ostaje i nadalje, i svaki
sljedei pokuaj izdvajanja iz ulaznog toka bie ignoriran. Tok moemo ponovo vratiti u ispravno stanje
pozivom funkcije clear bez parametara nad objektom ulaznog toka. U narednom primjeru emo
iskoristiti ovu funkciju i while petlju sa ciljem ponavljanja unosa sve dok unos ne bude ispravan:
int broj;
std::cout << "Unesite broj: ";
std::cin >> broj;
while(!std::cin) {
std::cout << "Nemojte se aliti, unesite broj!\n";
std::cin.clear();
std::cin.ignore(10000, '\n');
std::cin >> broj;
}
std::cout << "U redu, unijeli ste broj " << broj << std::endl;

Rad ovog isjeka je prilino jasan. Ukoliko je nakon zahtijevanog unosa broja zaista unesen broj,
ulazni tok e biti u ispravnom stanju, uvjet !std::cin nee biti taan, i tijelo while petlje nee se
uope ni izvriti. Meutim, ukoliko tok dospije u neispravno stanje, unutar tijela petlje ispisujemo
poruku upozorenja, vraamo tok u ispravno stanje, uklanjamo iz ulaznog toka znakove koji su doveli do
problema (pozivom funkcije cin.ignore), i zahtijevamo novi unos. Nakon toga, uvjet petlje se
ponovo provjerava, i postupak se ponavlja sve dok unos ne bude ispravan (tj. sve dok uvjet !std::cin
ne postane netaan).
U prethodnom primjeru, naredba za unos broja sa tastature ponovljena je unutar petlje. Moe li se
ovo dupliranje izbjei? Mada na prvi pogled izgleda da je ovo dupliranje nemogue izbjei (unos je
potreban prije testiranja uvjeta petlje, dakle praktino izvan petlje, a potrebno ga ponoviti u sluaju
neispravnog unosa, dakle unutar petlje), odgovor je ipak potvrdan, uz upotrebu jednog prilino
prljavog trika, koji vrijedi objasniti, s obzirom da se esto susree u C++ programima. Ideja je da se
sam unos sa tastature ostvari kao propratni efekat uvjeta petlje (slino kao u primjeru u kojem smo
demonstrirali zarez-operator). Naime, konstrukcija std::cin >> broj sama po sebi predstavlja izraz
(sa propratnim efektom unoenja vrijednosti u promjenljivu broj), koji pored injenice da oitava
vrijednost promjenljive broj iz ulaznog toka, takoer kao rezultat vraa sam objekat ulaznog toka
cin, na koji je dalje mogue primijeniti operator ! sa ciljem testiranja ispravnosti toka. Stoga je
savreno ispravno formirati izraz poput !(std::cin >> broj) koji e oitati vrijednost promjenljive
broj iz ulaznog toka a zatim testirati stanje toka. Rezultat ovog izraza bie taan ili netaan u
zavisnosti od stanja toka, odnosno on predstavlja sasvim ispravan uvjet, koji se moe iskoristiti za
kontrolu while petlje! Kada uzmemo sve ovo u obzir, nije teko shvatiti kako radi sljedei
programski isjeak:
int broj;
std::cout << "Unesite broj: ";
while(!(std::cin >> broj)) {
std::cout << "Nemojte se aliti, unesite broj!\n";
std::cin.clear();
std::cin.ignore(10000, '\n');
}
std::cout << "U redu, unijeli ste broj " << broj << std::endl;

Dr. eljko Juri: Tehnike programiranja /kroz programski jezik C++/


Radna skripta za kurs Tehnike programiranja na Elektrotehnikom fakultetu u Sarajevu

Predavanje 1_a
Akademska godina 2013/14

Znatno je istije sljedee rjeenje, u kojem je prvo upotrijebljena konstrukcija for(;;) koja
predstavlja petlju bez prirodnog izlaza, a koja se nasilno prekida pomou naredbe break u sluaju
ispravnog unosa:
int broj;
std::cout << "Unesite broj: ";
for(;;) {
std::cin >> broj;
if(std::cin) break;
std::cout << "Nemojte se aliti, unesite broj!\n";
std::cin.clear();
std::cin.ignore(10000, '\n');
}
std::cout << "U redu, unijeli ste broj " << broj << std::endl;

Ve smo rekli da je koritenje objekata ulaznog i izlaznog toka cin i cout mnogo jednostavnije
i fleksibilnije (kao to emo uskoro vidjeti) od koritenja funkcija iz biblioteke cstdio, kao to su
printf i scanf. To ipak ne znai da biblioteku cstdio treba potpuno zaboraviti. Dva su sluaja
u kojima treba koristiti funkcije iz ove biblioteke. Prvo, objekti poput cin i cout su, zbog svoje
velike fleksibilnosti, veoma masivni, i njihova upotreba osjetno produuje duinu generiranog izvrnog
koda. Stoga, samo ukljuenje biblioteke iostream u program, koja deklarira i definira ove objekte,
tipino produava duinu generisanog izvrnog koda za nekoliko desetina do stotina kilobajta (zavisno
od kompajlera). U sluaju da elimo generiranje kratkog izvrnog koda, treba zaobii ovu biblioteku i
koristiti funkcije iz biblioteke cstdio. Drugo, funkcije iz biblioteke cstdio su bre od
ekvivalentnih operacija sa tokovima, tako da je u sluaju kada iz ulaznog toka treba proitati vie
desetina hiljada podataka (ili kada treba ispisati vie desetina hiljada podataka na izlazni tok) za kratko
vrijeme, preporuuje se ne koristiti objekte tokova, nego funkcije iz biblioteke cstdio. U svim
ostalim sluajevima, upotreba biblioteke cstdio se u jeziku C++ ne preporuuje.

10

You might also like