C++ (deo udzbenika).pdf

November 20, 2017 | Author: Marko Kostic | Category: N/A
Share Embed Donate


Short Description

Download C++ (deo udzbenika).pdf...

Description

ELEKTRONSKI FAKULTET U NIŠU Katedra za računarstvo

Milena Stanković, Suzana Stojković, Miloš Radmanović, Ivan Petković

OBJEKTNO ORIJENTISANI JEZICI C++ i JAVA sa rešenim zadacima

Maj, 2005

OBJEKTNO ORIJENTISANI JEZICI C++ I JAVA sa rešenim zadacima

Dr Milena Stanković, Mr Suzana Stojković, Miloš Radmanović, Ivan Petković PROGRAMSKI JEZICI C++ I JAVA SA REŠENIM ZADACIMA Izdavač:

Elektronski fakultet u Nišu P. fah 73, 18000 Niš http://www.elfak.ni.ac.yu

Recenzenti:

Dr Živko Tošić. Dr Dragan Janković

Glavni i odgovorni urednik: Doc.dr Zoran Perić Odlukom Nastavno-naučnog veća Elektronskog fakulteta u Nišu, br. 1/05-151/05002 od 20.06.2005., rukopis je odobren za štampu kao pomoćni udžbenik. ISBN 86-85195-08-X CIP - Каталогизација у публикацији Народна библиотека Србије, Београд 004.432.2.C++(075.8) 004.438JAVA(075.8). OBJEKTNO orjentisani jezici C++ i Java : sa rešenim zadacima / STANKOVIĆ, Milena ...[et. al.]. – Niš : Elektronski Fakultet, 2005 (Niš : Mikops). – VI, 165 str. : graf. Prikazi ; 24 cm. – (Edicija Pomoćni udžbenici / [Elektronski fakultet, Niš]) Na vrhu nasl. str.: Univerzitet u Nišu, Katedra za računarstvo. - Tiraž 300. – Bibliografija: str. 165. ISBN 86-85195-08-X 1. Станковић, Милена а) Програмски језик “C++” b) Програмски језик “Java” COBISS.SR-ID 123772172

Preštampavanje ili umnožavanje ove knjige nije dozvoljeno bez pismene dozvole izdavača. Tiraž: 300 primeraka  Štampa: Mikops, Niš

SADRŽAJ SADRŽAJ

i

Kako koristiti udžbenik ?

vi

1.

UVOD U OBJEKTNO ORIJENTISANO PROGRAMIRANJE 1

1.1

Objektno orjentisana paradigma

1

1.2

Apstrakcija

1

1.3

Definicija objekta

2

1.4

Zatvaranje (Inkapsulacija)

2

1.5

Definicija klase

4

2.

C++ KLASE

6

2.1

Definisanje klase u jeziku C++

6

2.2

Kreiranje objekata

8

2.3

Konstruktori

10

2.4

Uništavanje (brisanje) objekata

14

2.5

Pristup funkcijama i atributima klase

16

2.6

Prijateljske funkcije klase

18

2.7

Operatorske funkcije

20

2.8

Zajednički članovi klasa

24

3.

NASLEĐIVANJE KLASA

3.1

Generalizacija

26

3.2

Nasleđivanje

26

26

i

3.3

Polimorfizam

30

3.4

Višestruko nasleđivanje

35

3.5

Virtualne osnovne klase

35

4.

С++ ŠABLONI

4.1

Generisanje funkcija

39

4.2

Generisanje klasa

39

5.

ULAZ/IZLAZ

5.1

Ulazni tok

42

5.2

Izlazni tok

46

6.

OBRADA IZUZETAKA

6.1

Koncept obrade izuzetaka

51

6.2

Sintaksa

51

7.

С++ - PRIMERI REŠENIH ZADATAKA

55

8.

UVOD U PROGRAMSKI JEZIK JAVA

98

8.1

Prevođenje programa

98

8.2

Put JAVA programa od sors-koda do izvršenja

98

8.3

Istorijat razvoja programskog jezika Java

99

9.

OSNOVNI KONCEPTI PROGRAMIRANJA U JAVI

100

10.

ELEMENTI PROGRAMSKOG JEZIKA JAVA

103

10.1

Leksički elementi programskog jezika Java

103

10.2

Promenljive i tipovi podataka

106

10.3

Upravljačke strukture u Javi

108

38

41

50

11.

KLASE U JAVI

112

11.1

Definicija klase

112

11.2

Kreiranje Java aplikacije

113

11.3

Metodi sa preklopljenim imenima

116

11.4

Konstruktori u Javi

118

11.5

Završni metodi

120

11.6

Preklopljeni metodi

120

11.7

Modifikator final

121

11.8

Modifikatori pristupa

122

11.9

Konverzija tipova podataka

122

12.

APSTRAKTNE KLASE I INTERFEJSI

12.1

Apstraktne klase

124

12.2

Interfejsi

125

13.

PAKETI

129

13.1

Korišćenje paketa

129

13.2

Kreiranje novog paketa

129

14.

JAVA - BIBLIOTKA KLASA

14.1

Paket java.lang

132

14.2

Paket java.io

134

14.3

Klase za rad sa character-tokovima podataka

137

15.

JAVA - PRIMERI REŠENIH ZADATAKA

144

16.

LITERATURA

165

iii

124

132

SPISAK REŠENIH ZADATAKA Zadatak 7.1 Klasa Vektor ................................................................................... 55 Zadatak 7.2 Matrica - klasa.................................................................................... 57 Zadatak 7.3 Lančana lista (dokument) - klasa ....................................................... 61 Zadatak 7.4 Dvostruko spregnuta lančana lista – klasa ......................................... 66 Zadatak 7.5 Vektor – izvedena klasa ..................................................................... 70 Zadatak 7.6 Studenti – izvedena iz apstraktne klase .............................................. 72 Zadatak 7.7 Krug – izvodjenje u više koraka ........................................................ 75 Zadatak 7.8 Radnici – dve izvedene klase ............................................................. 76 Zadatak 7.9 “Fuzija“ nizova objekata – generička funkcija .................................. 81 Zadatak 7.10 Stek (vektor) – generička klasa ........................................................ 84 Zadatak 7.11 Matrica – generička klasa ................................................................ 86 Zadatak 7.12 Zamena elementa u matrici – binarna datoteka................................ 90 Zadatak 7.13 Šifriranje teksta – tekstualna datoteka, obrada izuzetaka ................. 94 Zad atak 11.1 Rad sa datumima ....................................................................... 113 Zad atak 11.2 Izračunavanje vremena za koje se puni telo valjkastog oblika ... 117 Zad atak 11.3 Izračunavanje površine kružnog prstena ................................... 120 Zad atak 12.1 Izračunavanje površine i zapremine rogljastih tela ................... 124 Zad atak 12.2 Implementacija iteratora ........................................................... 126 Zad atak 13.1 R ad s a mag acino m ............................................................... 129 Zad atak 14.1 Sortiranje vektora (rad sa datotekama) ..................................... 138 Zad atak 14.2 Sumiranje elemenata niza (rad sa tekstualnim tokovima podataka) ...................................................................................................................... 141 Zad atak 15.1 Rad sa linernom i kvadratnom funkcijom.................................. 144 Zad atak 15.2 Nalaženje izvoda polinoma ....................................................... 148 Zad atak 15.3 Sabiranje brojeva proizvoljne dužine u brojnom sistemu proizvoljne osnove ........................................................................................ 151 Zad atak 15.4 Rad sa displejima ...................................................................... 155 Zad atak 15.5 Nalaženje nule funkcije metodom polovljenja intervala ........... 158 Zad atak 15.6 Kreiranje platnog spiska ........................................................... 160

iv

PREDGOVOR

Objektno orijentisano programiranje je široko prihvaćena tehnika programiranja koja se danas standardno primenjuje u razvoju softvera. Izučavanje objektno orijentisanih programskih jezika je sastavni deo nastavnih programa na svim školama u kojima se pripremnaju kadrovi iz oblasti računarstva. Objektno orijentisani jezici se izučavaju na Smeru za računarsku tehniku i infomatiku Elektronskog fakulteta u Nišu već više od deset godina. Kako je literatura iz ove oblasti oskudna autori su se prihvatili zadatka da materijal koji je dugo godina korišćen u održavanju predavanja, računskih i laboratorijskih vežbi iz ove oblasti pripreme u vidu udžbenika. Ovaj udžbenik se praktično sastoji iz dva dela od kojih se prvi odnosi na programski jezik C++, a drugi na programski jezik Java. Ova dva jezika su izabrana zbog toga što su zastupljena u nastavnom programu iz predmeta Programski jezici ali i zbog toga što su to u praksi veoma zastupljeni programski jezici. Ovaj udžbenik je prvenstveno namenjen studentima III godine Elektronskog fakulteta u Nišu koji izučavaju objektno orijentisane jezike u okviru predmeta Programski jezici, a koriste ih i u okviru nekoliko drugih predmeta. Kako je novim programom studija na Elektronskom fakultetu predviđen predmet Objektno orijentisano programiranje, ovaj udžbrnik će moći da se koristi i u nastavi iz ovog predmeta. Nadamo se da će udžbenik takođe biti od koristi i svima onima koji izučavaju objektno orijentisane programske jezike na drugim fakultetima ili su odlučili da samostalno savladaju neki od ovih jezika. Posebno želimo da zahvalimo recenzentima prof. dr Živku Tošiću i docentu dr Draganu Jankoviću, na uloženom trudu da detaljno pročitaju rukopis udžbenika i korisnim predlozima i primedbama. Takođe, ćemo iskreno biti zahvalni svim korisnicima udžbenika koji nam budu ukazali na propuste i štamparske greške, kojih sigurno ima, i pored našeg nastojanja da ih svedemo na minimum.

v

Kako koristiti udžbenik ? Ovaj udžbenik je pripremljen u vidu tutorijala, i ne objašnjava sve karakteristike jezika C++ i Java. Cilj je da polaznik na praktičnim i konciznim primerima ovlada osnovnim konceptima objektno orijentisanog programiranja, kao i programiranja primenom jezika C++ i Java. Za potpuniju listu karakteristika ovih jezika poželjno je koristiti dodatnu literaturu koja je navedena na kraju udžbenika. Uz deo udžbenika koji se odnosi na programski jezik C++ idu i dodatni primeri gotovi projekti realizovani u Microsoft Visual Studio 6 okruženju, koji su dostupni na sajtu Katedre za računarstvo Elektronskog fakulteta u Nišu http://cs.elfak.ni.ac.yu. Radi lakšeg korišćenja udžbenika, neki njegovi delovi su drugačije formatirani. Programski kod u primerima i rešenim zadacima je predstavljen Courier fontom. Naredbe koje su pridodate postojećem kodu u skladu sa nekim prethodnim objašnjenjem biće označene Courier fontom, bold. Tekst ispisan masnim slovima i uokviren na sivoj pozadini predstavlja važnu napomenu. Referenciranje na kompletan projekat koji se nalazi na sajtu http://cs.elfak.ni.ac.yu biće predstavljeno pravougaonikom sledećeg oblika: Poglavlje 2 - 01 - Kreiranje objekta

