PCchip
PC EXTRA
NauËite programirati u C++
Konteksti predloæaka i klasa Milijuni programera πirom svijeta koriste programski jezik C. Mnogi ga se tek spremaju nauËiti. Ipak, za dobro plaÊeno zaposlenje trebat Êe vam znanje neπto drukËijeg C-a. Ono πto se traæi jest C++, jer danas je moderno poznavati upravo taj jezik... Stoga vas ovaj mjesec u okviru priloga PC Extra uvodimo u C++ programiranje! piπe:: Saπa OrËiÊ •
[email protected]
K
aæe li vam netko da je C++ samo nadgradnja C-a, ne vjerujte mu. C++ traæi posve drukËiji pristup, a jedino πto mu je zajedniËko s C-om jest sintaksa osnovnih naredbi. Dobar C++ program bit Êe potpuno nerazumljiv Ëistom C programeru, dok Êe Ëisti C program C++ programeru zvuËati neracionalno, no bit Êe mu u potpunosti shvatljiv. Objektno programiranje veÊ je stara stvar; niste li joπ usvojili ovu paradigmu, kasnite, i to debelo. »isto proceduralno programiranje moæda je pogodno za uËenje programiranja, no Ëim prvi put pokuπate kreirati realni program za bilo koji grafiËki
i A PROSTORA JE TAKO MALO...
N
aravno da ovakav tekst ne pretendira biti sveobuhvatni vodiË programiranja u C++. Uostalom, pogledajte bilo koji priruËnik takve tematike: nema tanjeg od 500 stranica. Stroustrupova biblija (na koju se dobrim dijelom naslanja i ovaj umetak) ima 900+ stranica... C++ ima odliËan sistem obrade greπaka (exceptions) koji dozvoljava, uz malo programerskog truda, gotovo savrπen rad programa. Na æalost, iz umetka smo osim obrade greπaka morali ispustiti i joπ poneku interesantnu temu kao πto je prosljeivanje funkcija kao argumenata funkcija. Ipak, nadamo se da Êe potpuni poËetnici i Ëisti C programeri ovdje naÊi dovoljno materijala za poËetak. Uostalom, o vama ovisi hoÊe li i kada nastati nastavak ovog umetka; bude li dovoljno zainteresiranih, mogli bismo obraditi i naprednije teme (Windows programiranje, trikove, obradu greπaka...). Treba li vam - viËite.
Odabir novog projekta u Visual C++-u
operativni sistem, prednosti objektnog programiranja postaju oËite. Osim toga, naËin razmiπljanja objektnog programera prirodniji je od proceduralnog. Na æalost, ovaj je tekst previπe kratak i za opisivanje osnovnih pojmova C++-a, tako da jednostavno ne moæemo paralelno odræati i πkolu objektnog programiranja. Ipak, neπto od ovoga πto Êete nauËiti spada upravo u objektno programiranje.
Zaπto C++? Manji projekti (do 1.000 linija kôda) lakπe su izvedivi nekim jednostavnijim programskim jezicima. Primjerice, Visual BASIC je alat kojim je u posljednjih nekoliko godina nastalo mnoπtvo aplikacija. Dokle god od aplikacije ne trebate brzinu (odnosno, zadovoljni ste brzinom izvoenja programa u nekom od drugih programskih jezika) BASIC ili Pascal Ëine dobar izbor. No, Ëim projekt preraste u neπto viπe, C++ dolazi na svoje. Nemojte pogreπno shvatiti: C++ je pogodan i za razvoj sitnih aplikacija,
no jednostavno se bolje osjeÊa u ulozi velikog razvojnog programskog alata. C++ je viπenamjenski alat. To znaËi da njime moæete raditi programe niske razine (sistemske rutine), ali i uobiËajene korisniËke aplikacije. Osim toga, C++ je pogodan za poËetnike i zbog gotovo linearne krivulje uËenja jezika. »im nauËite nekoliko osnovnih naredbi, moÊi Êete poËeti programirati. Daljnjim uËenjem moguÊnosti programa C++ samo Êete profitirati; tako Êete poËeti primjenjivati klase, shvaÊati prednosti overloadinga operatora, izraivati svoje predloπke (templates)... Ovaj programski jezik omoguÊava i najviπe razine apstrakcije podataka. Dobro definirana klasa omoguÊit Êe rad bez ikakve brige o tipu podataka i operatorima koje smijete primjenjivati. Naravno, C++ naslijedio je od C-a i nisku razinu programiranja; izvoenje kompiliranog kôda stoga moæe biti vrlo brzo. C++ je portabilan na gotovo svaki danas postojeÊi operativni sustav. Dokle god se pridræavate osnovnih biblioteka i dok ne krπite osnovna pravila C++ programiranja, vaπ Êe program biti moguÊe kompilirati gotovo svakim C++ prevoditeljem. Ne smatrate li ovo u poËetku vaænim, priËekajte da vaπ program doæivi planetarnu slavu; tad Êe biti itekako vaæno kojim ste jezikom pisali program i na koji naËin... Naravno, svaki programski jezik ima i svoje nedostatke. Pri C++ programiranju najoËitiji nedostatak su zahtjevi za sistemskim resursima. Bez 128 MB radne memorije i brzim (500+ MHz) procesorom rad Êe biti nalik svemu osim ugodnom programiranju. Isto tako, planirate li koristiti Microsoftov Visual Studio paket (sastavni dio kojega je i C++), raËunajte da Êete trebati barem 1 GB prostora na disku - instalirate li i kompletan sustav.
Osnovni koncepti C++ programiranja Programiranje u C++ okruæenju prije svega podrazumijeva apstrakciju podataka. Isto tako, skrivanje podataka od korisnika dovedeno je do visokog stupnja. Glavni
PCextra k n j i æ i c a s u p u t s t v i m a
2
• C++ programiranje
koncept C++-a je klasa. U principu, klasa je korisniËki definirani tip. UoËite da se ne spominje tip podataka, jer C++ dozvoljava kreiranje tipa koji osim vrste podataka koji se koriste opisuje i operacije koje se nad takvim podacima smiju izvesti. C++ se viπe-manje automatski brine i o memory managementu. Dokle god u radu koristite standardne pozive funkcija za alokaciju i dealokaciju memorije - moæete biti mirni. Joπ jednom napominjemo da je vaæno πto viπe koristiti standardne biblioteke koje Êete dobiti uz svaki C++ compiler. »emu izmiπljati toplu vodu i pisati kompletan tîp za baratanje stringovima, ako takav veÊ postoji u standardnoj biblioteci? Upravo Êe zato dobar C++ programer viπe misliti o organizaciji programa nego o algoritmima. Najvaæniji korak k dobrom C++ programiranju leæi u dobro definiranim tipovima i njihovim meusobnim odnosima; zatreba li vam baπ neki algoritam kojeg nema u standardnoj biblioteci, dobro definirani tipovi omoguÊit Êe vam njegovo lakπe kodiranje.
Programiranje C++-om Ne postoji programski jezik koji omoguÊava predstavljanje baπ svakog tipa podatka. Takoer, jezik moæe biti siromaπniji ili bogatiji ugraenim funkcijama, no opet ne postoji nijedan koji ima ugraene baπ sve funkcije koje Êe vam ikada zatrebati. Stoga C++ omoguÊava izgradnju proizvoljnih tipova, a takoer i izgradnju proizvoljnih funkcija. Osnovna sintaksa C++-a jest nadskup sintakse C-a. Svaka C naredba radit Êe u C++-u, no obrat ne vrijedi. Takoer, postoje i naredbe C-a koje nije pametno koristiti pri C++ programiranju. TipiËan je primjer klasiËna malloc() funkcija; C++ programeri pozvat Êe new().
Klasa TipiËni Hello, world! C++ program ne zahtijeva poznavanje principa rada klasama. Isto tako, program kraÊi od 100tinjak linija vjerojatno Êe biti lakπe
programirati grubom silom: smisli algoritam i napiπi ga. No, svi ostali projekti jednostavno zahtijevaju koriπtenje osnovnog principa programiranja C++-om. Precizno definirano, klasa je skup objekata. Ona specificira kako se objekt kreira, kako se njime manipulira, πto mu se smije a πto ne smije uËiniti, kako se objekt uniπtava... Proceduralni programeri o klasi mogu razmiπljati kao o skupu tipova podataka i funkcija nad takvim tipovima podataka; takva zaokruæena cjelina je - viπe-manje - Prazan projekt omoguÊava poËetak od nule klasa. funkcija koje barataju nekim podacima Promotrimo primjer 1. prvi je korak pri izbjegavanju greπaka u programiranju. Pojavi li se run-time Primjer 1 class Datum { greπka u dobrom C++ programu, lako int d, m, g; Êemo naÊi klasu (a samim tim i funkciju) public: koja prouzrokuje greπku. Promijenimo void init(int dd, int mm, li internu reprezentaciju podataka, int gg); neÊemo morati kopati po cijelom void dodaj_dan(int n); programu nastojeÊi uskladiti funkcije s void dodaj_mjesec(int n); novom reprezentacijom; promijenit Êemo samo funkcije u klasi. Joπ vaænije, void dodaj_godinu(int n); kompletan ostatak programa ostaje }; kakav je i bio: svi pozivi funkcija izgledat Zaπto uopÊe koristiti klase? Nije li se Êe isto. To πto se promijenila prethodni primjer mogao implementirati reprezentacija podataka i naËin njihove obrade neÊe zanimati nijednu drugu Ëistim C pristupom iz primjera 2? funkciju - sve dok se funkcije iz klase naizgled ponaπaju kao i prije. Primjer 2 Klase su Ëesto meusobno povezane. struct Datum1 { Primjerice, iz naπe Datum klase moæe se int d, m, g; izvesti klasa Vrijeme, koja osim datuma void init(int dd, int sadræi i vrijeme u obliku sat:minuta. mm, int gg); Takva se klasa moæe definirati od nule, void dodaj_dan(int n); no prirodan pristup problemu nalaæe void dodaj_mjesec(int nam da takvu novu klasu na neki naËin n); poveæemo s klasom Datum, i da samo void dodaj_godinu(int dodamo nove podatke i funkcije n); specifiËne za raËunanje sati i minuta. } Ovakav se pristup zove nasljeivanje. IdentiËno, nije li? Pa, ne baπ... Nijedna Tipovi vanjska (ona koja nije definirana u klasi) Svima koji iole poznaju C funkcija ne moæe izravno pristupiti podacima iz klase. Primjerice, sljedeÊa savjetujemo da slobodno preskoËe nekoliko poglavlja. funkcija prou- sljedeÊih zroËit Êe greπku: Pretpostavljamo da se onima kojima je void Greska ova graa poznata ne dâ joπ jednom Ëitati objaπnjenja osnovnih tipova Datum& d) podataka kojima C++ barata, no { d.g=2000; poËetnicima (i nesigurnima) nema druge. Stoga poËnimo. } C++ barata razliËitim tipovima SliËna funkcija koja pri- podataka. Neki su od njih ugraeni u stupa podacima sâm jezik; to podrazumijeva da je iz deklaracije dozvoljena deklaracija varijabli tog tipa s t r u k t u r e i da je nad takvim podacima omoguÊeno (struct) Datum1 izvoditi razumne operacije. Razumno u bit Êe izvrπena ovom sluËaju znaËi da neÊete pokuπati bez poteπkoÊa. podijeliti dva stringa (niza znakova), veÊ, Osiguravanje na primjer, dva realna broja. Boolean je tip podataka koji poprima lokalizacije
Ad free
k n j i æ i c a s u p u t s t v i m a PCextra
C++ programiranje • samo dvije vrijednosti: istinu (true) i laæ (false). Interno se boolean podaci reprezentiraju integer vrijednostima, s tim da nula predstavlja laæ, a sve ostale vrijednosti ponaπaju se kao istina. Character sluæi za reprezentaciju znakova; char varijabla tako moæe sadræavati bilo koji znak prikaziv izlaznim jedinicama. C++ sadræi i podrπku za Unicode znakove, no opis baratanja takvim znakovima nadilazi prostorne moguÊnosti ovog teksta. Integer je cjelobrojna varijabla. Prefiks short znaËi da se radi o kratkom cijelom broju, a long pak oznaËava dugaËak cijeli broj. Koliko je koji od ovih integera doista dugaËak (koliko se bitova koristi za reprezentaciju broja) ovisi o operativnom sistemu i implementaciji C++-a. Short int najËeπÊe je πirok 16 bita, a long int se pak reprezentira u 32 bita. Na nekim pak sistemima long int bit Êe πirine 64 bita. Æelite li posebno naglasiti da Êete baratati samo pozitivnim integerima, koristite prefiks unsigned, a u
No, C++ ne voli ovako definirane statiËke strukture. Treba li vam doista matrica od 100*100 elemenata, u redu. No mnogo je ËeπÊi sluËaj da vam treba polje, ali ne znate toËno koje veliËine. I πto god da pokuπate, nikad niste sasvim sigurni da Êe pristup upaliti; kreirate li polje od 100 elemenata, tko kaæe da vam neÊe zatrebati i sto prvi? Ili Êe pak u radu polje ostati neiskoriπteno - koristit Êete samo njegovih prvih 10 elemenata. ©teta memorije... Stoga C++ preferira dinamiËko alociranje memorije. U ovakvom sluËaju zgodno je koristiti vektore - radi se o strukturi koja je sliËna statiËkoj definiciji polja, no ima nekoliko dodatnih prednosti. Nama je trenutaËno najvaænije svojstvo realokacije memorije - u samom programu moæete po volji mijenjati ukupnu veliËinu vektora. Promotrite sljedeÊi primjer: Primjer 3 int lista1[1000]; vector
lista2(1000); void main(); lista1[999]=1; lista1[1000]=2; /* greska - lista1 ima samo 1000 elemenata, gdje je 0 prvi element, a 999-ti je zadnji */ lista2[999]=1;
Dodavanje source (programskih) datoteka projektu
obrnutom sluËaju koristite prefiks signed. Konkretno, varijabla unsigned short int tipa moÊi Êe na veÊini sistema pohraniti vrijednosti od 0 do 65,535 (216-1), dok Êe signed long int dozvoliti raspon od 2,147,483,648 do 2,147,483,647 (-231 do 231-1). ToËnu duljinu svakog tipa saznat Êete funkcijom sizeof(). Pronaite okvir u kojem je funkcija opisana. Realne se varijable predstavljaju float, double i long double (Ëesto se koristi i extended) tipom. Razlika je opet samo u broju bitova kojima se reprezentira zapis: float je tip koji Ëuva najmanje precizne zapise, a long double osigurava visoku toËnost raËunanja. Svaka od funkcija u C-u i C++-u vraÊa neku vrijednost. Ako æelite eksplicitno naglasiti da funkcija ne vraÊa nikakvu vrijednost, koristite kljuËnu rijeË void.Tako u primjeru 1 nijedna od funkcija ne vraÊa nikakvu vrijednost.
Strukture i polja C++, kao i C, nizovima barata iskljuËivo kao jednodimenzionalnim poljima. To znaËi da Êete dvodimenzionalno polje (matricu) od m*n elemenata prikazati kao polje od m elemenata polja od n elemenata.
lista2.resize(lista2.size()+1); /* dodali smo jedno mjesto listi2 */ lista2[1000]=2; /* ovo je sad u redu */ }; Naravno, C++ i dalje podræava definiranje struktura podataka. Tako Êe struct Adresar { string ime, prezime; string telefon; int d, m, g; }; definirati strukturu Adresar, u kojoj Êe svaka osoba imati ime, prezime, telefonski broj i datum roenja. No, takva struktura definira samo tip podataka, dok niπta ne govori o funkcijama koje se na takvim podacima smiju primjenjivati, ne definira naËin konstruiranja i uniπtavanja varijabli tipa Adresar...
3
i SIZEOF()
R
azvijate li program u udobnosti doma, ne oËekujte da Êe portiranjem na UNIX stroj sve raditi bez ikakvih problema. Vrlo Ëest uzrok greπaka jest razliËita duljina reprezentacija osnovnih tipova podataka na razliËitim raËunalima. Varijabla svakog tipa, ukljuËujuÊi i osnovne tipove podataka, zauzima odreenu koliËinu memorije. No na raznim se raËunalima teoretski iste vrijednosti predstavljaju razliËitom duljinom zapisa. TipiËan je primjer int; iako na veÊini raËunala int varijabla zauzima 4 bajta, ponegdje Êe za njezin zapis biti potroπeno 2 bajta, a ponekad i svih 8 bajtova. Koliko toËno mjesta u memoriji zauzima varijabla odreenog tipa najlakπe je saznati funkcijom sizeof(). Ona vraÊa veliËinu varijable u bajtovima. Argument funkcije moæe biti bilo koja varijabla ili tip - ugraeni ili korisniËki definiran. Funkcija vraÊa i zauzeÊe memorije za statiËki definirane nizove (na primjer int polje[20]), ali ne i za dinamiËki kreirane nizove. Ponekad sizeof() vrati nekoliko bajtova veÊi rezultat nego πto ga oËekujete. Imate li u prevoditelju ukljuËenu optimizaciju brzine, ponekad se strukturi - zbog alignmenta - pridoda koji bajt.
samo adresu memorijske lokacije. No tipom mogu biti razliËiti; pointer na cijeli broj informirat Êe prevoditelja da se na toj memorijskoj adresi nalazi dva ili Ëetiri bajta koje mora pretvoriti u integer. Pointer na neku strukturu pak oznaËava adresu na kojoj poËinje podatak definiran tom strukturom; on moæe biti duljine jednog bajta, ali i mnogo viπe... Primjer 4 #include #include using namespace std ; string niz[100]; string* i; int main() { i=&niz[50]; ++i; *i= xyz ; cout << niz[51] << endl; return(0); }
Pointerska aritmetika SjeÊate li se osnovne πkole i pointera (pokazivaËa)? Ako ste ikada programirali, morali ste proÊi takvo πto. C++ nasljeuje sintaksu i smisao pointerskih varijabli iz obiËnog C-a. Ukratko: pointeri su varijable koje pokazuju na neku adresu u memoriji. Sadræajem su svi pointeri isti - oni Ëuvaju
Primjer 4 najbolje objaπnjava koriπtenje pointera. U prvoj liniji programa pointer i poprima adresu (prefiks & oznaËava adresu varijable) 50. podatka u listi niz. U sljedeÊoj liniji poveÊavamo vrijednost pointera, no ne nuæno za jedan. U ovisnosti o sadræaju 50tog elementa niza niz pointer i bit Êe uveÊan upravo za duljinu tog elementa, tako da Êe
PCextra k n j i æ i c a s u p u t s t v i m a
4
• C++ programiranje varijabli ima svojih prednosti; ima li tog programera koji kao kontrolnu varijablu petlje ne voli koristiti i? No, poæeljno je i neπto paænje. Redefinirate li tip varijable u unutraπnjem bloku, prevoditelj vas neÊe upozoriti da je vanjska varijabla nedostupna.
nakon toga pokazivati na sljedeÊi (51) element liste. Sad na tu adresu postavimo string xyz kojeg, testa radi, u sljedeÊoj liniji ispisujemo. O tipu string, kao i o standardnom ulazu i izlazu (to je onaj cout u pretposljednjoj liniji programa), bit Êe rijeËi kasnije.
Varijable
Operatori i izrazi
Dozvoljena imena u C++-u sastoje se od slova, znamenki i podcrte (znak “_”). Kako Êete ih kombinirati, vaπa je stvar; bitno je samo da na prvom mjestu imena TipiËan izgled Visual C++ radne okoline bude slovo. VeÊina se programera slaæe da je imenovanje varijabli umjetnost. Napiπete rijeËi kasnije). li program od 500 linija u kojem Êe se sve Varijable se u C++-u mogu deklarirati varijable zvati a, b, c i sliËno, malo Êe se tek kad vam zatrebaju. Unutar deklaracije tko u njemu lako snaÊi. »ak Êete i vi, varijablu je moguÊe i inicijalizirati. Ovakav pogledate li program nakon mjesec-dva pristup smanjuje moguÊnost greπke imati problema. Dobar izbor imena uzrokovane nedefiniranom vrijednoπÊu varijabli stvar je svakog programera varijable. ponaosob, no razvijate li tek svoj stil, Varijable moæete deklarirati na poËetku pokuπajte se pridræavati osnovnih pravila. bloka, na πto su navikli tradicionalni Prije svega, varijablama dajte imena programeri. No, proizvoljna deklaracija malog poËetnog slova. Sastoji li se ime omoguÊuje i egzibicije iz primjera 5. varijable od dvije rijeËi, razdvojite ih podcrtom. Primjerice, prezime_korisnika Primjer 5 Ëitljivije je od prezimekorisnika ili for(int i=100; i; i ) prezimeKorisnika. Kako C++ razlikuje { mala i velika slova, upotreba velikih slova /* naredbe unutar foru imenu varijable samo Êe vam dodatno bloka */ zakomplicirati stvar. Tko Êe se nakon sto } linija definiranja varijable sjetiti zove li se ona jedandva, JedanDva ili jedanDva? Dakle, jednostavno definirajte varijablu Velikim poËetnim slovom uobiËajeno je onog trenutka kad je zatrebate. Joπ je oznaËavati imena struktura i klasa. Uvijek zgodnije to πto podruËje vrijednosti pokuπajte upotrebljavati opisna imena varijable nadzirete sami. varijabli, no pretjerivanje nije poæeljno. Budete li viπe nego jednom morali upisati Scope kontrolna_varijabla_za_veliku_petlju, ubrzo Valjanost deklaracije varijable u C++-u Êete shvatiti zaπto imena moraju biti πto jest blok u kojem je varijabla definirana. No, kraÊa. IskljuËivo velikim slovima obiËno se postoji li unutar tog bloka novi blok i u oznaËavaju makro naredbe (o kojima Êe biti njemu redefinirate varijablu, unutar tog (malog) bloka neÊete moÊi pristupiti varijabli definiranoj u velikom (vanjskom) bloku.
Ad free
Primjer 6 int i=3; { /* u ovom trenutku i je jos uvijek cjelobrojna varijabla vrijednosti 3 */ double i=1.2345; /* sad je i realna varijabla vrijednosti 1.2345 } /* izlaskom iz bloka sve lokalne varijable prestaju postojati, i je opet cjelobrojna vrijednosti 3 */ PodruËje valjanosti deklaracije varijable naziva se scope. OËito je da ovakav naËin rada kroz redefiniranje
k n j i æ i c a s u p u t s t v i m a PCextra
Nadamo se da znate kojim se redoslijedom izvode osnovne raËunske operacije. Prefiks operator ++ (poveÊaj vrijednost varijable za jedan) primjenjuje se pak prije svih osnovnih matematiËkih operacija. Tako je nakon sljedeÊeg kôda: int k=1; k+=++k+5; vrijednost varijable poveÊana na 9. Operator += zapravo znaËi “dodaj desnu stranu izraza varijabli s lijeve strane operatora; svi ne-C programeri ovo su navikli pisati kao k=k+... Naπ izraz bit Êe izraËunat sljedeÊim redom: najprije Êe se vrijednost varijable k poveÊati za 1 (++k), zatim Êe se tome dodati 5 i na kraju Êe se rezultat dodati varijabli k. Postfiks operator ++ izvrπava se zadnji. Tako Êe rezultat primjera: int k=1; k+=k+++5; biti 8: najprije se izraËuna k+5, doda se varijabli k i tek se onda izvrπava k++. Pripazite. Ovo je uzrok Ëestih poËetniËkih pogreπaka.
Grananja i petlje C++ je naslijedio sintaksu C-a za sve programske dijelove koji izvode grananja i petlje. Tako je dio programa if (uvjet) /* ako je zadovoljen uvjet */ blok_naredbi /* izvrsi ovaj blok naredbi */ else /* a ako pak uvjet nije zadovoljen */ blok_naredbi /* onda izvrsi ovaj blok */ zapravo dio C programa. I switch-case blok izgleda uobicajeno: switch (varijabla) { case vrijednost1: blok_1; case vrijednost2: blok_2; ... default: d_blok; } Ovisno o vrijednosti varijable, u switch naredbi bit Êe izvrπen jedan od blokova naredbi. Dok je blok naredbi unutar if petlje oznaËen klasiËno (toËkom-zarez - ako je rijeË o jednoj naredbi; vitiËastim zagradama - ako se radi o viπe naredbi), blokovi naredbi u switch-case ne postavljaju se unutar
C++ programiranje • vitiËastih zagrada. Isto tako, æelite li da se nakon naredbi u bloku 1 izvrπavanje programa nastavi iza switch bloka, kao zadnju naredbu u bloku koristite naredbu break. U suprotnom, nakon izvrπavanja naredbi u bloku 1, bit Êe izvrπene i naredbe u bloku 2 itd. Naredbe u d_bloku izvrπit Êe se ako vrijednost varijable ne odgovara nijednoj od popisanih vrijednosti u case izjavama. Osnovna petlja u C++ jeziku je for petlja. TipiËan izgled for petlje je for(definicija_poËetnih_vrijednosti; ispitivanje_uvjeta; neka_naredba) { /* tijelo petlje */ } Prvi dio (definicija...) sluæi za inicijalizaciju varijabli koje Êe se koristiti tijekom petlje. BuduÊi da se ovaj dio programa izvrπava samo jednom, i to prije same petlje, nema razloga da tu postavite naredbe koje nemaju nikakve veze s petljom. Ipak, kultura programiranja nalaæe
da se ovdje postave upravo inicijalizacijske vrijednosti brojaËa petlje. Ispitivanje uvjeta je drugi dio deklaracije petlje; ovdje moæete postaviti bilo koji izraz koji daje logiËku (istina-laæ) vrijednost. NajËeπÊe se koriste izrazi tipa i<=10 ili i!=10 ili sliËni. Nastojite li petljom obraditi samo jednu naredbu, moæete je postaviti na mjesto na kojem se nalazi neka_naredba. Ipak, ovaj se dio for naredbe tradicionalno koristi za uveÊavanje (umanjivanje) ili promjenu stanja kontrolnog brojaËa same petlje, a u tijelo petlje (izmeu vitiËastih zagrada) postavljaju se naredbe koje petlja mora izvrπiti. InaËe, zanimljivo je primijetiti da je u vrijeme buma C jezika sugeriran minimalizam, zbog Ëega ste sve πto ste mogli - trpali u jednu liniju. Tako je tada petlja for(n=100; n ; niz[n]= null ); bila in. Onda su programeri C-om poËeli izraivati velike projekte na kojima
i OPERATORI Dvije osnovne vrste operatora - aritmetiËki i logiËki - u C++-u se mogu proizvoljno mijeπati. Pritom je jedino bitno znati da C++ rezultat logiËkog operatora vraÊa kao cjelobrojnu vrijednost, pri Ëemu je istinita vrijednost oznaËena jedinicom, a neistina nulom. Tako su sasvim legalni izrazi tipa i=(ab i ab)*(ab) && (a
ii=(a
if ((a>=b)&&(b>=c)) cout << c<=b<=a << endl; else if ((c<=a)&&(c
5
je radilo viπe programera koji nisu imali volje ni strpljenja razmiπljati πto je pisac htio reÊi, pa smo se (na svu sreÊu) vratili korijenima - preglednom pisanju kôda. Dobro napisana petlja iz prethodnog primjera ipak je neπto Ëitljivija: for(n=100; n>0; n ) niz[n]= null ;
Ulazno-izlazne operacije C++ osim C-ovske printf() funkcije omoguÊava i jednostavnije baratanje standardnim ulazom (tipkovnica) i izlazom (monitor). UkljuËite li u svoj program klasu iostream, dobit Êete moguÊnost ispisivanja na ekran i uËitavanja podataka s tipkovnice. Pritom prilikom ispisa ne morate brinuti o tipu podataka koji πaljete na ispis (kao kod printf() funkcije); klasa iostream definirana je tako da prihvaÊa sve klasiËne tipove podataka. Prilikom ispisa koriπtenjem iostream klase intenzivno se koristi operator prosljeivanja (<<). Naredba cout << a; doslovno kaæe proslijedi sadræaj varijable a na standardni izlaz. Pritom uopÊe nije bitno kojeg je tipa a; pravilan ispis regulirat Êe sama klasa, a ne vi. Pri uËitavanju podataka trebate biti neπto paæljiviji. Deklarirate li a kao string, naredba cin >> a; je sintaksno posve toËna, no rezultati koje Êete dobiti moæda neÊe ispuniti vaπa oËekivanja. Naime, cin isporuËuje podatke redom kojim su upisani (tipkovnicom), no kao separator koristi se bjelina (space).Tako Êe unoπenje stringa “Kako je?” prouzroËiti smjeπtanje rijeËi Kako u varijablu a, dok Êe drugi dio reËenice (je?) biti zanemaren. Rjeπenje? Bacite pogled na opis getline() funkcije u help datoteci vaπeg prevoditelja... Naravno, nitko ne kaæe da ne smijete koristiti printf() funkciju; ponekad je njome lakπe formatirati ispis nego baciti se u prouËavanje iostream klase. Ulazno-izlazne operacije nad datotekama obavljaju Ëlanovi triju klasa: fstream, ofstream i ifstream. Posljednje dvije su derivati (izvedenice) fstream klase, koja jedina omoguÊava slobodno Ëitanje i pisanje po datoteci. Rad s takvim klasama donekle podsjeÊa na klasiËno C-ovsko baratanje datotekama. Uostalom, promotrite primjer 7. Primjer 7 string ss= Sadrzaj ovog stringa zelimo zapisati u datoteku ; fstream fd( test.dat , ios_base::out); fd.write((char*) &ss[0], ss.size()); fd.close(); Drugi redak primjera 7 nareuje
PCextra k n j i æ i c a s u p u t s t v i m a
6
• C++ programiranje
prevoditelju da kreira novi objekt imena fd i tipa fstream te da mu pridijeli ime datoteke test.dat, a posljednji argument (base::out) kaæe da Êemo u datoteku zapisivati podatke. SljedeÊa naredba zapisuje kompletan sadræaj stringa ss u datoteku test.dat. Naizgled Ëudna konverzija (char*) &ss[0] posljedica je tipa podataka kojeg smijemo koristiti kao buffer u funkcijama za zapisivanje podataka; ovdje smije stajati samo varijabla tipa (char*). Stoga smo naπ string ss pretvorili u pointer (char*) koji pokazuje na adresu prvog znaka ([0]) u stringu. Posljednji redak zatvara datoteku; time smo osigurali da svi podaci koji su moæda zaostali u bufferu budu uredno zapisani na disk prije terminacije programa.
Klase Klasa je zapravo samo nadgradnja tipova veÊ ugraenih u C++. Korisniku je omoguÊeno definiranje potpuno novih tipova - skupova podataka i funkcija koje obrauju te podatke. Iako implementacija klasa nije jednostavno programersko podruËje, dobro definirana klasa u konaËnici proizvodi kraÊi i pregledniji program te omoguÊuje ugodniji rad s manje pogreπaka. A to je neπto Ëemu svaki programer teæi... Umjesto pukog teoretiziranja, kreirajmo sami jednu klasu. Mnogima se neÊe svidjeti to πto kreiramo klasu Datum (“to je veÊ tako potroπeno...”), no ova je klasa gotovo idealna za ilustriranje principa kojih se valja dræati prilikom kreiranja klase. Osnovnu definiciju klase veÊ imamo (u primjeru 1). No, ono πto nam nedostaje jest elegantan naËin za inicijalizaciju objekta iz klase. Iako bi to mogla uËiniti i spomenuta funkcija init(), u klasama se primjenjuje drugaËiji princip. Uvodi se pojam konstr uktora - funkcije koja inicijalizira vrijednost varijable. Konstruktor mora imati isto ime kao i klasa, no ne mora se u definiciji klase pojaviti samo jednom.
i WINDOWS PROGRAMI
K
reiranje programa koji Êe raditi u Windows okolini zahtijevat Êe od vas solidno poznavanje klasa. Ipak, Ëak i potpuni poËetnik moæe se poigrati osnovnom Windows aplikacijom. Samo odaberite File, New, Projects, Win32 Application, pa na sljedeÊem ekranu odaberite A typical “Hello, world” application. I eto ga - imate Windows aplikaciju koja zna poneπto i ispisati na ekran, a ponaπa se kao Ëistokrvna Win32 aplikacija; moæete po æelji razvlaËiti prozor, minimizirati ga, iskljuËiti program odabirom File, Exit ili klikom na kvadratiÊ u gornjem desnom kutu prozora... Ipak, za iole smisleniju aplikaciju morat Êete se i sami malo pomuËiti. Dok Êete za ispisivanje vlastitih poruka u prozoru joπ moÊi proÊi samo s malo ËaËkanja po C++ kôdu kojeg imate pred sobom, veÊ Êe unoπenje vrijednosti u program traæiti malo Ëeprkanja po MSDN bibliotekama; na primjer, unos s tipkovnice dobro je opisan (i potkrijepljen primjerima) u Platform SDK, User Interface Services, User Input dijelu dokumentacije. »im se C++ Win32 programiranjem poËnete baviti neπto ozbiljnije, vidjet Êete da stvari ne idu baπ toliko glatko kao u DOS BASIC-u ili Ëak Visual BASIC-u, no rutina stvara brzinu, a brzina i mnoπtvo sati provedenih u ispravljanju pogreπaka - Ëine majstora... Za nestrpljive: nemojte po programu traæiti string “Hello, world!”; on se nalazi u odvojenoj resource datoteci (pronaite na ekranu ResourceView tipku, pa je kliknite; dalje biste se trebali snalaziti sami). ©to su resursi i kako obraivati poruke - πto je i bit Windows programiranja - tema je koju bismo ipak radije obradili u nastavku ove C++ πkole...
U naπoj klasi, na primjer, napravit Êemo Ëetiri konstruktora, a razlikovat Êe se po broju parametara koje oËekuju: jedan Êe konstruktor raditi bez oËekivanih parametara, drugi oËekuje jedan parametar, treÊi dva, a Ëetvrti tri parametra - dan, mjesec i godinu. Takva je klasa prikazana u primjeru 8. Primjer 8 class Datum { int d, m, g; public: Datum(); /* inicijalizacija bez parametara - datumska varijabla poprimit ce inicijalnu vrijednost */ Datum(int);/* ocekuje samo dan kao parametar, mjesec i godina ce biti postavljeni na podrazumijevane vrijednosti */ Datum(int, int); /*
Vjerovali ili ne, sve ove pogreπke ispisane su samo zbog jedne ispuπtene toËke-zareza...
Ad free
k n j i æ i c a s u p u t s t v i m a PCextra
ocekuje dan i mjesec */ Datum(int, int, int); / * ocekuje dan, mjesec i godinu */ void init(int dd, int mm, int gg); void dodaj_dan(int n); void dodaj_mjesec(int n); void dodaj_godinu(int n); }; Naravno, nitko nam ne brani da kreiramo joπ konstruktora. Primjerice, moæemo napisati konstruktor Date(const char*), koji kao parametar oËekuje datum u tekstualnom obliku, ili moæemo dodati joπ i konstruktore za double tip parametara... Tako su sad sve sljedeÊe deklaracije legalne: Datum danas; /* danas ce poprimiti danasnji datum, ukoliko je to podrazumijevana vrijednost */ Datum prvi(1,1,1); /* prvi sijecnja prve godine */ Datum praznik(1,12); /* prvi prosinca tekuce godine */ Jasno je da ovakva reprezentacija moæe dovesti i do zabune. Tko kaæe da Êe baπ svaki programer koji se sluæi vaπom definicijom klase podrazumijevati da dva parametra oznaËavaju baπ dan i mjesec, a ne, primjerice, mjesec i godinu? Nije li moguÊe shvatiti da Date(int, int, int) oËekuje najprije godinu, pa onda mjesec i dan? Stoga dizajniranje kompletne klase ukljuËuje i pisanje barem okvirnog naputka korisnicima te klase; ako niπta drugo, posluæiti Êe i samo nekoliko redaka komentara. Primijetili ste (nadamo se) da je u definiciji klase izostavljen programski kôd samih funkcija. Njih je moguÊe napisati
C++ programiranje • kasnije, imajuÊi na umu oznaËavanje pripadnosti funkcije. Tako Êe kôd Datum::Datum(int dan, int mjesec, int godina) { } definirati jedan od konstruktora klase Datum. Istim naËinom moæete kreirati i ostale funkcije klase. Naravno, nitko vam ne brani da kôd funkcija ubacite u samu definiciju klase, pri Ëemu, naravno, ne morate referencirati klasu dodatnom klasa:: oznakom. Gotovo svaka klasa sadræi podatke i funkcije do kojih korisnik ne smije izravno pristupati.Takvi se objekti smjeπtaju na sâm poËetak deklaracije klase ili u blok koji poËinje kljuËnom rijeËi private:. S druge strane, public: oznaËava objekte klase koji su vidljivi programu. Stoga je deklaracija klase Datum mogla izgledati i ovako: class Datum { public: Datum(); /* inicijalizacija bez parametara - datumska varijabla poprimit ce inicijalnu vrijednost */ Datum(int);/* ocekuje samo dan kao parametar, mjesec i godina ce biti postavljeni na podrazumijevane vrijednosti */ Datum(int, int); /* ocekuje dan i mjesec */ Datum(int, int, int); / * oŁekuje dan, mjesec i godinu */ private: int d, m, g; public: void init(int dd, int mm, int gg); void dodaj_dan(int n); void dodaj_mjesec(int n); void dodaj_godinu(int n); private: static Datum inicijalni_datum; }; Naravno, ovakav zapis najvjerojatnije Êe zbuniti i vas i krajnjeg korisnika klase. Stoga je ipak poæeljno grupirati public i private Ëlanove klase. KljuËna rijeË static oznaËava statiËku varijablu klase. Naime, inicijalizacijom svake varijable tipa Datum troπi se memorijski prostor za kopiju svih podataka neophodnih za funkcioniranje klase. Tako se u naπem sluËaju definicijom Datum novi_datum inicijalizira varijabla novi_datum, πto ukljuËuje i rezerviranje memorijskog prostora za pohranjivanje
7
i BIBLIOTEKE
N
abrajanje svih standardnih biblioteka i opis njihovih funkcija zapravo bi zauzelo kompletan prostor predvien za ovaj umetak. Stoga Êemo se u ovom prikazu ograniËiti samo na (po nama) poËetnicima korisne stvari. Svaka standardna C++ biblioteka sadræi nekoliko klasa kontejnera. O ovim tvorevinama mislite kao o spremnicima nizova vaπih podataka. Primjerice, zatrebate li ikad mehanizam koji Êe pohranjivati parove podataka u sortiranom redoslijedu (pri Ëemu se vi uopÊe ne morate muËiti oko sortiranja elemenata, rezerviranja prostora na heapu i sliËnih sitnica), koristit Êete funkcije iz standardne biblioteke map. Zatreba li vam samo efikasan naËin alociranja i dealociranja proizvoljnih koliËina memorije, tu je vector. Baπ vam treba implementacija reda, ali da su objekti u redu automatski poredani po vaænosti? Kontejner priority_queue usliπat Êe vaπe molbe... Svaki od ovih kontejnera sam za sebe neÊe vrijediti previπe, nemate li uz to i standardne funkcije za pristup i ubacivanje podataka. Biblioteka algorithm, na sreÊu, sadræi upravo takve najËeπÊe koriπtene funkcije. VeÊina standardnih funkcija ima i podvrste. Tako, na primjer, osim funkcije sort() koja u prosjeËnom sluËaju ima vrijeme izvrπavanja proporcionalno s n*log(n), moæete koristiti i stable_sort(). Ova funkcija izvrπava se u vremenu koje ovisi o n*log2(n), no u najgorem sluËaju (obrnuto sortirana lista) bit Êe bræa od funkcije sort(). Isto tako, sort() ne Ëuva meusobni poredak elemenata, dok stable_sort() Ëuva. Osim ovih, postoje joπ i funkcije partial_sort() i partial_sort_copy() za sortiranje samo dijela kontejnera, a sliËne su dodatne funkcije dostupne za joπ neke osnovne funkcije pobrojane u tabeli. Standardni kontejneri list Implementacija tipiËne liste - tipiËne operacije su ubaci element, nai element, pomakni se naprijed/natrag za jedno mjesto... map Mapa je lista podataka tipa kljuË-vrijednost. Ne smije i ne moæe sadræavati dva jednaka kljuËa. multimap U potpunosti jednaka mapi, osim πto dozvoljava viπestruko pojavljivanje istog kljuËa s razliËitim vrijednostima. priority_queue Red u kojeg se elementi ubacuju na mjesto ovisno o danom prioritetu. queue Kontejner koji sadræi elemente koji se ubacuju na kraj (back()), a izbacuju s poËetka (front()) - dakle, tipiËan red. set Implementacija skupa zapravo je (gotovo) identiËna implementaciji mape, osim πto se drugi podatak (argument) zanemaruje. stack KlasiËan LIFO red; posljednji element ubaËen na stack bit Êe i prvi s njega povuËen... vector Generalna klasa za alokaciju memorije te baratanje listama podataka. Standardni algoritmi for_each(poc, kraj, f()) find(poc, kraj, vrijednost) find_if(poc, kraj, f()) count(poc, kraj, vrijednost) count_if(poc, kraj, f()) replace(poc, kraj, p1, p2) replace_if(poc, kraj, f(), p2) copy(poc, kraj, novi) unique_copy(poc, kraj, novi) sort(poc, kraj) merge(p1, k1, p2, k2, novi)
Nad svakim objektom kontejnera koji je izmeu poc i kraj izvrπit Êe se funkcija f() Nalazi prvi objekt vrijednosti vrijednost Nalazi prvi objekt za kojeg funkcija f() vraÊa istinu (true) Prebroji sve objekte jednake vrijednosti SliËno kao find_if() Zamjenjuje sva pojavljivanja p1 s p2 Zamjenjuje sve objekte za koje je f() istina vrijednoπÊu p2 Kopira sve elemente od poc do kraj u strukturu (ne nuæno kontejner) novi Isto kao copy(), osim πto eliminira ponavljajuÊe uzastopne elemente Sortira elemente od poc do kraj Spaja elemente p1 do k1 prvog i p2 do k2 drugog kontejnera u novi
dana, mjeseca i godine. S obzirom na to da je inicijalni_datum vrijednost koja se ne mijenja stvaranjem nove varijable, nije neophodno rezervirati prostor za nju u svakoj novoj varijabli tipa Datum. OËigledno je da se funkcije definirane u klasi Datum pozivaju na klasiËan naËin: dodaj_dan(praznik) postavit Êe vrijednost varijable praznik na drugi dan mjeseca prosinca. No, klase nude i neπto bolje, a to je samo-referenciranje. »esto je zgodno pisati neπto poput
praznik.dodaj_dan(3).dodaj_ mjesec(1); Ovaj Êe redak poveÊati vrijednost dana u varijabli praznik za tri, a uz to Êe poveÊati i mjesec za jedan. Sve πto je potrebno za ovakve (naizgled) egzibicije jest redefinirati sve funkcije koje æelimo ovako pozivati. Stoga bi definicija funkcija koje nisu konstruktori morala izgledati ovako: Date& init(int dd, int mm, int gg); Date& dodaj_dan(int n);
PCextra k n j i æ i c a s u p u t s t v i m a
8
• C++ programiranje
Date& dodaj_mjesec(int n); Date& dodaj_godinu(int n); Sve πto smo napravili jest postavljanje tipa kojeg funkcije vraÊaju. Dok u primjeru 8 funkcije ne vraÊaju nikakvu vrijednost, u prethodnim redovima definirano je da funkcije vraÊaju tip Date. Tako je sad moguÊe pozivati funkcije klase njihovim navoenjem iza imena varijable tipa Datum. Ugraeni tipovi automatski se brinu i o joπ neËemu. »im program izae iz podruËja vrijednosti neke varijable (sjetite se, ovo se zove scope) - πto znaËi zavrπetkom bloka ili funkcije u kojoj je varijabla definirana memorija zauzeta varijablom vraÊa se sistemu. Ovakvo ËiπÊenje mora omoguÊiti i korisniËki definirana klasa. Funkcije koje obavljaju ove poslove nazivaju se destruktori. TipiËan destruktor jednostavnog je oblika. Sve πto treba jest navesti znak “~” (tilda) ispred imena klase i - eto vam destruktora. Promotrite primjer 9:
MSDN - viπe nego iscrpan sustav pomoÊi programerima
s[velicinaPrimjer 9 class Cache { char* s; size_t velicina; public: Cache(size_t bajtova=1024) { s = new char[velicina=bajtova]; } ~Cache() { delete[] s; } Cache& insert(int mjesto, char znak) { s[mjesto]=znak; return *this; } Cache& insert(int mjesto, char* str) { int l=strlen(str); if ( mjesto+l
1]= \0’; return *this; } Cache& print() { cout << s << endl; return *this; } }; Primjer definira klasu Cache. Zamijetite vezu konstruktora Cache i destruktora ~Cache - jedan rezervira memorijski prostor, drugi ga oslobaa. Iz primjera je vidljiva i joπ jedna Ëinjenica: konstruiramo li funkciju koja vraÊa vrijednost, moramo i definirati koja je to vrijednost. KljuËna rijeË this oznaËava objekt kojim trenutaËno baratamo; u terminima klase zapravo se radi upravo o klasi samoj. Vaæno je primijetiti i da konstruktor, ovakav kakav je, dozvoljava i pisanje uobiËajene deklaracije Cache cc; no, kolika je u tom sluËaju veliËina cachea? Pametno je osigurati svoju klasu u ovakvim sluËajevima: Cache je osiguran navoenjem default (podrazumijevane) veliËine cachea od 1024 bajta u samoj deklaraciji konstruktora Cache(). UoËite i dvostruku definiciju funkcije insert(). KlasiËan C ovo neÊe progutati; rede finicija funkcije s istim imenom jednostavno proizvodi greπku. C++ dozvoljava da jedna funkcija ili
Ad free
k n j i æ i c a s u p u t s t v i m a PCextra
operator imaju viπestruku definiciju, no s razliËitim parametrima. Pogledate li paæljivije deklaracije funkcija insert(), uoËit Êete da prva funkcija preuzima kontrolu kad su kao parametri navedeni cijeli broj i znak, a druga Êe se izvrπiti kad proslijedite cijeli broj i niz znakova (char*). Ovo se naziva overload; moæda je preoptereÊenje preteπka rijeË, no i prevoditelju u æivotu moæe biti teπko... Klase dozvoljavaju i (re)definiranje standardnih operatora. Primjerice, poæelite li kopirati sadræaj varijable cache2 u varijablu cache1 (obje tipa Cache), standardna procedura izgledat Êe otprilike ovako: Cache& kopiraj(Cache& c2) { if (velicina==c2.velicina) for (int i=0; i
C++ programiranje • /* ... */ } Poseban problem predstavlja upotreba identiËnih imena funkcija u razliËitim klasama. Tako smo mogli definirati i funkciju Datum& kopiraj(Datum& d2) { dan=d2.dan; mjesec=d2.mjesec; godina=d2.godina; return *this; } u klasi Datum. No, sve funkcije koje kao rezultat vraÊaju tip klase kojoj pripadaju jednoznaËno su definirane. Tako Êe prevoditelj sâm znati odabrati kopiraj() funkciju koju Êe upotrijebiti, ovisno o tipu varijable nakon koje se funkcija poziva. Neka je d tipa Datum, a c tipa Cache. Sad Êe d.kopiraj(d2) pozvati funkciju iz klase Datum, a c.kopiraj(c2) prouzroËit Êe izvoenje kopiraj() funkcije iz klase Cache. Ova pogodnost vrijedi sve dok koristite funkcije koje vraÊaju tip; pokuπate li deklarirati funkcije void init(Datum& d) u klasi Datum i funkciju void init(Cache c) u klasi Cache, bit Êe problema. Koristite li obje klase u istom programu, pozivi init(d); /* d je tipa Datum */ init(c); /* c je tipa Cache */ jednostavno neÊe proÊi; bit Êete obavijeπteni o pogreπci. No, pokuπajte funkcije definirati ovako:
static void init(Datum& d) static void init(Cache& c) Naravno, svaku funkciju definirat Êete u svojoj klasi. Nakon toga Êe pozivi Cache::init(cc); Datum::init(d); proÊi bez poteπkoÊa. Sad Êe i Cache::init(d), gdje je d tipa Datum, prouzroËiti (logiËnu) poruku o greπci. Nakon takve funkcije moÊi Êete pisati d+=d1, gdje su obje varijable tipa Datum. Naravno, i osnovni operatori dozvoljavaju overload, tako da moæete definirati i Datum& operator+=(int) za dodavanje dana datumu, pa moæete smisliti i Datum& operator+=(double&), pa Datum& operator+=(const char...)...
Makro naredbe Omiljena igraËka C programera Ëesto sa sobom donosi velik rizik. Makro naredbe su naredbe pretprocesoru (program koji se izvrπava prije samog prevoditelja) kojima se jedan niz znakova zamjenjuje drugim. TipiËan makro ima ovakav oblik: #define ime opisno_ime Kad pretprocesor naie na pojavljivanje niza znakova ime, zamijenit Êe ga s opisno_ime. Makroi u C++-u su opasni iz viπe razloga. Na primjer, ne pribjegavaju overload pravilu. Isto tako, i mnoge makro naredbe su opasne; tipiËan je primjer Ëesta poËetniËka greπka #define kvadrat(a) a*a Naizgled, sve je u redu, no naie li
i SAVJETI
O
snovni problem svakog novopeËenog C ili C++ programera jest pronalaæenje greπaka u naizgled Ëistom kôdu. Na sreÊu, postoji nekoliko osnovnih pravila koja Êe vam pomoÊi pri pronalaæenju osnovnih greπaka. Prije svega, izbaci li prevoditelj u Build prozoru koju greπku, dvaput je kliknite je; to Êe vas odvesti u redak u kojem je greπka naena. Dobro analizirajte liniju (treba li vam toËka-zarez, moæda joπ koja zagrada...). No, Ëesto problem uopÊe ne leæi u toj liniji, pa dobro pogledajte i liniju ispred inkriminirane. Imate li toËku-zarez? A zagrade? A vitiËastu zagrada za kraj bloka? Ne pronaete li problematiËno mjesto, traæite i po prethodnim linijama; svaki C++ prevoditelj ipak Êe vam barem okvirno reÊi u Ëemu je problem... Svaku programsku liniju postavite u zaseban redak. Iako je prevoditelju potpuno svejedno hoÊete li pisati
int i=5, j=6, cout << i je ili mozda int i=5, j=6; int k=i+j; cout << i: if (i
k=i+j; cout << manji! ;
i:
<< i << endl; if (i
<< i << endl; i je manji! ;
oËito je da je drugi primjer jednostavnije Ëitljiv, a potkrade li vam se greπka, prevoditelj Êe vam pokazati toËnu liniju u kojoj ste pogrijeπili. »esto Êete pogrijeπiti i ako ne navedete oznaku za kraj bloka (zatvorena vitiËasta zagrada). Tada Êe prevoditelj locirati greπku u zadnjem redu programa - samo stoga πto smatra da blok nije zatvoren. Dobro proËeπljajte kôd i naite mjesto na kojem nedostaje “}”. Pokuπajte se pridræavati konvencije o upotrebi velikih i malih slova; nastavite li se nakon poËetnih koraka baviti programiranjem, ovo vam moæe uπtedjeti gomile briga. Dobra je praksa i komentirati program jer moæda Êete se sutra sjetiti πto ste htjeli uËiniti s funkcijom moja_funkcija(). Ako se pak vratite programu nakon nekoliko dana ili tjedana, vrlo Êete vjerojatno proËeπljati funkciju da shvatite πto ste njome htjeli reÊi. Kratak komentar na kraju svakog neoËiglednog retka programa πtedi æivce vama, ali i svim ostalim korisnicima vaπeg programa.
9
pretprocesor na redak kvadrat(a+2), on Êe ga zamijeniti s a+2*a+2, a rezultat tog izraza (3a+2) baπ i nije ono πto ste æeljeli (a+2) 2=(a 2+2a+4). I sljedeÊi makro priliËno je Ëest gost C programa: #define min(a,b) a
Standardne biblioteke Svaki C++ prevoditelj isporuËuje se s nekoliko standardnih biblioteka. VeÊina ih je korisna svim korisnicima, a manji je broj namijenjen onima s posebnim prohtjevima. Najvaænijih 13 biblioteka ukljuËeno je u STL (Standard Template Library) skup. Zajedno s 18 klasiËnih biblioteka iz C jezika ove biblioteke Ëine moÊno orue. Zaπto biste izmiπljali toplu vodu ako u nekoj od standardnih biblioteka veÊ postoji rjeπenje vaπeg problema? VeÊina funkcija iz biblioteka ukljuËena je u standardni prostor imena (namespace). OdluËite li iskuπati primjere iz ovog teksta, ubrzo Êete shvatiti da tu neπto ne πtima; prevoditelj Êe javljati o razliËitim greπkama. Sad pokuπajte u program ubaciti liniju using namespace std; Ovim ste omoguÊili slobodno koriπtenje imena funkcija i operatora ukljuËenih u standardni namespace. Primjerice, cout je Ëlan std namespacea, tako da biste ga bez gornje deklaracije mogli koristiti jedino kao std::cout. Poæelite li koristiti koju od standardnih biblioteka, potrebno ju je ukljuËiti u program. To se postiæe navoenjem retka #include na samom poËetku programa. Pripazite samo na razmake; nehotiËna upotreba razmaknice moæe vas koπtati zbunjenog promatranja monitora, jer i < ime>
PCextra k n j i æ i c a s u p u t s t v i m a
10
• C++ programiranje dozvoljavaju koriπtenje vanjskih (extern) funkcija definiranih u drugim dijelovima programa, pametna upotreba klasa ovakve bi probleme morala svesti na minimum. Zbog ogromnih moguÊnosti koje C++ pruæa, logiËno je oËekivati da Êe prevoenje iole duæeg projekta potrajati. Stoga joπ jednom ponavljamo: bez solidnog raËunala radije se nemojte upustiti u svijet C++-a, osim ako se namjeravate zaustaviti na osnovnim primjerima.
Visual C++
Samo za Visual C++ i MSDN trebat Êe vam 800 MB prostora na disku
(s razmakom izmeu “<“ i “i”) ne tretiraju se kao iste biblioteke... Ovaj tekst ne moæe biti referenca za sve Ëlanove svih biblioteka, no valja spomenuti barem neke od najkorisnijih biblioteka, pa stoga pogledajte tablicu uz tekst.
S vremenom je Microsoftov C++ postao najbrojniji alat C-programera πirom svijeta. Moæda i nije rijeË o najbræem, najboljem ili najpouzdanijem prevoditelju, no svaki Êe programer lako prihvatiti ovaj program. U danaπnjem je Windows svijetu nezamislivo razvijati program u okolini koja
Prevoenje (kompiliranje) Nastao kao odgovor na zahtjeve programera za pouzdanim alatom za razvoj velikih projekata, C dozvoljava grupni rad na nekoliko razina. Osnovna razina oËituje se u prevoenju programa koji se nalazi u viπe datoteka. Naravno, samo jedna od tih datoteka smije sadræavati funkciju main() kojom program zapravo zapoËinje izvrπavanje. Svaku od ostalih datoteka, naravno, valja ukljuËiti u prevoenje navoenjem #include naredbe u tijelu glavne datoteke (one u kojoj je main(). No, rad s viπe datoteka lako dovodi do problema. Pripazite na viπestruko definiranje globalnih varijabli. Nazovete li varijablu jednako u razliËitim dijelovima programa, tko zna hoÊe li to linker primijetiti ili Êe vaπ program neispravno raditi (“a sve je teoretski savrπeno ispravno...”). Valja pripaziti i na ponavljanje imena funkcija; iako i C++ ima mehanizme koji
Iscrpan pregled klasa upotrijebljenih u programu
ne funkcionira po IDE (Integrated Development Environment = integrirano razvojno okruæje) principu. I Visual C++ je takav, a kao Ëlan Visual Studio paketa nudi i mnogo viπe od Ëistog razvoja programa u integriranom editoru. Dodatni set alata zadovoljit Êe i najprobirljivije; razni alati za ispitivanje ponaπanja aplikacije naslanjaju se na ugraeni debugger. Grupe Êe najviπe profitirati SourceSafe alatom, koji se brine o usklaenosti cijelog projekta. PoËetnici Êe pak itekako profitirati MSDN bibliotekom. Radi se o gotovo gigabajtu HTML baziranog sistema pomoÊi, koji Êe vam, budete li samo dovoljno uporni u traæenju, dati odgovore na baπ sva pitanja o Microsoftovom C++ prevoditelju, kao i o C++ jeziku. BuduÊi da je Visual C++
Ad free
k n j i æ i c a s u p u t s t v i m a PCextra
i KORISNE INTERNET STRANICE • http://www.austinlinks.com/ CPlusPlus/ • http://www.phys.ualberta.ca/~binliu/ cc.htm • http://www.ameritech.net/users/ mbelan/program.html • http://www.cplusplus.org/
matiËni alat autora teksta, svi su primjeri pisani i provjeravani Visual C++-om, no to ne znaËi da neÊe raditi i na bilo kojem drugom C++ prevoditelju, dokle god se taj prevoditelj dræi osnovnih C++ konvencija. Rad u Visual C++-u zapoËet Êete kreiranjem novog radnog prostora (Workspace). Pritom valja odmah na poËetku odabrati okruæje kojem je vaπ projekt namijenjen. Planirate li se ograniËiti na osnovne C++ primjere, odaberite konzolnu aplikaciju. Kliknite, dakle, na File, pa New, Projects. Odaberite Win32 Console Application i dajte svojem projektu ime. Na sljedeÊem ekranu odaberite An empty project i potvrdite svoj izbor. Otvorite novu datoteku (File, New, Files) i odaberite C++ source program kao tip datoteke. Nazovite datoteku kako æelite (kod nas se zove glavni) i - to je to. Joπ podesite izgled prozora po svojim æeljama (Tools, Options, Format; iskuπajte Lucida Console i Verdana fontove veliËine 9 toËaka) i spremni ste za igranje. Dok ne svladate osnove C++-a, dobro je kompletan program dræati u jednoj datoteci; tako Êete lakπe pronaÊi greπke i ukloniti ih. (Naravno, znate li makar beknuti engleski...) Poæelite li promijeniti smjeπtaj i veliËinu prozora, primijetite da se svi prozori mogu slobodno pomicati i razvlaËiti; jest da nisu pomiËni u klasiËnom Windows stilu, ali potrudite se malo... Pokretanje kompiliranja obavlja se pritiskom na CTRL+F7, a proe li sve u redu, moæete i napraviti exe datoteku - samo pritisnite CTRL+F5 a prevoditelj i linker Êe sami ustanoviti treba li joπ πtogod napraviti. Naravno, u sluËaju pogreπaka neÊe biti izraen izvrπni program, veÊ Êe rad biti prekinut a u Build prozoru dobit Êete poruku o pogreπci. Zapravo, vjerojatnije je da se radi o nekoliko pogreπaka. NajËeπÊa je pogreπka ispuπtanje toËke-zareza; upravo je fascinantno koliko greπaka prevoditelj moæe ispisati zbog samo jedne ispuπtene toËkezareza... Preporuka: u tom je sluËaju dovoljno kliknuti na opis prve greπke. Integrirana razvojna okolina dolazi do izraæaja; kursor Êe biti postavljen u programsku liniju u kojoj je (po miπljenju prevodioca) doπlo do pogreπke. Nedostaje li toËka-zarez, pomaknite se liniju iznad one koju prevoditelj prijavljuje i dobro odmjerenim pritiskom unesite vraæju toËkuzarez... n