vi

I deo

Programski jezik C++

7

viii

UVOD U OBJEKTNO ORIJENTISANO PROGRAMIRANJE

1.

UVOD U OBJEKTNO ORIJENTISANO PROGRAMIRANJE

1.1 Objektno orjentisana paradigma U svakodnevnom životu često upotrebljavamo pojam objekat, a da pri tome ni ne pokušavamo da ga definišemo. Čovek na svom putu do posla ulazi u kafić, naručuje kafu, sedne, popije je, i zatim nastavi put. U kafiću postoji više objekata: sam lokal, sto, kafemat, kafa, itd. Svaki objekat ima više različitih karakteristika (atributa) koje ga opisuju: kafić može da bude otvoren ili zatvoren; kafa može da bude gorka, srednje slatka ili veoma slatka i da ima određenu cenu; stolovi mogu da budu zauzeti ili slobodni. Takođe, neki objekti mogu da vrše i određene funkcije, npr. kafemat može da pravi kafu, konobarica može da donosi kafu. Objektna orijentacija je tehnika modelovanje sistema; gde je sistem softverski sistem ili sistem u opštem smislu, npr. kafić iz prethodnog primera. Objektna orijentacija opisuje, ili modeluje, sistem kao skup objekata koji su u međusobnoj interakciji. Objektna orjentacija je slična načinu na koji ljudi doživljavaju okolinu. Objektno orjentisana paradigma (ili OO) je savremeni pristup programiranju i sastoji se od nekoliko koncepata, relativno novih u svetu programiranja, kao što su smanjenje složenosti razdvajanjem programa na manje module, smanjenje složenosti kroz apstrakciju (vidi 1.2), i korišćenje koncepta ponovne upotrebljivosti (reuseability) radi lakšeg i bržeg razvoja aplikacija.

1.2 Apstrakcija Ljudi gledaju na fizički svet sa različitim nivoima apstrakcije. Vozač vidi kola kao transportni mehanizam sa točkovima, sedištima i rezervoarom (neki nisu čuli ni da postoji ulje i da treba da se menja). Sa druge strane, automehaničar shvata kola detaljnije: kao skup raznih mehaničkih delova koji pokreću kola. Telefon apstrakuje detalje o načinu uspostavljanja veze – jednostavno, korisnika ne interesuje šta se dešava pri uspostavljanju veze – njemu je važno da on može da razgovara. Apstrakcija je proces ignorisanja detalja radi koncentrisanja na važnim, suštinskim karakteristikama. Apstrakcija se fokusira na najvažnijim svojstvima nekog objekta, kao što je njegova svojstva ili funkcije, i ignoriše nepotrebne detalje. Na primer, motor ima dva točka i jedno sedište, dok automobil ima četiri točka i četiri sedišta. Ali, i motor i automobili su u suštini prevozna sredstva. Do ovog zaključka smo došli apstrakcijom detalja. 1

PROGRAMSKI JEZIK C++

1.3 Definicija objekta Recimo da hoćemo da napravimo prodavanicu automobila. Zapažamo da automobil ima skup atributa koji ga opisuju (snaga motora, boja, klima, ...) kao i skup funkcija koje on može da izvršava (startovanje motora, grejanje sedišta, ubrzavanje,...). Sve te karakteristike ćemo staviti u katalog performansi automobila. Međutim, osim gore navedenih atributa, ovaj automobil ima i XYZ šrafova i X metara žica. Da li je to potencijalnom kupcu automobila bitno? Naravno da nije. Zbog toga ignorišemo nebitne detalje, a zadržavamo samo nama bitne. Kao rezultat ove apstrakcije dobijamo objekat. Taj objekat je apstrakovana predstava automobila zato što zanemaruje nama nebitne detalje. Objekti su apstrahovane predstave stvari. Objekti je definisan atributima i funkcijama. Međutim, ovako apstrakovan objekat je dobar samo u kontekstu prodavnice automobila. Šta ako hoćemo da napravimo prodavnicu automobilskih delova? Da li bi nam u tom slučaju takav objekat bio adekvatan. Ne, jer smo zanemarili detalje koji su u ovom slučaju bitni! Glavni problem u objektno-orjentisanom razvoju je nalaženje pravih objekata da bi se modelovao predstavljeni sistem. Identifikovanje objekata u kontekstu problema je umetnost, a ne nauka, tj. ne postoji jednoznačno rešenje. Objekti imaju sledeće karakteristike: •

Objekti mogu da budu realni (stvarni) ili imaginarni (izmišljeni),



Objekti mogu da budu jednostavni ili složeni,



Sastoje se od atributa i funkcija,



Nezavisni su,



Odgovaraju kontekstu problema.

Na primer, automobil je složeni i realan objekat. Međutim, objekat ne mora uvek da ima fizičku predstavu, to može da bude i koncept. Na primer, bankovni račun je više koncept nego fizički objekat.

1.4 Zatvaranje (Inkapsulacija) Da li, da bi obavili tevefonski razgovor, morate da znate kako se uspostavlja sama telefonska veza? Ne, potrebno je samo da razumete kako da koristite telefon, tj. da razumete interfejs (interface – def. stanje ili tačka preko koje različite stvari interaguju) koji se sastoji od dugmića, zvučnika i mikrofona. Možete ignorisati interni proces uspostavljanja veze koja možda prolazi i kroz nekoliko zemalja. Ukoliko se 2

UVOD U OBJEKTNO ORIJENTISANO PROGRAMIRANJE

tehnologija u uspostavljanju veze promeni, ili promenite operatera, vi ćete i dalje telefonirati na isti način. Inkapsulacija razdvaja spoljašnje aspekte objekta, koji su dostupni drugim objektima, od internih (unutrašnjih) implementacionih detalja objekta, koji su skriveni od ostalih objekata. Kupili smo automobil. Njegova implementacija su sva tehnička rešenja smeštena ispod haube. Nas ne interesuje kako motor radi. Njegov interfejs su volan, kvačilo, gas, kočnica i ostale sitnice u kabini. Mi koristimo objekat (automobil) preko interfejsa. Međutim, u nekom trenutku odlučimo da ugradimo sistem za alternativno gorivo (plin). Menja se samo implementacioni deo, dok interfejs ostaje isti. Pošto je interfejs ostao isti, mi (drugi objekat koji koristi objekat automobil) ne moramo da ponovo učimo kako da koristimo automobil (drugi objekti koji koriste promenjeni objekat se ne menjaju). Prednost inkapsulacije, koja krije implementacione detalje, je u tome da se objekat može promeniti. Ukoliko se samo dese promene u implementaciji, koja je inače skrivena, i ukoliko je novi interfejs (deo dostupan ostalim objektima) ostao isti kao original, objekti koji koriste promenjeni objekat ostaju isti. Recimo da vozač hoće da zaustavi automobil pritiskom papučice za kočenje (operacija interfejsa) koja pomoću hidrauličnog sistema (implementacija) smanjuje brzinu automobila (privatni atribut, deo internih informacija). Vozač uopšte ne mora da zna da postoji hidraulični sistem za kočenje da bi ukočio automobil. Objekat se sastoji iz tri dela tj. aspekta: •

Javno dostupnog interfejsa: spoljašnji deo koji ostali objekti koriste za interakciju sa tim objektom (papučice za kočenje, gas, volan automobila,...)



Implementacije: interne funkcije i ostali implementacioni detalji; svrha objekta (motor i ostala mehanika automobila)



Interne informacije: šta bi objekat trebalo da zna da bi izvršio funkciju. (brzina automobila,...)

Funkcije i atributi nekog objekta se obično nazivaju članovima objekta. Članovi mogu da budu javni (public), privatni (private) ili zaštićeni (protected). Ukoliko je član definisan kao javni, bez obzira da li se radi o atributu ili operaciji, on postaje deo interfejsa, što znači da je javno dostupan. Ukoliko je član definisan kao privatni, onda je on član implementacije i nije javno dostupan.

3

PROGRAMSKI JEZIK C++

U potpuno objektno orjentisanim sistemima, svi atributi su privatni i mogu biti promenjeni ili dostupni samo preko javnih funkcija interfejsa.

1.5 Definicija klase Na Slici 1.1 su prikazani različiti objekti: automobil, brod, mlaznjak, motor, jedrilica, voz, dvokrilni avion i raketa. Ovaj skup objekata je moguće klasifikovati (grupisati) po nekim zajedničkim karakteristikama. Na primer, motor, automobil i lokomotiva se mogu svrstati u kopneni transport. Raketa, mlaznjak i dvokrilni avion bi predstavljali vazdušni transport. Brod i jedrilica predstavljaju vodeni transport.

Slika 1.1 Prevozna sredstva Objekti sa istim skupom karakteristika se grupišu u klase. Klase su šabloni kojima se opisuje struktura objekata. Kažemo da objekti pripadaju klasi koja ih opisuje, da predstavljaju primerke (uzorke, istance) klase. Moguće je sledeće poređenje: Ukoliko je klasa nacrt zgrade na papiru, onda su objekti sve zgrade izgrađene po tom nacrtu. Može se reći da klasa predstavlja logičku apstrakciju, dok objekat poseduje fizičku reprezentaciju. U našem primeru bi to značilo da objekti motor, automobil i lokomotiva pripadaju klasi kopneniTransport, raketa, mlaznjak i dvokrilni avion klasi

4

UVOD U OBJEKTNO ORIJENTISANO PROGRAMIRANJE

vazdusniTransport, i brod i jedrilica klasi morskiTransport, Slika 1.2.

Slika 1.2 Tri klase: za kopeneni, morski i vazdušni transport

5

PROGRAMSKI JEZIK C++

2.

C++ KLASE

2.1 Definisanje klase u jeziku C++ U programskom jeziku C++ klase su strukture kojima se opisuju objekti. Klasa opisuje svojstva objekata, njihove atribute i funkcije koje definišu ponašanje objekata. Na primer, kamion, autobus, automobil, motor i bicikal pored očiglednih razlika imaju jednu zajedničku karakteristiku – to su sve prevozna sredstva. Po tome se mogu svrstati u klasu vozilo koja će predstavljati skup svih (kopnenih) prevoznih sredstava. Deklarišemo klasu vozilo : class vozilo { }; Kada upotrebimo termin prevozno sredstvo, pozivamo se na klasu vozilo, jer se misli na bilo koji primerak klase koji se klasifikuje kao vozilo (nije bitno da li je to bicikal, automobil ili neko drugo prevozno sredstvo). U okviru bloka klase ( između zagrada{ i }) deklarišu se atributi i funkcije klase (umesto termina funkcije koriste se i termini: metodi ili operacije). U ovom udžbeniku biće korišćen termin funkcije. class vozilo { // atributi // funkcije }; Atributi i funkcije mogu da budu deklarisani kao private, protected ili public. Privatni atributi klase su dostupni samo unutar klase u kojoj su deklarisani dok su atributi deklarisani kao public javni i može im se pristupiti od strane drugih objekata koji su izvan klase. Atributi deklarisani kao protected su takođe privatni u odnosu na klasu u kojoj su deklarisani ali su dostupni i iz klasa koje su njeni potomci, o čemu će biti reči kasnije. Obično se atributi klase deklarišu kao private ili protected čime se ograničava direktan pristup ovim atributima spolja, a za promenu njihovih vrednosti se moraju koristiti eksplicitno definisane funkcije deklarisane kao public. Recimo, u klasi vozilo možemo da deklarišemo atribut stanje kojim ćemo označavati da je motor uključen ili isključen (1 – uključen, 0 – isključen).

6

C++ KLASE

class vozilo { private: int stanje; }; Klasi ćemo dodati dve funkcije, ukljuci() i iskljuci(), za paljenje i gašenje motora, respektivno. Prva funkcija atributu stanje klase vozilo dodeljuje vrednost 1, a druga vrednost 0. Ove dve funkcije ćemo deklarisati unutar klase vozilo kao public, da bi ih mogli pozivati izvan klase. U okviru deklaracije klase biće navedeno samo zaglavlje ovih funkcija, interfejs preko kojeg ih pozivamo, ali ne i telo funkcija. Deklaracija funkcija se daje prema sledećoj sintaksi: ( ); Na primer: int promeniBrzinu ( int brzina ); Unutar jedne klase moguće je definisati i veći broj funkcija sa istim imenom koje se razlikuju po broju i tipovima argumenata. Tako u klasi vozilo može da postoji i druga funkcija promeniBrzinu koja brzinu povećava, na primer, uvek za 1 i čija bi deklaracija imala sledeći izgled. int promeniBrzinu ( ); Parametri funkcije u programskom jeziku C++ mogu da imaju i svoje podrazumevane vrednosti. Pretpostavimo da u prvoj funkciji argument nije nova brzina vozila već korak promene brzine. Druga funkcija bi takođe vršila promenu brzine za zadati korak, ali u ovom slučaju bi korak bio unapred poznat i nema smisla navoditi ga. U tom slučaju funkcija za promenu brzine bila bi deklarisana na sledeći način: int promeniBrzinu ( int korak = 1 ); Ovo znači da, ukoliko u pozivu funkcije nije naveden korak promene brzine, njegova podrazumevana vrednost je 1. Kada funkcija ima veći broj parametara, parametri koji imaju podrazumevane vrednosti se pišu na kraju liste argumenata. Npr. int f1( float a, float b, int c=0, int d=200 ); Funkcija f1 može da se poziva navođenjem 2, 3 ili 4 stvarna argumenta. Ukoliko je funkcija pozvana sa 3 argumenta, podrazumeva se da je u pozivu navedena vrednost parametra c, dok se za parametar d uzima njegova podrazumevana 7

PROGRAMSKI JEZIK C++

vrednost. Nikako u pozivu ne može da bude izostavljena vrednost parametra c, a navedena vrednost parametra d. Zbog toga je kod navođenja argumenata sa podrazumevanim vrednostima veoma važno voditi računa o njihovom redosledu. Definisanje funkcije podrazumeva ispisivanje tela (koda) funkcije i uglavnom se vrši van deklaracije klase. Najčešće su deklaracije i definicije u različitim datotekama. Uobičajeno je da se definicija klase nalazi u datoteci sa nastavkom .h (u našem primeru to će biti datoteka vozilo.h), a definicija u datoteci sa nastavkom .cpp (vozilo.cpp, u našem primeru). (vozilo.h) class vozilo { private: int stanje; public: void ukljuci(); void iskljuci(); } Definisanje funkcija: (vozilo.cpp) void vozilo::ukljuci() { this->stanje = 1; } void vozilo::iskljuci() { this->stanje = 0; } U ovom primeru this je pokazivač na sam objekat klase čija je funkcija pozvana (u ovom slučaju je to klasa vozilo). Nije obavezno da se pokazivač this eksplicitno navodi. Moguće je navesti samo ime atributa klase. U primerima koji slede operator this će biti korišćen čisto radi lakšeg razumevanja koda.

2.2 Kreiranje objekata Pošto smo deklarisali i definisali klasu vozilo (i to zapisali u dve datoteke, vozilo.h i vozilo.cpp), kreiraćemo novu datoteku, main.cpp (ime je proizvoljno) u kojoj ćemo iskoristiti klasu vozilo. U te svrhe potrebno je sa 8

C++ KLASE

definišemo globalnu funkciju main()koju ćemo smestiti u ovu datoteku. Sve tri datoteke treba da budu u istom projektu. (main.cpp) void main() { } Da bi funkcija main() “videla” klasu vozilo, treba da, korišćenjem include direktive, uključimo header (zaglavlje) datoteku vozilo.h. (main.cpp) #include ”vozilo.h” void main() { } U okviru funkcije main vršimo kreiranje (stvaranje, konstruisanje) objekata, pravljenje primerka klase vozilo. To se može uraditi na dva načina: statički i dinamički: (main.cpp) #include ”vozilo.h” void main() { vozilo bmw; vozilo *yugo; yugo = new vozilo(); } Prvi način, statički: vozilo bmw; Klasa vozilo se koristi kao apstraktni tip podataka i kreira se objekat kao njen uzorak koji ima strukturu (atribute) opisanu klasom. Taj objekat se naziva stalni objekat. Drugi način, dinamički: vozilo *yugo; U ovom slučaju yugo je pokazivač na objekat klase vozilo, objekat nije kreiran, već to treba eksplicitno da se uradi pomoću operatora new. yugo = new vozilo(); 9

PROGRAMSKI JEZIK C++

Operator new služi za dinamičko alociranje (dodeljivanje) memorije (kao malloc u C-u). Tako napravljeni objekti su dinamički objekti. Sada u memoriji postoje dva objekta klase vozilo kojima možemo manipulisati: mw i yugo. U programu je moguće definisati i referencu (upućivač) na objekat klase vozilo. Referenca je drugo ime za neki podatak (objekat). Zbog toga reference moraju da se inicijalizuju prilikom definisanja: vozilo& strano = bmw; vozilo& domaće = *yugo; Parametri funkcija i atributi klase takođe mogu da budu objekti, pokazivači i reference. Ukoliko se objekat pojavi kao parametar funkcije u trenutku poziva funkcije pravi se kopija objekta koji se pojavio kao stvarni argument, a na kraju izvršavanja funkcije taj objekat se briše iz memorije. Kada se referenca pojavi kao parametar funkcije, od trenutka poziva funkcije to je drugo ime za objekat koji predstavlja odgovarajući stvarni argument. To znači da se referenca u praksi najčešće koristi da se omogući da se vrednosti stvarnih argumenata funkcije promene u njenom telu. Drugi razlog zašto se referenca koristi pri prenosu parametara je i ušteda u memoriji. Objekti mogu sadržati veliki broj atributa i veoma kompleksne atribute pa je pravljenje njihovih kopija prilikom poziva funkcije krajnje neekonomično. Ako se parametar u funkciji neće menjati iako je prenet po referenci, to se eksplicitno navodi pomoću ključne reči const ispred njegove deklaracije: void f1( const vozilo& automobil) Kada je referenca atribut klase onda se njena inicijalizacija vrši u konstruktoru (vidi sledeće poglavlje).

2.3 Konstruktori Proširićemo prethodni primer zahtevom da klasa vozilo ima i sledeće atribute: maksimalnu brzinu i trenutnu brzinu kao i funkcije: ubrzaj (kojom ubrzava) i uspori (kojom usporava). Takođe ćemo obraditi slučajeve kada vozilo pri ubrzavanju dostigne maksimalnu brzinu i kad pri usporavanju stane. (vozilo.h) class vozilo { private: int stanje; int max_brzina; int brzina; 10

C++ KLASE

public: void void void void

ukljuci(); iskljuci(); ubrzaj(); uspori();

} (vozilo.cpp) void vozilo::ukljuci() { this->stanje = 1; } void vozilo::iskljuci() { this->stanje = 0; } void vozilo::ubrzaj() { if (this->brzina < this->max_brzina) >brzina++; } void vozilo::uspori() { if (this->brzina > 0) this->brzina--; }

this-

Međutim, u našem primeru javlja se problem jer nismo definisali maksimalnu brzinu. To se može rešiti uvođenjem nove funkcije koja će dodeliti neku vrednost za maksimalnu brzinu. Ovo rešenje je dobro ukoliko svako vozilo (objekat tipa vozilo) ima različitu maksimalnu brzinu. Ali, šta ako hoćemo da napravimo stotinak vozila (objekata) identične maksimalne brzine? Ispisivanje stotinak naredbi nije baš efikasno rešenje. Najbolje rešenje bi bilo kada bi definisali inicijalnu (default) vrednost za maksimalnu brzinu svih vozila, tako da nije potrebno eksplicitno zadavati maksimalne brzine za svaki od ojekata klase vozilo. To je moguće korišćenjem konstruktora. Konstruktor je posebna funkcija klase koja ima isto ime kao i klasa ( T() za klasu T), a služi da se definišu početne vrednosti podataka klase. Konstruktor se poziva u trenutku kreiranja objekta klase. Može da bude bez argumenata ili da ima jedan ili više argumenata. Takođe, 11

PROGRAMSKI JEZIK C++

moguće je da postoji više od jednog konstruktora, pri čemu svaki mora da ima različitu listu argumenata. U zaglavlju klase vozilo deklarisaćemo konstruktor bez argumenata i u okviru njega definisati početne vrednosti atributa klase: (vozilo.h) class vozilo { private: int stanje; int max_brzina; int brzina; public: vozilo(); void ukljuci(); void iskljuci(); void ubrzaj(); void uspori(); } U izvornu (.cpp) datoteku treba dodati: vozilo::vozilo() { this->brzina = 0; this->max_brzina = 180; this->stanje = 0; } Time će pri inicijalizaciji objekata klase vozilo vrednost atributa max_brzina biti 180. Poglavlje 2 - 01 - Kreiranje objekta

Šta ako bi hteli da postoji mogućnost navođenja i druge maksimalne brzine prilikom kreiranja objekta? Rešenje bi bilo u uvođenju drugog konstruktora koji bi imao argument preko kojeg bi se manjala vrednost atributa max_brzina. (vozilo.h) class vozilo { private: int stanje; int max_brzina; int brzina; public: 12

C++ KLASE

vozilo(); vozilo(int max); void ukljuci(); void iskljuci(); void ubrzaj(); void uspori(); } Inicijalizacija atributa može da se navede u telu konstruktora, ali i u delu za inicijalizaciju. Deo za inicijalizaciju se piše iza zagrlvavlja konstruktora, a pre njegovog tela i od zaglavlja je odvojen simbolom :. U delu za inicijalizaciju pozivaju se konstruktori atributa kao ikonstruktor roditeljske klase (o tome će kasnije biti reči). Tu se obavezno inicijalizuju reference i konstantni atriburi. (vozilo.cpp) ... vozilo::vozilo(int max) : brzina(0), max_brzina(max), stanje(0) { } Koji će konstruktor biti pozvan određuje se na osnolu liste argumenata. Ukoliko kreiramo objekat korišćenjem prvog konstruktora (bez argumenata): vozilo bmw; ili vozilo *yugo = new vozilo; vrednost atributa max_brzina objekta bmw i yugo će biti 180. Ali, ukoliko kreiramo novi objekat korišćenjem drugog konstruktora (sa navođenjem željene maksimalne brzine): vozilo bmw(300); ili vozilo yugo = new vozilo(150); vrednost atributa max_brzina objekta bmw će biti 300, a objekta jugo 150. Poglavlje 2 - 02 - Klasa sa više konstruktora

Ukoliko u klasi nije definisan ni jedan konstruktor prevodilac će sam definisati konstruktor bez argumenata (takozvani podrazumevani konstruktor).

13

PROGRAMSKI JEZIK C++

Posebna vrsta konstruktora su konstruktori za kopiranje. Konstruktori za kopiranje prave kopiju postojećeg objekta. Konstruktor za kopiranje klase vozilo izgledao bi: vozilo::vozilo(vozilo& v) { this->brzina = v.brzina; this->max_brzina = v.max_brzina; this->stanje = v.stanje; } Postojeći objekat se konstruktoru za kopiranje obavezno prenosi po referenci. To je razumnljivo jer, ukoliko se parametar prenosi po vrednosti, u trenutku poziva funkcije se poziva konstruktor za kopiranje za sve objekte koji se prenose po vrednosti. Tako bi se dobila jedna beskonačna rekurzija koja bi na kraju dovela do „pada“ aplikacije. Konstruktor za kopiranje se obavezno pravi kada klasa sadrži dinamičke atribute. Ukoliko nije napravljen konstruktor za kopiranje, kada treba praviti kopiju objekta svi atributii postojećeg objekta se jednostavno prepišu u atribute novog. Ako su atributi postojećeg objekta pokazivači na druge objekte, kada se napravi novi objekat, vrednosti pokazivča će se prepisati, tako da će i stari i novi objekat ukazivati na iste dinamičke članove. To će dovesti do problema pri promenama takvih članove (ako se u starom objekat nešto izmeni, promena će se odraziti i na novi objekat, i obrnuto), ali će najveći problem biti pri brisanju objekata. Ako se obriše, recimo, novi objekat i on ispravno obriše i sve svoje dinamičke članove, aplikacija će ponovo da „padne“ ukoliko se iz starog objekta pokuša da pristupi tom dinamičkom članu, ili u trenutku kada se briše stari objekat pa i on pokuša da obriše svoje dinamičke članove (koji su već obrisani).

2.4 Uništavanje (brisanje) objekata Ukoliko nam neki objekti više nisu potrebni, preporučljivo je njihovo brisanje kako bi oslobodili zauzeti memorijski prostor. Specijalna funkcija klase koja se implicitno poziva u trenutku kada se objekat klase briše iz memorije je destruktor Destruktor je funkcija bez argumenata i u klasi može da postoji samo jedan destruktor. Identifikator destruktora je isti kao identifikator klase, sa prefiksom ~ na početku (~T() za klasu T). Dodaćemo našem primeru sledeći kod: (vozilo.h) ~vozilo(); (vozilo.cpp) vozilo::~vozilo() { 14

C++ KLASE

// telo desktruktora } Ukoliko se ne definiše destruktor, podrazumeva se destruktor sa praznim telom. Kada i zašto koristiti destruktor? Ukoliko smo u okviru nekog objekta kreirali dinamičke strukture (lančane liste, stabla, ...) ili objekte, moramo radi oslobađanja memorije, pre uništavanja tog objekta da uništimo i te strukture i objekte. U praksi se dešava da dođe do gomilanja neuništenih dinamičkih struktura i objekata ako se objekat u kome se kreiraju više puta kreira i uništava a da se pri tome ne uništavaju uredno kreirane strukture. To dovodi do toga da sistem ostane bez memorije i aplikacija “pada” (memory leak). Takve greške se veoma teško otkrivaju jer aplikacija neko vreme radi stabilno, a onda u nekom proizvoljnom trenutku “padne”. To se takođe, dešava nezavisno od procesorske i memorijske snage računara. Što računar ima veću memoriju, to je ovu grešku teže otkriti (jer će sistem ređe “padati”)! Kada se destruktor automatski poziva? Vratimo se našem primeru u kome je istanciran objekat bmw: void main() { vozilo bmw(250); // proizvoljan kod } Vreme života objekta bmw će biti sve dok se funkcija main izvršava. Onog trenutka kad se main završi, destruktor se automatski poziva i uništava objekat. To znači da ukoliko neka funkcija statički inicijalizuje objekat, taj objekat će postojati sve do završetka izvršavanja funkcije. Operator delete služi za dinamičko dealociranje (oslobađanje) memorije (nalik na free u programskom jeziku C). Pomoću njega se mogu eksplicitno uništiti dinamički kreirani objekti, kao i sve ostale dinamičke strukture (element lančane liste možemo uništiti sa delete()). Na primer, dinamički kreirani objekat jugo se može uništiti sa: void main() { vozilo *yugo; yugo = new vozilo(); delete yugo; } Poglavlje 2 - 03 – Uništavanje objekata

15

PROGRAMSKI JEZIK C++

2.5 Pristup funkcijama i atributima klase Da bi se pristupilo funkciji nekog objekta, potrebno je navesti ime objekta, propraćeno tačkom (.) i zatim ime funkcije sa odgovarajućom sintaksom (stvarni parametri u okviru zagrada funkcije, ukoliko postoje). Navedena funkcija mora da bude deklarisana u okviru klase korišćenog objekta. . -> Ukoliko navodimo pokazivač na objekat, onda koristimo strelicu (->) umesto tačke. Recimo da hoćemo da uključimo motor prvog vozila. To ćemo uraditi na sledeći način: bmw.ukljuci(); Važno je napomenuti da je motor drugog vozila i dalje ostao isključen, tj. operacija ukljuci() je promenila samo atribut stanje objekta nad kojim je primenjena. Uzimajući primer sa vozilo, uključićemo motore oba vozila, a zatim isključiti motor prvog vozila. (main.cpp) #include ”vozilo.h”; void main() { vozilo bmw; vozilo *yugo; yugo = new vozilo(); bmw.ukljuci(); yugo->ukljuci(); } Ista sintaksa važi i za pristup atribitima klase ako su deklarisani kao javni (public), Na primer: bmw.stanje = 1; Međutim, poželjno je izbegavati direktno pristupanje atributima klase pomoću funkcija drugih klasa. Time se narušava koncept inkapsulacije. Zbog toga se obično atributi klase deklarišu kao privatni, a za pristup atributima koristite se public funkcije klase za postavljanje i preuzimanje vrednosti atributa (u engleskoj literaturi poznate kao set i get funkcije). 16

C++ KLASE

Funkcije za pristup atributima se obično prave kao inline funkcije. Telo inline funkcija se piše direktno u definiciji klase, ili se u definiciji klase ispred deklaracije funkcije navede ključna reč inline, a implementacija se navodi iza definicije klase (u .h fajlu). Inline funkcije štede vreme izvršenja programa na taj način što prevodilac njihov kod umeće u kod u svakoj tački njihovog poziva. Zbog toga se kao inline definišu samo funkcije koje se veoma često pozivaju, a čije je telo veoma klatko. Neki kompilatori čak nameću ograničenje da funkcije koje sadrže cikluse ne mogu biti definisane kao inline funkcije. Klasi vozilo dodaćemo funkcije za postavljanje i preuzimanje vrednosti brzine. (vozilo.h) class vozilo { private: int stanje; int max_brzina; int brzina; public: vozilo(); vozilo(int max); void ukljuci(); void iskljuci(); void ubrzaj(); void uspori(); const int uzmiBrzinu() const { return brzina; } inline void postaviBrzinu( int brzina ); }; void vozilo::postaviBrzinu( int brzina) { this->brzina = brzina; } U ovom primeru može se uočiti još jedna novina. Ispred povratnog tipa funkcije navedena je ključna reč const, što znači da je vrednost koju funkcija vraća konstantna (ne sme se menjati dalje u programu). Iza zaglavlja funkcije navedena je takođe klučna reč const. To označava da je funkcija konstantna, tj. da funkcija ne menja objekat nad kojim je pozvana. Kad god funkcija ne menja vrednosti atributa treba je definisati kao konstantnu jer se nad konstantnim objektima klase mogu pozivati samo konstantne funkcije. To znači da u programu možemo definisati konstanti objekat: const voziloNaIzlozbi(200); Nad ovako definisanim objektom dozvoljeno je pozvati samo funkciju uzmiBrzinu jer je trenutno u klasi samo ona definisana kao konstantna. 17

PROGRAMSKI JEZIK C++

2.6 Prijateljske funkcije klase Klasi vozilo ćemo pridružiti funkciju koja će upoređivati maksimalne brzine dva vozila. Na početku deklaracije funkcije ćemo dodati modifikator friend koji označava da ovoj operaciji mogu pristupati prijateljske funkcije. Prijateljska funkcija klase je funkcija koja nije član klase, ali ima pristip privatnim članovima te klase. To mogu da budu obične funkcije ili funkcije neke druge (prijateljske) klase. (vozilo.h) class vozilo { private: int stanje; int max_brzina; int brzina; public: vozilo(); vozilo(int max); void ukljuci(); void iskljuci(); void ubrzaj(); void uspori(); friend bool uporediBrzine(vozilo& v1, vozilo& v2){ if (v1.max_brzina > v2.max_brzina) return 1; else return 0; } }; Definicija prijateljske funkcije može da se izvši na dva načina. U oba slučaja će se smatrati globalnom funkcijom! Prvi je inline, tj. u definiciji klase. Argumenti funkcije uporediBrzine su reference na objekte klase vozilo. Recimo da hoćemo da u funkciji main uporedimo maksimale brzine dva automobila koristeći funkciju uporediBrzine. U tom slučaju funkcija main predstavlja prijateljsku funkciju. (main.cpp) #include #include "vozilo.h" void main() 18

C++ KLASE

{ vozilo bmw(230), ferrari(340); if (uporediBrzine(bmw, ferrari)) brzi"); else printf ("Ferrari je brzi"); }

printf("Bmw

je

Funkcija uporediBrzine vraća true ukoliko je maksimalna brzina prvog objekta veća od maksimalne brzine drugog (što u gornjem primeru nije slučaj). Drugi način je da se prijateljska funkcija deklariše u zaglavlju (vozilo.h) ali bez bloka naredbi (tela funkcije): friend void ispisiKarakteristike(vozilo& v); a zatim da se u nekom implementacionom fajlu (recimo vozilo.cpp) definiše kao globalna funkcija: void ispisiKarakteristike(vozilo& v) { printf ("max. brzina: %d \n", v.max_brzina); printf ("trenutna brzina: %d \n", v.brzina); printf ("stanje motora: %d \n", v.stanje); } Zašto koristiti prijateljske funkcije klase? Njihova najčešća upotreba je pri preklapanju operatora (operator overloading) o čemu će biti više rečeno kasnije. Može se reći da se njihovom upotrebom povećava efikasnost jer direktno pristupaju privatnim elementima, izbegavajući indirektni pristup preko funkcija klase. Još jedna od pogodnosti je i to što je upotreba tih funkcija prirodnija onima koji su navikli na proceduralno programiranje. Umesto bmw.uporediBrzine(ferrari) možemo pisati uporediBrzine(bmw, ferrari). Taj efekat ne možemo postići definisanjem proizvoljne globalne funkcije jer ona ne bi mogla da pristupi privatnim članovima klase. Zašto NE koristiti prijateljske funkcije klase? Što se tiče negativnih strana korišćenja prijateljskih funkcija, one predstavljaju bočne veze u hierarhiji klasa. Vodi se polemika da li one krše koncept inkapsulacije, ili samo proširuju javni interfejs. Programski kod koji ih koristi će generalno biti teži za razumevanje i podložniji greškama. Zbog toga, treba biti oprezan pri korišćenju prijateljskih funkcija. Poglavlje 2 - 04 – Prijateljske funkcije

19

PROGRAMSKI JEZIK C++

2.7 Operatorske funkcije Definišimo klasu za kompleksan broj. Ona bi trebalo da ima dva atributa: za realan i imaginaran deo, i jedan konstruktor koji će imati dva argumenta (za realni i imaginarni deo): (Complex.h) class Complex { public: double real; double imag; public: Complex(double r, double i); }; (Complex.cpp) #include "Complex.h" Complex::Complex(double r, double i) { real = r; imag = i; } Dodajemo i globalnu funkciju main u kojoj ćemo inicijalizovati objekat klase Complex: (main.h) #include #include "Complex.h" void main() { Complex c(10,5); printf ("realni deo = %f, imaginarni deo = c.real, c.imag); }

%f\n",

Naredbom Complex c(10,5) inicijalizuje se objekat klase Complex sa realnim delom 10 i imaginarnim delom 5. Međutim, šta ako hoćemo da saberemo dva kompleksna broja? Kako za sada nemamo nikakav mehanizam za sabiranje kompleksnih brojeva, morali bi da

20

C++ KLASE

saberemo prvo njihove realne, a zatim i njihove imaginarne delove. To bi se ponavljalo kod svakog sabiranja. Očigledno da je ovo vrlo neefikasno. Mnogo efikasnije rešenje bi bilo kada bi za sabiranje kompleksnih brojeva mogli da napišemo naredbu kao i za sabiranje celih ili realnih brojeva: c = a + b; gde su a, b i c kompleksni brojevi. Tako nešto može da se postigne korišćenjem operatorskih funkcija. Dodaćemo jednu operatorsku funkciju, i to za oduzimanje (-). Deklarisaćemo je u zaglavlju: (Complex.h) Complex operator- (Complex& a); Ovu funkciju definišemo u implementacionom fajlu (Complex.cpp): (Complex.cpp) Complex Complex::operator- (Complex& a) { // b = t - a Complex b; b.real = this->real - a.real; b.imag = this->imag - a.imag; return b; } Poziv ove operatorske funkcije, po pravilu koje važi za bilo koju funkciju klase, bi bio: b = c.operator-(a); Kako se radi o operatorskoj funkciji, može se pisati i kao: b = c – a; U prethodnoj naredbi iskorišćen je i operator = kojim se objektu b dodeljuje objekat koji predstavlja rezultat operacije c-a. Na taj način se svi atributi objekta koji se nalazi sa desne strane operatora dodele dodeljuju odgovarajućim atributima objekta koji se nalazi sa njegove leve strane. Ovo je, opet, prihvatljivo samo ukoliko objekti ne sardže dinamičke atribute. U suprotnom, i operator = mora biti implementiran. Problemi koji nastaju pri realizaciji operatora = bi'e pokazani na primeru klase Vector. (Vector.h) class Vector 21

PROGRAMSKI JEZIK C++

{ private: int size; int* elements; public: Vector ( int max ) { size = max; elements = new int[max]; } ~Vextor() { delete [] elements; } Vector& operator=( const Vector& v); }; Prvi problem koji se javlja pri implementaciji operatora = je da vektori sa leve i sa desne strane operatora ne moraju biti iste dužine. Zbog toga u vektoru kojem se dodeljuje vrednost treba obrisati prethodni niz elemenata i kreirati novi odgovarajuće dužine. Drugi problem koji se odmah uočava je da će ovo brisanje dovesti do problema ukoliko se sa leve strane operatora nađe isti taj vektor. Zbog toga je najkorektnije operator = u ovom slučaju realizovati na sledeći način: (Vector.cpp) Vector& Vector::operator=( const Vector& v ) { if ( this != &v ) { size = v.size; delete [] elements; elements = new int[size]; for( int i=0; i(). Izbor povratnih tipova operatorskih funkcija: • Operatori koji menjaju levi operand (npr. =) treba da vrate referencu na levi operand. • Operatori koji biraju ili isporučuju operande (npr. [], ->, ()) obično treba da vrate referencu na izabranu vrednost. • Operatori koji izračunavaju novu vrednost, a ne menjaju svoje operande (operatori nad bitovima, +, -, *, /, %, &, \, ^, &&, ||, unarni (-, +), !~) obično treba da vrate kopiju lokalno kreiranog objekta, tj. Da lokalne objekte vrate po vrednosti. • Preinkrement i predekrement - obično treba da vrate referencu na operand. • Postinkrement i postdekrement - obično treba da sačuvaju kopiju svog operanda, izmene operand i zatim vrate kopiju. 23

PROGRAMSKI JEZIK C++

U sledećem primeru je isti operator realizovan na dva načina, kao prefiksni i postfiksni. class demo{ privat: int val; public: demo& operator++() // prefiks { val++; return *this; } demo operator++(int) // postfiks //da bi se realizovao kao postfiksni, uvodi se // formalni argument koji se u telu // funkcije i ne koristi { demo tmp; tmp.val=val; val++; return tmp; //vraća objekat po vrednosti } };

2.8 Zajednički članovi klasa Recimo da hoćemo da napravimo lančanu listu u kojoj svaki element sadrži podatak o ukupnom broju elemenata te liste u tom trenutku. Ukoliko bi obrisali jedan element liste, morali bi, prolaskom kroz celu listu, da ažuriramo podatak o broju elemenata u svakom elementu liste. To je prilično neefikasno i loše rešenje. Idealno bi bilo ukoliko bi svi elementi liste čitali vrednost tog podatka sa jednog mesta.U tom slučaju ne bi bilo problema sa ažururanjem liste. Zajednički atributi klase upravo to i rade. Ukoliko je broj elemenata liste zajednički podatak, postojaće samo jedna kopija tog podatka za sve elemente te liste. Isti prtincip važi i za zajedničke funkcije klase. Stavljanjem modifikatora static na početak definicije ili deklaracije nekog člana date klase, taj član postaje zajednički za sve objekte te klase. Zajednički atibuti su slični globalnim podacima, ali se njihove vrednosti mogu menjati samo na način koji odgovara opisu klase. Definisaćemo klasu Abc koja će imati jedan zajednički i jedan običan (pojedinačan) atribut, i jednu zajedničku i dve pojedinačne funkcije. (Abc.h) class Abc 24

C++ KLASE

{ static int a; atributa int b; public: static int getA(); int getB(); void incA(); 1 };

//

deklarisanje

zajednickog

// pojedinacni atribut // uzima vrednost a // uzima vrednost b // increase a, povecaj a za

Zajednički atribut je samo deklarisan, ali nije i deifinisan. Da bi smo ga definisali, u implementacionom fajlu (Abc.cpp) ćemo dodati: int Abc::a = 10; Definicija atributa mora obavezno da se izvrši pre kreiranja bilo kog objekta te klase, ili pre pozivanja funkcije main(). Definisanje funkcija bi izgledalo ovako: (Abc.cpp) int Abc::getA() { return Abc::a; } int Abc::getB() { return b; } void Abc::incA() { Abc::a ++; } Funkcija getA() je zajednička, i kao takva može pristupati samo zajedničkim atributima, a ne i pojedinačnim. Sa druge strane, pojedinačna funkcija može pristupati i pojedinačnim i zajedničkim atributima. Pri pristupanju zajedničkim članovima, izričito se preporučuje korišćenje izraza oblika Klasa::član (iako to nije jedini način). Poglavlje 2 - 06 – Zajednički članovi klasa

25

PROGRAMSKI JEZIK C++

3.

NASLEĐIVANJE KLASA

3.1 Generalizacija Pri razmatranju skupa objekata, pogodno je grupisati ih po sličnim atributima i zajedničkim funkcijama. Na primer, hoćemo da napravimo klasu koja će predstavljati sve objekte koji služe za transport. Ona bi uključivala objekte aviona, objekte automobila ili čak objekte brodova. Drugim rečima, ona bi objedinjavala klase kopneniTransport, vazdusniTransport i morskiTransport. Nazovimo tu klasu Transport. Ovu klasu smo dobili procesom generalizacije. Generalizacija je proces kojim se identifikuju i definišu zajednički atributi i funkcije u skupu objekata. Identifikovanjem zajedničkih delova sistema, generalizacija smanjuje redundansu (višak) u razvoju i omogućava ponovno korišćenje pojedinih delova.

3.2 Nasleđivanje Klasa Transport je na vrhu hierarhije. Ona se zove superklasa (roditeljska klasa), dok se klase vazdusniTransport, morskiTransport i kopneniTransport nazivaju podklase. Hierarhija je prikazana na donjoj slici.

Slika 3.1 Hierarhija klasa 26

NASLEĐIVANJE KLASA

Atribute klase Transport (npr. maksimalan broj putnika) kao i funkcije te klase (transportuj, stani, kreni) su nasledile podklase. Može se takođe reći da su podklase izvedene iz roditeljske klase. Nasleđivanje je mehanizam definisanja novih klasa od već postojećih klasa. Tako napravljene klase dele, tj. nasleđuju članove roditeljske klase. Zbog čega je nasleđivanje korisno? Ukoliko izvršimo bilo kakvu izmenu među članovima roditeljske klase (promena ili dodavanje nekog atributa ili funkcije), ta izmena će se automatski (tj. bez ikakve naše intervencije) odraziti i na sve podklase (potomke) koje su izvedene iz roditeljske klase. Nasleđivanje se često predstavlja pomoću stabla. Kretanjem niz stablo, klase postaju sve više specijalizovane. Kretanjem uz stablo, klase postaju mnogo generalnije.

Specijalizacija

Generalizacija

Slika 3.2 Specijalizacija i generalizacija Objekti veoma retko pripadaju totalno različitim grupama – često postoji značajno preklapanje funkcija među grupama objekata. Radi ilustracije, deklarisaćemo klasu Transport. Posedovaće par atributa – trenutnu brzinu i stanje rezervoara vozila. Ovaj atribut spada u interne informacije klase (vidi 1.4). Ova klasa će imati i dve privatne funkcije, koje predstavljaju implementacioni deo klase. To bi bile funkcije za povećanje dotoka goriva u motor (čime se povećava brzina) (povecajDotokGoriva()) i za kočenje ukljuciSistemKocenja()). Ove dve funkcije su zajedničke za sva (motorna) prevozna sredstva. Interfejs klase bi se sastojao od dve funkcije: za ubrzavanje vozila (ubrzaj()) i za usporavanje vozila (uspori()).

27

PROGRAMSKI JEZIK C++

Vozača ne treba da brinu implementacioni detalji, tj. kako i zbog čega se povećava brzina vozila. (transport.h) class Transport { int brzina; int stanje_rezervoara; protected: void povecajDotokGoriva(); void ukljuciSistemKocenja(); public: void ubrzaj(); void uspori(); }; (transport.cpp) void Transport::povecajDotokGoriva(){ } void Transport::ukljuciSistemKocenja(){ } void Transport::ubrzaj(){ povecajDotokGoriva(); } void Transport::uspori(){ ukljuciSistemKocenja(); } Prvo ćemo iz roditeljske klase Transport izvesti podklase vazdusniTransport, morskiTransport i kopneniTransport. Svaka od ovih klasa će sadržati i neke dodatne atribute i funkcije – svojstvene samo njima. Radi ilustracije će biti prikazane samo .h datoteke. Nasleđivanje se vrši tako što se pri deklaraciji klase, posle navođenja imena klase stavi znak “:“ (dve tačke), zatim tip nasleđivanja (private, protected ili public, vidi tabelu 1) i ime klase iz koje se vrši nasleđivanje: class vazdusniTransport : public Transport { public: 28

NASLEĐIVANJE KLASA

povecajVisinu(); smanjiVisinu(); } Ova klasa će sadržati sve atribute i funkcije svoje roditeljske klase, kao i dodatne dve funkcije koje su dodatno deklarisane. Na primer: void main() { vazdusniTransport avion; avion.povecajVisinu(); avion.ubrzaj();

// nasledjena funkcija

} Poglavlje 3 - 01 Nasleđivanje klasa

Postoji nekoliko vrsta nasleđivanja: private, protected i public. Zavisno od vrste nasleđivanja će se menjati i pristupačnost članova izvedene klase. Na primer, ukoliko je pristupnost nekog člana roditeljske klase public, i ukoliko se podklasa privatno izvede (private), onda će pristupnost tog istog člana u izvedenoj klasi biti private. Tabela 3.1 Vrste nasleđivanja i pristupačnost izvedenih klasa Pravo pristupa članovima u osnovnoj klasi

Pravo pristupa istim članovima u privatno izvedenoj klasi

Pravo pristupa istim članovima u zaštićeno izvedenoj klasi

Pravo pristupa istim članovima u javno izvedenoj klasi

(private)

(protected)

(public)

nepristupačan

nepristupačan

nepristupačan

nepristupačan

private

nepristupačan

nepristupačan

nepristupačan

protected

Private

protected

protected

public

Private

protected

public

Odnos sastoji se od znači da izvedena klasa sadrži kopiju osnovne i svu njenu funkcionalnost, ali objekti izvedene klase ne mogu da se koriste u slučajevima kada se zahtevaju objekti osnovne klase.

29

PROGRAMSKI JEZIK C++

Kod privatno izvedene klase javni članovi osnovne klase postaju privatni u izvedenoj klasi.

3.3 Polimorfizam Normalno, svaki tip vozila funkcioniše na drugačiji način – brod, avion i automobil usporavaju na sasvim drugačiji način. To znači da im se menjaju i implementacioni detalji, tj. svaki od njih bi trebalo da ima različite funkcije za povećanje dotoka goriva i sistema kočenja. Sa druge strane, interfejs bi ostao isti – usporavanje i ubrzavanje vozila. Polimorfizam omogućava da klasa saopšti da ima isti interfejs kao i osnovna klasa, ali da je ponašanje izvedene klase različito od ponašanja osnovne klase. Na primer, avion usporava na sasvim drugi način od automobila. Samim tim bi trebalo da je drugačija i njegova funkcija ukljuciSistemKocenja. Ustvari, svaka od izvedenih klasa (vazdusniTransport, morskiTransport i kopneniTransport) trebalo bi da ima drugačije definisanu funkciju ukljuciSistemKocenja. Polimorfizam omogućava upravo to – da izvedene klase imaju svoje “verzije” funkcija deklarisanih u osnovnoj klasi. Polimorfizam se postiže deklarisanjem željenih funkcija osnovne klase virtualnim. Ako izvedena klasa ne definiše virtualnu funkciju osnovne klase, ona jednostavno usvaja ponašanje osnovne klase.

3.3.1 Virtualne funkcije Virtualna funkcija je funkcija članica za koju se očekuje da bude redefinisana u nasleđenim klasama. Da bismo omogućili polimorfizam funkcije ukljuciSistemKocenja u gornjem primeru, potrebno je da u deklaraciji te funkcije u osnovnoj klasi dodamo prefix virtual kako bi naznačili da je ta funkcija virtualna: virtual void ukljuciSistemKocenja(); (transport.h) class Transport { int brzina; int stanje_rezervoara; public: void povecajDotokGoriva(); virtual char* ukljuciSistemKocenja() {return “Ukljucen”;} void ubrzaj() {povecajDotokGoriva();} char* uspori() {return ukljuciSistemKocenja();} }; 30

NASLEĐIVANJE KLASA

(vazdusniTransport.h) class vazdusniTransport : public Transport { public: virtual char* ukljuciSistemKocenja(){ return “Vazdusne kocnice podignute”; } } (kopneniTransport.h) class kopneniTransport : public Transport { public: virtual char* ukljuciSistemKocenja(){ return “Kocioni diskovi rade”; } }; (morskiTransport.h) class kopneniTransport : public Transport { public: virtual char* ukljuciSistemKocenja(){ return “Propeler koci”; } }; U glavnom programu biće kreirani objekti definisanih klasa. U ovom primeru to ćemo izvršiti dinamičkim putem, pomoću pokazivača (kreiranje objekata se može vršiti i statički (videti 2.2)). (main.cpp) void main() { Transport *vozilo; vozilo = new vazdusniTransport; vozilo->koci(); // ispisuje: “Vazdusne kocnice podignute” delete vozilo; vozilo = new kopneniTransport; vozilo->koci(); // ispisuje: “Kocioni diskovi rade”}; 31

PROGRAMSKI JEZIK C++

delete vozilo; vozilo = new morskiTransport; vozilo->koci(); // ispisuje: “Propeler koci” delete vozilo; } Nepisano je pravilo da se destruktori klasa prave kao virtuelne funkcije. U prethodnom primeru definisan je pokazivač tipa Transport. U trenutku brisanja objekta (delete vozilo), kada destruktor nije definisan kao virtuelna funkcija, poziva se destruktor klase Transport. U tom slučaju, da su u izvedenim klasama postojali podaci smešteni u dinamičkoj zoni memorije, oni bi ostali neobrisani, tj. taj deo memorijskog prostora bi bio blokiran do kraja izvršenja programa. Kada se destruktor osnovne klase definiše kao virtuelan, onda se u trenutku brisanja objekta, proverava stvarni tip objekta i poziva se destruktor odgovarajuće izvedene klase. Destruktor izvedene klase na kraju implicitno poziva destruktor osnovne klase. Tako će u tom slučaju biti obrisani svi dinamički atributi i izvedene i osnovne klase. Iz prethodnog razmatranja zaključujemo da desturktori osnovih (roditeljskih) klasa treba da budu virtuelni. Medjutim, stablo nasledjivanja nikada nije konačno. Verovatno će u nekoj drugoj primeni i izvedene klase biti dalje nasledjene. Čak i ako u postojećim klasama nema dinamičkih podataka (što je bilo i u našem primeru), to ne znači da ih neće biti ni u izvedenim klasama. Zato destruktor u klasi treba uvek definisati kao virtuelnu funnkciju. Nad objektom izvedene klase se ne može pozvati funkcija roditeljske klase koja je u izvedenoj klase predefinisana. Međutim, u funkcijama izvedene klase se može koristiti funkcija osnovne klase koja je u izvedenoj predefinisana. U tom slučaju funkcija roditeljske klase se poziva na sledeći način: ::( ) Ovaj mehanizam poziva funkcije roditeljske klase biće pokazan na primeru kreiranja klase Krug i iz nje izvedene klase Valjak. Funkcija povrsina() definisana u klasi Krug biće korišćena u funkcijama za zračuvnavanje površine i zapremine u klasi Valjak. (Krug.h) class Krug { float r; public: Krug( float r ); 32

NASLEĐIVANJE KLASA

float obim(); float povrsina(); }; (Krug.cpp) Krug::Krug( float r ) { this->r = r; } float Krug::obim() { return 2*3.14*r; } float Krug::povrsina() { return 3.14*r*r; } (Valjak.h) class Valjak :: public Krug { float H; public: Valjak( float r, float H ); float zapremina(); float povrsina(); }; (Valjak.cpp) Valjak::Valjak( float r, float H ) : Krug( r ) { this->H = H; } float Krug::zapremina() { return ( Krug::povrsina()*H); } float Krug::povrsina() { return ( 2*Krug::povrsina() + obim()*H ); } 33

PROGRAMSKI JEZIK C++

Polimorfizam igra značajnu ulogu u projektovanju objektno orjentisanih aplikacija, pogotovu u primeni projektnih obrazaca (design patterns). Poglavlje 3 - 02 – Polimorfizam

3.3.2 Čiste virtualne funkcije, apstraktne klase Izvedena klasa prečišćava i proširuje ponašanje osnovne klase. U mnogim realnim aplikacijama, izvedene klase pored svojeg ponašanja, koriste osobine i ponašanje osnovnih (roditeljskih) klasa. Ali postoje situacije kada je osnovna klasa jednostavno skelet ili okvir, dok se funkcionalnost dobija tek kod izvedenih klasa. U primeru transporta, funkcija povecajDotokGoriva() klase Transport nema telo, zato što se implementacija razlikuje od vozila do vozila. Ona samo ukazuje na zajednički oblik i naziv funkcije, dok telo funkcije varira zavisno od implementacije. Svako pisanje tela funkcije povecajDotokGoriva() u klasi Transport bi bilo beskorisno, s obzirom da bi ionako bilo zamenjeno u izvedenoj klasi. U takvim slučajevima treba koristiti čiste virtualne funkcije. Čista virtualna funkcija je virtualna funkcija kojoj nije potrebno telo. Ona u deklaraciji iza imena sadrži notaciju “=0”. U tom slučaju bi deklaracija funkcije povecajDotokGoriva() izgledala ovako: void povecajDotokGoriva()=0; Čim klasa ima makar jednu čistu virtualnu funkciju, ona se naziva apstraktnom. Apstaktna klasa je klasa koja sadrži čistu virtualnu funkciju ili klasa koja nasleđuje virtualnu funkciju ali je ne definiše. Ne može da se kreira primerak apstaktne klase, mada može da postoji pokazivač ili referenca na apstaktnu klasu. Pokazivač ne može stvarno da pokaže na neki objekat osnovne (apstraktne) klase, uvek će pokazivati na neki objekat jedne od klasa izvedenih iz osnovne. Nije dozvoljeno raditi ništa što će kreirati pravi objekat osnovne klase. To ilustruje sledeći primer: Osnovna *pok; //pokazivac na objekat klase Glavna pok = new Osnovna; //nije ispravno pok = new Izvedena; //ispravno void uradiNesto(Osnovna & ref); //funkcija koja uzima //referencu na objekat klase Glavna U primeru transporta, klasa Transport nije funkcionalno upotrebljiva – još uvek se ne zna o kom tipu vozila je reč, nema sve potrebne funkcije, itd. Međutim, ona 34

NASLEĐIVANJE KLASA

pruža zajedničku osnovu za ostale izvedene klase (vazdusniTransport, morskiTransport i kopneniTransport). Možda su i te izvedene klase previše neodređene da bi se koristile – možda tek kad bi recimo iz klase kopneniTransport izveli klasu Automobil, onda bi zadnje navedena klasa bila upotrebljiva, i onda bi mogli da pravimo objekte te klase. U tom slučaju, klase Transport, vazdusniTransport, morskiTransport i kopneniTransport nisu direktno primenljive, ne mogu se kreirati objekti ovih klasa. Takođe, one mogu da sadrže i čiste virtualne funkcije, što ih čini idealnim kandidatima na apstraktne klase.

3.4 Višestruko nasleđivanje Jednostruko nasleđivanje podrazumeva da izvedena klasa nasleđuje osobine jedne osnovne klase. Višestruko nasleđivanje znači nasleđivanje osobina od više osnovnih klasa, Slika 3.3. Time se omogućava da se u izvedenoj klasi kombinuju i proširuju karakteristika nekoliko osnovnih klasa. class Izvedena : public Osnovna1, public Osnovna2 { ... } Osnovna1

Osnovna2

Izvedena

Slika 3.3 Višestruko nasleđivanje

3.5 Virtualne osnovne klase Klase B1 i B2 su izvedene iz A, a klasa C je izvedena iz B1 i B2. Klasa A se pojavljuje dva puta, po jednom preko svake od osnovnih klasa klase C. Ako je klasa A integralni deo B1 i B2, tada su verovatno potrebne dve kopije A u klasi C, Slika 3.4 class class class class

A { }; B1: public A { }; B2: public A { }; C: public B1, public B2 { }; 35

PROGRAMSKI JEZIK C++

Slika 3.4 Višestruko nasleđivanje klasa izvedenih iz iste klase U nekim slučajevima je potrebno da imate samo jednu kopiju A, Slika 3.5. Zato je potrebno da se A učini virtualnom osnovnom klasom. Kada prevodilac gradi klasu, on uzima sve virtualne primerke klase koji spaja u jedinstveni primerak. class class class class

A { }; B1: public virtual A { }; B2: public virtual A { }; C: public B1, public B2 { };

Moguće je i da bude uključena i virtualna i nevirtualna verzija klase. Kada se to dogodi svi virtualni primerci se kombinuju u jedan primerak, dok se nevirtualni primerci tretiraju normalno i unose odvojeno.

Slika 3.5 Višestruko nasleđivanje sa jednom kopijom klase A Kada je klasa obična osnovna a ne virtualna osnovna, konstruktori izvedene klase kontrolišu koji konstruktori osnovne klase se pozivaju. Ali kada je osnovna klasa virtualna stvari postaju nejasne zato što ima mnogo izvedenih klasa od kojih svaka polaže jednaka prava na osnovnu klasu. Pošto virtualna osnovna klasa treba da se inicijalizuje jednom, potrebno je pravilo za određivanje izvedene klase koja zaista kontroliše aktiviranje konstruktora virtualne osnovne. U jeziku C++ je usvojeno 36

NASLEĐIVANJE KLASA

rešenje da kontrolu ima klasa stvarnog objekta, koja se ponekad zove i najviše izvedena klasa. Na primer, ako Z na neki način ima virtualnu osnovnu klasu koja se zove A, tada je na Z konstruktorima da kontrolišu koji će A konstruktori biti pozvani. Ako A ima standardni konstruktor i Z konstruktor se ne odnosi eksplicitno na A u listi inicijalizacije, tada će biti upotrebljen standardni konstruktor klase A. Međutim, ako A ima samo konstruktore koji uzimaju argumente, tada Z mora eksplicitno da izabere jedan od konstruktora klase A. Iako je pravilo da kontrolu ima najviše izvedena klasa, podrazumeva se da konstruktori svake izvedene klase moraju da odrede koji od A konstruktora treba pozvati, jer ne možete da znate koja će od izvedenih klasa biti kreirana tokom izvršavanja. Tako, ako A ima samo konstruktore koji uzimaju argumente, svaka klasa koja ima A kao virtualnu osnovnu klasu, bez obzira na udaljenost u naslednom stablu, mora eksplicitno da obezbedi konstruktore koji u inicijalizacionoj listi pozivaju A konstruktore. Ako u hijerahiji klasa postoji A kao virtualna osnovna, tada će se koristiti samo inicijalizacija A koju odredi najviša izvedena klasa, a sve druge se preskaču. Npr. ako se kreira objekat klase B, koristiće se njegova inicijalizacija virtualne osnovne klase A. Ali ako se klasa C izvede iz B i kreira se objekat klase C, biće preskočena inicijalizacija virtualne osnovne A od strane B i koristiće se inicijalizacija iz klase C.

37

PROGRAMSKI JEZIK C++

4.

С++ ŠABLONI

Recimo da je potrebno da napišemo funkciju koja određuje koji je od data dva broja veći. Ukoliko se radi o celim brojevima, funkcija bi izgledala: int max (int i, int j) { return i>j ? i : j; } Međutim, šta ako ista funkcija treba za realne brojeve? Onda bi funkcija izgledala ovako: float max (float i, float j) { return i>j ? i : j; } Razlike između ove dve funkcije se svode na sistematsko zamenjivanje oznake tipa unutar cele definicije fukcije. To je rutinski posao, i on se može automatizovati korišćenjem generičkih funkcija. Primer generičke funkcije za gornji primer bi bio: template T max (T i, T j) { return i>j ? i : j; } Poglavlje 4 - 01 – Generičke funkcije

Prvi red u gornjem kodu: template definiše klasu T kao formalni argument koji će biti zamenjen stvarnim argumentom u trenutku korišćenja šablona. Na primer, ukoliko se pozove max(5,8) T će biti tipa int. Funkcije ili klase opisane pomoću šablona nazivaju se generičke funkcije ili generičke klase. Na osnovu njih se kasinje generišu konkretne funkcije ili klase. Opšti oblik za šablone generičkih funkcija ili klasa je: template < argument, argument, ... > opis Argumenti mogu da označavaju tipove ili konstante. Opšti oblik argumenata je: 38

ULAZ/IZLAZ

class identifikator_tipa (npr. class T) ili oznaka_tipa identifikator_konstante (npr. int k) Opis može da bude deklaracija (prototip) ili definicija generičke funkcije (odnosno klase) čiji se šablon opisuje. Šabloni mogu značajno da povećaju fleksibilnost i da smanje veličinu koda.

4.1 Generisanje funkcija Funkcije na osnovu zadatog šablona se generišu: •

automatski - kad se naiđe na poziv generičke funkcije sa stvarnim argumentima koji mogu da se uklope u šablon bez konverzije tipova argumenata.



na zahtev programera - navođenjem deklaracije (prototipa) za datu generičku funkciju sa željenim argumentima.

Zaobilaženje generisanja funkcija na osnovu šablona postiže se navođenjem definicije odgovarajuće obične funkcije koju prevodilac mora da nađe pre nego što izvrši generisanje na osnovu šablona. Primer: float max (float, float);

// eksplicitni zahtev za // generisanje

float g = max (i, f);

4.2 Generisanje klasa Konkretna klasa (tip) se generiše na osnovu šablona generičke klase u trenutku kad naiđe na prvu definiciju objekta u kojoj se koristi identifikator generičke klase. Tada se generišu i sve funkcije članice generisane klase. Oznaka generisane klase treba za sadrži stvarne argumente za šablon. Koristi se sledeća sintaksa: naziv_klase naziv_objekta; Na primer: Vektor vek; Sledi primer generičke klase, uz korišćenje konstante argumenata šablona: Definicija klase: template 39

PROGRAMSKI JEZIK C++

class Vektor { T element [k]; public: int duzina; public: Vektor(); T& operator[] (int i); }; Definicije funkcija klase: template Vektor::Vektor () { duzina = k; } template T& Vektor::operator[] (int i) { return element[i]; } Ukoliko bi želeli da napravimo vektor realnih brojeva dužine 12, napisali bi: Vektor vek; Pristupanje elementima vektora (atribut element) se vrši pomoću operatora []: vek[3] = 4.2;

Poglavlje 4 - 02 – Generičke klase

Poglavlje 4 - 03 – Primer genericke klase Matrica

40

ULAZ/IZLAZ

5.

ULAZ/IZLAZ

Ulaz i izlaz podataka nije deo jezika C++, već postoji odgovarajuća biblioteka klasa za rad sa ulazom/izlazom. Datoteke u C++-u su samo dugački nizovi bajtova i nazivaju se tokovima. Nema suštinske razlike između toka na disku (datoteke) i toka u operativnoj memoriji. Rad sa tokovima u C++ realizuje se odgovarajućim klasama, Slika 5.1. Konkretni tokovi su primerci (objekti) tih klasa. Većina funkcija nad tokovima je ista, bez obzira gde su oni smešteni. Sa slike se može videti da je klasa ios osnovna klasa za sve klase za ulazni/izlazni tok. Iako ios tehnički nije apstraktna klasa, obično se ne kreiraju objekti ove klase, niti se klase izvode direktno iz ios. Za nasleđivanje se koriste klase iostream ili ostream, ili druge izvedene klase. Klasa istream služi za ulazne tokove, a klasa ostream za izlazne tokove. Klasa iostream nasleđuje obe klase (istream i ostream). Dve najvažnije klase koje se izvode iz klase iostream su: •

fstream, za rad sa tokovima na disku (datoteke)



strstream, za rad sa nizovima (eng. strings, tj. tokovima u operativnoj memoriji)

Generalno, za rad sa datotekama postoje tri klase: •

ifstream, samo za ulazne datoteke



ofstream, samo za izlazne datoteke



fstream, za kombinovani ulaz i izlaz

Nalik na prethodni slučaj, za rad sa nizovima postoje tri klase: •

istrstream, za uzimanje podataka iz tokova



ostrstream, za smeštanje podataka u tokove



strstream, za uzimanje i smeštanja podataka u/iz tokova

Postoje 4 standardna toka (primerka klase ostream i istream) koji se automatski formiraju na početku izvršavanja svakog programa: •

cin – glavni (standardni) ulaz tipa istream (preciznije objekat klase istream_withassign) . Standardno predstavlja tastaturu, ukoliko se drugačije ne specificira (da se izvrši skretanje glavnog ulaza unutar samog programa ili u komandi operativnog sistema za izvršavanje programa).

41

PROGRAMSKI JEZIK C++

Slika 5.1 Hierarhija klasa za ulazni/izlazni tok •

cout – glavni (standardni) izlaz tipa ostream (preciznije objekat klase ostream_withassign). Predstavlja ekran, koristi se za ispisivanje podataka koji čine rezultate izvršavanja programa.



cerr – standardni izlaz za poruke tipa ostream. Predstavlja ekran, obično se koristi za ispisivanje poruka o greškama.



clog – standardni izlaz za zabeleške tipa ostream. Predstavlja ekran, koristi se za “vođenje dnevnika” o događajima za vreme izvršenja programa.

Biblioteka “stdio.h” jezika C i dalje može da se koristi za ulaz/izlaz podataka, ali su ipak C++ klase efikasnije. Nikako se ne preporučuje da se koriste obe vrste ulazno-izlaznih biblioteka.

5.1 Ulazni tok Tri najvažnije klase za ulazne tokove su istream, ifstream, i istrsteam. Klasa istream je najbolja za rad sa sekvencijalnim tekstualnim ulazom. Klasa ifstream podržava ulaz iz datoteke. 42

ULAZ/IZLAZ

5.1.1 Konstruisanje objekata ulaznih tokova Ukoliko koristite cin objekat, ne treba da se napravi ulazni tok. Ulazni tok se mora napraviti ukoliko koristite: •

tok iz datoteke (File stream)



tok iz niza (String stream)

5.1.1 Konstruktori ulaznih tokova datoteka Postoji tri načina da se napravi ulazni tok iz datoteke: 1. korišćenjem konstruktora bez argumenata 2. navođenjem naziva datoteke i odgovarajućih parametara 3. navođenjem deskriptora datoteke. Tabela 5.1 Parametri iosmode i dosmode ios::app

upisivanje na kraj datoteke

ios::ate

pozicioniranje na kraj datoteke posle otvaranja

ios::in

otvara ulaznu datoteku

ios::out

otvara izlaznu datoteku

ios::nocreate

otvara datoteku ukoliko već postoji

ios::noreplace

otvara datoteku ukoliko već ne postoji

ios::trunc

otvara datoteku i briše stari sadržaj

ios::binary

binarna datoteka. Ako se ništa ne kaže, podrazumeva se rad sa tekstualnom datotekom.

Korišćenjem konstruktora bez argumenata kreira se objekat klase ifstream, a zatim se poziva funkcija open koja otvara navedenu datoteku: ifstream f; // na steku f.open ( "test.txt", iosmod); ili ifstream* f = new ifstream; // na heap-u f->open("test.txt", iosmode); Navođenje naziva datoteke i odgovarajućih parametara (mode flags) se vrši na sledeći način: 43

PROGRAMSKI JEZIK C++

ifstream f("test.txt", iosmode); Navođenje deskriptora datoteke se vrši za datoteku koja je već otvorena. Standardni (default) način za postizanje navedenog je: int fd = _open("test.txt", dosmode); ifstream f(fd); Za parametre iosmode i dosmode se koriste vrednosti date u Tabeli 5.1.

5.1.2 Konstruktori ulaznih tokova niza Konstruktor ulaznog toka niza zahteva adresu prethodno alocirane (dodeljene) i inicijalizovane memorije: char s[] = "123.45"; double amt; istrstream myString( s ); myString >> amt; // Amt = 123.45

5.1.3 Funkcije ulaznog toka Operator ekstrakcije (>>) je programiran za sve standardne C++ tipove podataka, i predstavlja najlakši način da se preuzmu bajtovi iz objekta ulaznog toka. char ime[20]; cin >> ime; Operator >> pamti ulazne podatke samo do prvog unešenog blanko znaka. Za unos "Marko Markovic" će se dobiti samo "Marko". Poglavlje 5 – 01 – Operator ekstrakcije.

Funkcija get se ponaša kao i operator >>, osim što pamti i blanko znakove. Postoji više prototipova ove funkcije, navešćemo samo najčešće korišćene. istream &get(char& znak); - uzima sledeći znak iz ulaznog toka i smešta ga u promenljivu znak (npr. cin.get(c)). istream &get(char* niz, int max); - čita max broj znakova (ukoliko postoji toliko znakova) i smešta ih u niz (npr. cin.get(ime, 20) ). istream &get(char* niz, int max, char kraj); - čita sve znakove do prvog pojavljivanja znaka kraj. Pored toga, može da pročita najviše max broj znakova (npr. cin.get(ime, 20, '\n') ). Funkcija getline je veoma slična operaciji get, jedino što uklanja znak prekida (npr. '\n') koji get ne uklanja. Poglavlje 5 – 02 – Operacija get

44

ULAZ/IZLAZ

Funkcija read čita bajtove iz toka u određeni deo memorije. Mora se navesti lokacija gde se upisuju pročitani podaci, kao i broj pročitanih bajtova. Na primer, ukoliko imamo sledeću strukturu: struct Radnik { char ime[20]; double plata; }; i hoćemo da pročitamo iz (već napravljene) datoteke "plata.dat" jedan slog (strukture Radnik), prvo bi otvorili tok iz te datoteke: ifstream is( "plata.dat", ios::binary | ios::nocreate ); Ukoliko je datoteka uspešno otvorena, vrši se čitanje podataka iz toka koji se upisuju u strukturu r, i odmah zatim i štampanje strukture r: if( is ) { Radnik r; is.read( (char *) &r, sizeof(r) ); cout ( istream& is, Datum& dt ); }; gde je operator>> prijateljska funkcija koja je definisana na sledeći način: istream& operator>> ( istream& is, Datum& dt ) { is >> dt.dan >> dt.mesec >> dt.godina; return is; } operator>> mora biti definisan kao prijateljska funkcija jer funkcija definisana u klasi se poziva nad levim operandom. Kako je uobičajeno da na levoj strani ove funkcije bude objekat klase istream, to bi značilo da ta funkcija mora da bude članica klase istream. Kako mi nemamo pravo da menjamo bibliotečke klase, jedino nam ostaje da ovu funkciju definišemo na globalnom nivou (kao samostalnu). Sada se operator >> može koristiti na sledeći način: Datum dt; cin >> dt; Poglavlje 5 – 04 – Preklapanje operatora ekstrakcije

5.2 Izlazni tok Tri najvažnije klase za izlazne tokove su ostream, ofstream i ostrstream. Veoma retko se konstruišu objekti klase ostream, već se koriste predefinisani objekti (cout, cerr, clog). Klasa ofstream podržava rad sa datotekama. Za kreiranje niza u memoriji treba koristiti klasu ostrstream.

5.2.1 Konstruisanje objekata izlaznih tokova Konstruisanje objekata izlaznih tokova se vrši potpuno isto kao i kod ulaznih tokova (isto postoje tri načina), jedino što se koristi klasa ofstream. 46

ULAZ/IZLAZ

5.2.2 Upotreba operatora umetanja Operator umetanja (
View more...

Comments

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF