cpp programiranje

99
Sveuč ilište u Zagrebu Fakultet elektrotehnike i rač unarstva Zavod za telekomunikacije C C + + + + I OSNOVE OBJEKTNO ORIJENTIRANOG DIZAJNA Predavanja Marko Lackovi ć Zagreb, 2002.

Upload: dario-cvrtila

Post on 10-Feb-2016

55 views

Category:

Documents


16 download

DESCRIPTION

osnove oo programiranja

TRANSCRIPT

Page 1: Cpp programiranje

Sveučilište u Zagrebu Fakultet elektrotehnike i računarstva

Zavod za telekomunikacije

CC++++

II OOSSNNOOVVEE OOBBJJEEKKTTNNOO OORRIIJJEENNTTIIRRAANNOOGG

DDIIZZAAJJNNAA Predavanja

Marko Lacković

Zagreb, 2002.

Page 2: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

2

Sadržaj 1 Literatura................................................................................................................................7 2 Plan predavanja......................................................................................................................7 3 Osnovni tipovi podataka........................................................................................................8

3.1 Vrste podataka........................................................................................................................8 3.2 Identifikatori...........................................................................................................................8 3.3 Objekti i varijable...................................................................................................................9 3.4 Definicija (deklaracija) objekata ............................................................................................9 3.5 Ugrađeni tipovi podataka .....................................................................................................10 3.6 Konstante..............................................................................................................................10 3.7 Brojevi..................................................................................................................................10 3.8 Sufiksi...................................................................................................................................11 3.9 Znakovi.................................................................................................................................11 3.10 Pretvorbe između tipova. Cast operator. ............................................................................12 3.11 Pobrojeni tipovi, identifikator typedef .............................................................................12

4 Operatori...............................................................................................................................14 4.1 Aritmetički operatori ............................................................................................................14 4.2 Logički operatori ..................................................................................................................14 4.3 Poredbeni operatori ..............................................................................................................15 4.4 Bitovni operatori i operator razdvajanja...............................................................................15 4.5 Hijerarhija operatora ............................................................................................................16

5 Naredbe za kontrolu toka programa..................................................................................17 5.1 Blokovi naredbi ....................................................................................................................17 5.2 Uvjetno grananje – naredba naredba if ...............................................................................17 5.3 Uvjetni operator ? :.............................................................................................................18 5.4 Naredba switch ...................................................................................................................18 5.5 Petlja for..............................................................................................................................19 5.6 Naredba while .....................................................................................................................19 5.7 Blok do while .....................................................................................................................20 5.8 Naredba continue continue ................................................................................................21

5.8.1 Znakovni niz..................................................................................................................23 5.8.2 Pridruživanje adrese pokazivaču...................................................................................25 5.8.3 Aritmetika pokazivača...................................................................................................25 5.8.4 Veza polja i pokazivača.................................................................................................25 5.8.5 Dvodimenzionalna polja i pokazivači ...........................................................................25 5.8.6 Dinamička alokacija memorije, operator new ...............................................................25 5.8.7 Dinamička alokacija polja.............................................................................................26

5.9 Reference..............................................................................................................................26 6 Funkcije.................................................................................................................................27

6.1 Deklaracija funkcije .............................................................................................................27 6.2 Definicija funkcije................................................................................................................27 6.3 Argumenti funkcije ..............................................................................................................28 6.4 Argumenti funkcije (prijenos po vrijednosti).......................................................................28 6.5 Argumenti funkcije (prijenos kao pokazivač) ......................................................................28 6.6 Argumenti funkcije (prijenos kao referenca) .......................................................................28 6.7 Polja kao argumenti funkcije................................................................................................29

Page 3: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

3

6.8 Podrazumijevani argumenti..................................................................................................29 6.9 Neodređeni argumenti ..........................................................................................................30 6.10 Pokazivači na funkcije .......................................................................................................30 6.11 Stog i rekurzija ...................................................................................................................30 6.12 Pokazivači i reference kao povratne vrijednosti ................................................................31 6.13 Globalni i statički objekti ...................................................................................................31

7 Ulazni i izlazni tokovi...........................................................................................................32 7.1 Što su ulazni i izlazni tokovi ................................................................................................32 7.2 Ispis pomoću cout ...............................................................................................................32 7.3 Kontrola ispisa......................................................................................................................32 7.4 Učitavanje pomoću cin........................................................................................................33 7.5 Učitavanje znakovnih nizova ...............................................................................................33 7.6 Datoteke i ulazno izlazni tokovi...........................................................................................34

7.6.1 Čitanje i pisanje iz/u datoteke .......................................................................................34 7.6.2 Modovi otvaranja datoteke............................................................................................34 7.6.3 Određivanje i postavljanje položaja unutar datoteke ....................................................35 7.6.4 Rad s datotekama (C) ....................................................................................................35 7.6.5 Rad s datotekama (C++)................................................................................................36

7.7 Primjeri zadataka..................................................................................................................36 8 Uvod u OOP..........................................................................................................................38 9 Enkapsulacija .......................................................................................................................39

9.1 Objekti i klase.......................................................................................................................39 9.2 Definicija klasa.....................................................................................................................39 9.3 Dodjela prava pristupa .........................................................................................................40 9.4 Prijateljske funkcije..............................................................................................................40 9.5 Umetnute (inline) funkcije...................................................................................................40 9.6 Deklaracija objekta...............................................................................................................41 9.7 Ključna riječ this ................................................................................................................41 9.8 Konstruktori .........................................................................................................................42

9.8.1 Podrazumjevani konstruktor .........................................................................................42 9.8.2 Konstruktor s parametrima............................................................................................42 9.8.3 Konstruktor kopije.........................................................................................................43 9.8.4 Destruktor......................................................................................................................43

9.9 Polja objekata .......................................................................................................................44 9.10 Statički članovi...................................................................................................................44 9.11 Pokazivači na podatkovne članove klase ...........................................................................44 9.12 Pokazivači na funkcijske članove klase .............................................................................45 9.13 Strukture .............................................................................................................................45 9.14 Unije ...................................................................................................................................45 9.15 Primjeri zadataka................................................................................................................46

10 Polimorfizam.....................................................................................................................49 10.1 Što je polimorfizam............................................................................................................49 10.2 Preopterećenje funkcija ......................................................................................................49 10.3 Korisnički definirane konverzije........................................................................................50

10.3.1 Konverzija konstruktorom...........................................................................................50 10.3.2 Operator konverzije.....................................................................................................50

10.4 Preopterećenje operatora ....................................................................................................51

Page 4: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

4

10.4.1 Definicija operatorske funkcije ...................................................................................51 10.4.2 Operator =....................................................................................................................52 10.4.3 Operator [] [] ..............................................................................................................53 10.4.4 Operator () () ..............................................................................................................53 10.4.5 Operator ->..................................................................................................................53 10.4.6 Prefiks i postfiks operatori ++ i -- ...........................................................................54 10.4.7 Operator umetanja << ..................................................................................................54

10.5 Primjeri zadataka................................................................................................................54 11 Nasljeđivanje.....................................................................................................................58

11.1 Što je nasljeđivanje?...........................................................................................................58 11.2 Zadatak ...............................................................................................................................58

11.2.2 Sveoptička mreža s WDM...........................................................................................59 11.2.3 Optičke komponente ...................................................................................................59

11.3 Prijenos znanja u C++ ........................................................................................................60 11.4 Odnosi među komponentama (taksonomija)......................................................................61 11.5 Bazna klasa.........................................................................................................................61 11.6 Specificiranje nasljeđivanja................................................................................................61 11.7 Nasljeđivanje i prava pristupa ............................................................................................62 11.8 Nasljeđivanje i relacije .......................................................................................................62 11.9 Višestruko nasljeđivanje ....................................................................................................63 11.10 Virtualna osnovna klasa ...................................................................................................64 11.11 Karakteristike virtualnog nasljeđivanja............................................................................64 11.12 Pristup članovima izvedenih klasa ...................................................................................65 11.13 Ugrađene pretvorbe i nasljeđivanje..................................................................................65 11.14 Konstruktori i destruktori izvedenih klasa .......................................................................66 11.15 Područje pretraživanja......................................................................................................66 11.16 Ugniježđeni tipovi ............................................................................................................67 11.17 Klase kao argumenti funkcija (ugrađene konverzije) ......................................................67

11.17.1 Klase kao argumenti funkcija (korisničke konverzije) .............................................68 11.18 Nasljeđivanje i preopterećeni operatori............................................................................68 11.19 Polimorfizam i tipovi vezivanja .......................................................................................68 11.20 Dinamičko povezivanje i virtualni funkcijski članovi .....................................................69 11.21 Pozivi virtualnih funkcijskih članova...............................................................................70 11.22 Čiste virtualne funkcije ....................................................................................................71 11.23 Virtualni destruktori .........................................................................................................71 11.24 Polimorfizam i proširivost koda.......................................................................................72 11.25 Primjeri zadataka..............................................................................................................73

11.25.1 Listing datoteke List.h ............................................................................................73 11.25.2 Listing datoteke List.cpp ........................................................................................74

12 Predlošci (templates).........................................................................................................80 12.1 Što su predlošci (templates)?..............................................................................................80 12.2 Predlošci funkcija...............................................................................................................80

12.2.1 Definicija predloška funkcije ......................................................................................80 12.2.2 Implicitno instanciranje predložaka ............................................................................81 12.2.3 Eksplicitno instanciranje predložaka...........................................................................81 12.2.4 Preopterećivanje predložaka funkcija .........................................................................82

12.3 Predlošci klasa....................................................................................................................82

Page 5: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

5

12.3.1 Definicija predloška klase ...........................................................................................82 12.3.2 Instanciranje predloška klase i deklaracija objekta .....................................................83 12.3.3 Specijalizacija funkcijskih članova .............................................................................83 12.3.4 Predlošci, statički članovi i ugnježđivanje ..................................................................84 12.3.5 Predlošci i prijatelji klasa ............................................................................................84 12.3.6 Predlošci i nasljeđivanje..............................................................................................84

12.4 Primjeri zadataka................................................................................................................85 13 Imenici (namespaces) ...................................................................................................88

13.1 Što su imenici? ...................................................................................................................88 13.2 Deklaracija imenika............................................................................................................88 13.3 Pristup elementima imenika ...............................................................................................88 13.4 Deklaracija i direktiva using .............................................................................................89 13.5 Klase i deklaracija using ...................................................................................................89

14 Iznimke..............................................................................................................................90 14.1 Što su iznimke? ..................................................................................................................90 14.2 Blokovi pokušaja i hvatanja iznimaka ...............................................................................90 14.3 Identifikacija iznimaka.......................................................................................................90 14.4 Tijek obrade iznimaka........................................................................................................91 14.5 Liste mogućih iznimaka .....................................................................................................91

15 Pretprocesorske naredbe .................................................................................................92 15.1 Pojam pretprocesorskih naredbi .........................................................................................92 15.2 Naredbe #include, #define i #undef ...........................................................................92 15.3 Uvjetno prevođenje ............................................................................................................93 15.4 Uvjetno prevođenje, assert i pronalaženje pogrešaka ....................................................93 15.5 Operatori za rukovanje nizovima .......................................................................................94

16 Organizacija koda ............................................................................................................95 16.1 Zašto je organizacija koda potrebna...................................................................................95 16.2 Datoteke zaglavlja ..............................................................................................................95 16.3 Povezivanje ........................................................................................................................96 16.4 Specifikatori static i extern ...........................................................................................96

17 C i C++ ..............................................................................................................................97 17.1 Usporedba C i C++.............................................................................................................97 17.2 Sličnost sintakse C i C++ ...................................................................................................97 17.3 Razlike sintakse C i C++....................................................................................................97 17.4 Novi mehanizmi u C++......................................................................................................98 17.5 Nove ključne riječi u C++ ..................................................................................................98 17.6 C++ kao objektno orijentirani jezik ...................................................................................99

Page 6: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

6

Language shapes the way we think,

and determines what we can think about.

B. L. Whorf (iz B. Stroustrup, "The C++ Programming Language")

Page 7: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

7

1 Literatura • H. Schildt: “Turbo C++ for Windows Inside & Out”, Osborne McGraw-Hill

brzi uvod u osnove C++-a za čitatelje koji poznaju C sintaksu. Osnove C++ programiranja za MS Windowse.

• Boris Motik i Julijan Šribar: “Demistificirani C++”, Element, Zagreb, 1997.

detaljan pregled C++ jezika uz opsežan pregled C sintakse. Pristup objektno orjentiranom dizajnu.

• S. B. Lippman: “C++ Primer”, 2nd Edition , Addison-Wesley Publishing Company, Reading, Massachusetts

sveobuhvatan prikaz C++ jezika uz kratak uvod u C sintaksu

• B. Eckel: “Thinking in C++”, 2nd edition

(vrlo) detaljan pregled C i C++ jezika

• M. A. Ellis, B. Stroustrup: “The Annotated C++ Reference Manual”, Addison-Wesley Publishing Company, Reading, Massachusetts

C++ standard

• R. B. Murray: “C++ Strategies and Tactics”, Addison-Wesley Publishing Company, Reading, Massachusetts

napredniji pregled uporabe C++ jezika uz naglasak na objektno orjentiranom dizajnu

2 Plan predavanja Teme obrađene u predavanjima:

1. osnovni elementi prog. jezika C:

• Osnovni tipovi podataka • Operatori • Naredbe za kontrolu toka programa • Polja, pokazivači i reference • Funkcije • Ulazni i izlazni tokovi

2. osnovni elementi prog. jezika C++

3. objektno orijentirani dizajn

Page 8: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

8

3 Osnovni tipovi podataka Teme obrađene u ovom dijelu:

• Vrste podataka • Identifikatori • Objekti i varijable • Definicija (deklaracija) objekata • Ugrađeni tipovi podataka • Konstante • Brojevi • Sufiksi • Znakovi • Pretvorbe između tipova. Cast operator. • Pobrojeni tipovi, identifikator typedef

3.1 Vrste podataka Program obavlja operacije nad nekim podacima. Podaci se mogu svrstati u dvije osnovne skupine:

• podaci s promjenjivom vrijednošću (varijable), i

• podaci s nepromjenjivom vrijednošću (konstante).

Osnovna razlika između ovih podataka leži samo u dozvoli pridruživanja njihove vrijednosti – kod konstanti je to moguće samo pri definiciji konstante (uvođenju simboličkog imena – identifikatora u prostor imena (scope)) dok je kod varijabli ta promjena moguća bilo gdje unutar programa ali nakon definicije varijable

Navedena razlika vrijedi u većini programskih jezika, dok neki ipak ne razlikuju varijable od konstanti, tj. dozvoljavaju pridruživanje vrijednosti samo jednom (npr. single assignment jezici kao Erlang).

3.2 Identifikatori Podacima se pristupa preko nekog imena koji se naziva identifikator: int mojaVar; //mojaVar je identifikator

Identifikatori moraju poštovati sljedeća pravila:

1. identifikator može sadržavati slova (engleske) abecede, brojeve i znak za podcrtavanje (poveznica, underscore _),

2. prvi znak identifikatora mora biti slovo ili poveznica, ui

3. identifikator ne smije biti jednak nekoj od ključnih riječi, ali ključna riječ može biti dio identifikatora:

mojaDoubleVarijabla

Identifikatori se obično pišu malim slovima, za razliku od imena simboličkih konstanti koja se (obično) pišu velikim slovima:

Page 9: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

9

#define MAX 10 //konstanta imena MAX

int max; //varijabla s identifikatorom max

Za identifikator je najbolje odabrati mnemoničko ime koje će asocirati na svrhu njegove uporabe u programu.

Identifikator koji se sastoji od više riječi treba pisati uporabom poveznice ili velikih početnih slova riječi: int indeks_polja; //C konvencija

int indeksPolja; //C++ konvencija

U ovim predavanjima biti će korištena isključivo C++ konvencija.

3.3 Objekti i varijable Pod nazivom objekt se smatra dio memorije koji sadrži podatke nekog tipa, a koji je dostupan preko svog simboličkog imena (identifikatora). U C++ se pod nazivom objekt u užem smislu misli na konkretnu realizaciju klase, a pod nazivom varijabla jednostavan objekt koji sadrži samo jedan tip podataka (jedan cijeli broj, znak, itd.).

Sa svakim objektom povezane su dvije vrijednosti:

1. vrijednost podatka (podataka) spremljenih na neku memorijsku lokaciju. Ova vrijednost se često naziva rvalue, i

2. početna adresa memorije na kojoj je spremljen taj podatak (podaci). Ova vrijednost se naziva lvalue.

Prije upotrebe objekta potrebno je rezervirati njegovo mjesto u memoriji. Definicija objekta je rezervacija tog memorijskog prostora. Veličina prostora ovisiti će o tipu objekta. Deklaracija objekta je uvođenje njegovog imena u prostor imena (scope), ali ne i njegovo zauzeće u memoriji. Definicija tog objekta mora se nalaziti na nekom drugom mjestu.

Razlika između deklaracije i definicije objekata dolazi do izražaja samo kod nekih specijalnih slučajeva (kao što je extern povezivanje objekta). Zato se umjesto definicije najčešće upotrebljava riječ deklaracija. Kod funkcija je ta razlika velika pa tamo nije moguće upotrebljavati ove riječi kao sinonime.

3.4 Definicija (deklaracija) objekata Najjednostavnija deklaracija se sastoji od tipa objekta (koji može biti ugrađeni ili korisnički) te imena objekta: tip_objekta imeObjekta;

int var; //int je ugrađeni tip

moj_tip b; //moj_tip je korisnički tip

Deklaracijom objekta rezervira se njegov memorijski prostor ovisno o tipu:

Vrijednost podataka koju objekt sadrži (rvalue) deklaracijom u pravilu nije određena pa se na nju ne treba oslanjati (iako C++ inicijalizira vrijednost varijabli na 0). U jednom bloku naredbi ne smije se deklarirati više objekata s istim imenom.

Prilikom deklaracije objekta moguće je provesti i njegovu inicijalizaciju: double c=2.2; //inicijalizacija varijable pridruživanjem vrijednosti

Page 10: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

10

klasa obj1(1); //inicijalizacija objekta konstruktorom

klasa obj2=obj1; //inicijal. objekta pridruživanjem (copy konstruktor)

3.5 Ugrađeni tipovi podataka U C++ definirani su ugrađeni tipovi podataka (memorijsko zauzeće), te operacije nad njima (pridruživanje, aritmetičke operacije).

Pravila provjere tipa (type-checking rules) utvrđuju pogrešne operacije nad tipom. Pravila konverzije određuju postupak u slučaju predaje pogrešnog tipa operaciji (funkciji)

U tablici su dani ugrađeni brojevni tipovi podataka:

Navođenjem ključne riječi unsigned ispred tipa ograničava se raspon vrijednosti samo na pozitivne brojeve, pri čemu se dodatni bit predznaka upotrebljava za proširenje raspona granice cjelobrojnih tipova se mogu pronaći u limits.h, a decimalnih tipova u values.h.

3.6 Konstante Ključnom riječi const prije tipa objekta moguće je objekt učiniti konstantom: const tip obj;

Time se prevoditelju naznačuje da se vrijednost objekta ne smije promijeniti pri čemu će svaki pokušaj promjene uzrokovati grešku vrijednost konstante mora se inicijalizirati pri deklaraciji: const float pi=3.14;

Ime konstante definirane na ovaj način postoji u simboličkoj tablici imena i moguće ga je dohvatiti iz programa za simboličko lociranje pogrešaka konstantu je moguće definirati i kao simboličku konstantu pretprocesorskom naredbom #define: #define PI 3.14

Ovo ime ne postoji u simboličkoj tablici imena i predstavlja lošije rješenje,

3.7 Brojevi U C++ postoje dva osnovna tipa brojeva

1. cijeli brojevi (integers), i

2. brojevi s pomičnim zarezom (floating point).

Ugrađeni tip Zauzeće memorije (bytes)

Raspon vrijednosti Točnost

char 1 short int 2 -32768 do 32768 int 2 (4) -32768 do 32768

(-2147483648 do 2147483647)

long int 4 -2147483648 do 2147483647

float 4 -3.4·1038 do –3.4 ·1038 i 3.4·10-38 do 3.4·1038

7 dec. znamenki

double 8 1.7·10308 do –1.7 ·10308 i 1.7·10-308 do 1.7·10308

15 dec. znamenki

long double 10 (8) -1.1·104932 do –3.4 ·10-4932 i 3.4·10-4932 do 1.1·104932

18 dec. znamenki

Page 11: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

11

Unutar koda moguće je koristiti brojevne konstante za inicijalizaciju vrijednosti varijabli ili eksplicitno navođenje vrijednosti unutar izraza.

Cjelobrojne konstante se mogu prikazati u sljedećim brojevnim sustavima:

• dekadskom int a=16;

• oktalnom (navođenje brojke 0 ispred cjelobrojne konstante) int a=020;

• heksadekadskom (navođenje 0x ispred cjelobrojne konstante) int a=0x10;

Brojevne konstante moguće je navoditi i u znanstvenoj notaciji (“exp” zapis): float b=-1.6e-19; //ili -1.6E-19

Unutar broja nisu dozvoljene praznine (npr. između predznaka i broja ili broja i “e”).

3.8 Sufiksi Cijeli se brojevi tretiraju kao int vrijednosti što je moguće promijeniti u double dodavanjem decimalne točke iza broja 1.

Realni se brojevi tretiraju kao double vrijednosti, što je moguće pretvoriti u float dodavanjem slova f ili F iza konstante: 1.2f

Povećanje preciznosti postiže se dodavanjem slova l ili L: 1.2l //long double

Ograničenje na pozitivne brojeve dodavanjem slova u ili U: 1.2u

Broj ispred sufiksa Sufiks Rezultirajući tip cijeli

L, l U, u

int long int unsigned int

decimalni F, f L, l

double float long double

3.9 Znakovi Znakovi se pridružuju varijablama tipa char pri čemu se navode između jednostrukih navodnika: char a=‘a’;

Za znakove koji se ne mogu prikazati na zaslonu koristi se poseban zapis (escape sequence): char noviRed=‘\n’;

Na znakove se mogu primjenjivati operatori uspoređivanja i aritmetički operatori pri čemu se znak pretvara u svoj cjelobrojni ekvivalent iz ASCII tablice: char zn=‘A’;

Page 12: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

12

zn++;

cout<<zn<<endl; //B

zn=zn+32;

cout<<zn<<endl; //b

znak značenje \n novi red \t horizontalni tab \v vertikalni tab \b backspace \r pomak na početak reda \f nova stranica \a zvučni signal \\ backslash \? upitnik \' jednostruki navodnik \" dvostruki navodnik \0 NULL znak \yyy znak čiji je kod zadan oktalno \xyyy znak čiji je kod zadan heksadekadski

3.10 Pretvorbe između tipova. Cast operator. Definirane su uobičajene implicitne konverzije koje pretvaraju neki ugrađeni tip podataka u drugi ako je to potrebno (npr. funkcija kao argument očekuje float, a naveden je int).

Iste konverzije se primjenjuju pri obavljanju aritmetičkih funkcija na dva različita tipa (npr. određivanje tipa rezultata pri zbrajanju int i float).

Uobičajene su konverzije na jedan tip “više” po zauzeću memorije. Operatorom dodjele tipa (cast operatorom) moguće je eksplicitno dodijeliti tip konverzije brojevnoj konstanti ili varijabli. Ciljni tip određuje se navođenjem imena u zagradama ispred identifikatora varijable ili broja: float rez=(float)a / (float)b;

Dozvoljeno je koristiti i funkcijski oblik dodjele tipa: float rez=float(a) / float(b);

Za specificiranje tipa konstante češće se koriste sufiksi. Predani tip Ugrađene konverzije prema char int

long float (long) double

int long float (long) double

long float (long) double

float (long) double double long double

3.11 Pobrojeni tipovi, identifikator typedef

Page 13: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

13

Pobrojani tip (enumerated type) se koristi ako varijabla poprima vrijednost iz (malog) skupa ograničenih vrijednosti: enum godina {prva, druga, treca, cetvrta, peta};

godina trenutnaGod = cetvrta;

Vrijednost varijable pobrojanog tipa zamjenjuje se cijelim brojem koji ukazuje na poziciju vrijednosti unutar liste (prvi element ima podrazumijevanu vrijednost 0): cout<<trenutnaGod<<endl; //3

Može se eksplicitno odrediti vrijednost indeksa prvog člana: enum godina {prva = 1, druga, treca, cetvrta, peta};

godina trenutnaGod = cetvrta;

cout<<trenutnaGod<<endl; //4

Ako se navede deklaracija varijabli odmah iza definicije pobrojanog tipa, njegovo se ime može i ispustiti (osim ako se ne koristi dalje u programu): enum {false=0, true} logicka;

Operatorom typedef uvodi se novo ime za već postojeći ugrađeni ili korisnički tip podataka:

typedef poznati_tip novoIme;

typedef int cjelobrojni;

cjelobrojni a,b;

Page 14: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

14

4 Operatori Teme obrađene u ovom dijelu:

• Aritmetički operatori • Logički operatori • Poredbeni operatori • Bitovni operatori i operator razdvajanja • Operatori pridruživanja • Hijerarhija operatora

4.1 Aritmetički operatori Unarni operatori se mogu primijeniti samo na jedan objekt, dok binarni operatori djeluju na dva objekta.

tip operatora primjena operatora nad operandima

značenje

+x unarni plus -x unarni minus ++x uvećaj prije --x umanji prije x++ uvećaj nakon

unarni

x-- umanji nakon x+y zbrajanje x-y oduzimanje x*y množenje x/y dijeljenje

binarni

x%y modulo

Kod upotrebe prefiks operatora (++x, --x) prvo se varijabla uvećava ili umanjuje, a zatim se dohvaća njezina vrijednost. Kod upotrebe postfiks operatora (x++, x--) prvo se dohvaća vrijednost varijable, a zatim se ona uvećava ili umanjuje.

Ukoliko vrijednost varijable prekorači gornju dopuštenu granicu dolazi do brojčanog (numeričkog) preljeva pri čemu se gubi bit najveće težine koji više ne stane u rezerviranu memorijsku lokaciju: int i=32767+1; //0

Prilikom obavljanja aritmetičke operacije nad različitim tipovima dolazi do ugrađene konverzije na zajednički tip. Ukoliko su tipovi operanada jednaki ili se daju svesti na zajednički tip obavlja se aritmetička operacija nad tim tipom. Konverzija na tip varijable u koju se pohranjuje rezultat vrši se nakon izračuna rezultata: float a=1/3;

• daje rezultat 0, jer je rezultat cjelobrojnog dijeljenja 0 koja se tek pri pridruživanju varijabli konvertira na float double b=1/3.;

• obavlja se double dijeljenje jer se broj 1 svodi na zajednički tip double.

4.2 Logički operatori

Page 15: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

15

Varijable ugrađenog tipa bool mogu poprimiti logičke vrijednosti true (ili broj 1) i false (ili broj 0) bool logicka=true;

Varijable tipa bool nisu dio C++ standarda (barem ne kod starijih standarda) tako da se ako programer ima želju koristiti vrijednosti true i false preporuča deklaracija pobrojenog tipa: enum {false=0, true} mojBool;

mojBool logicka=true;

Time se osigurava sigurna portabilnost koda između starijih i novijih prevoditelja Logički operator Značenje Zamjenska oznaka x || y logički ili x or y x && y logički i x and y !x logička negacija not x

4.3 Poredbeni operatori Poredbeni operatori omogućuju usporedbu dva objekta pri čemu se dobiva rezultat tipa bool ako je uvjet usporedbe zadovoljen rezultat je true, inače false. cout<< (1 < 2) <<endl; //1

char a=‘M’;

int b=77;

cout<< (int)a!=b <<endl; //0

Poredbeni operator Značenje x!=y različito x==y jednako x>=y veće ili jednako x>y veće od x<=y manje ili jednako x<y manje od

4.4 Bitovni operatori i operator razdvajanja Bitovni operatori omogućuju izravne operacije nad bitovima.

Operator razdvajanja služi za razdvajanje izraza u naredbama, pri čemu se oni izvode s lijeva na desno: int i; i = 1 , i + 1; //varijabli i se prvo pridruži vrijednost 1, a zatim se ona uveća za 1 unsigned int a=UINT_MAX; int b=23; cout<<b<<endl; //23 cout<<(b&a)<<endl; //23, a=0xffffffff (hex)

bitovni operator značenje x>>n posmak udesno x<<n posmak ulijevo x ^ y isključivi ili x | y binarni ili x & y binarni i ~x komplement

2.5. Operatori pridruživanja

Page 16: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

16

Operator pridruživanja = objektu s lijeve strane pridružuje vrijednost izraza s desne strane.

Operatori obnavljajućeg pridruživanja (update assignment) obavljaju zadanu operaciju između objekta s lijeve strane i vrijednosti izraza s desne strane.

Operatori obnavljajućeg pridruživanja sastoje se od odgovarajućeg operatora i znaka = != &= ^= <<= >>= %= /= *= -= += = Operatori pridruživanja imaju manji prioritet od svih ostalih operatora (osim razdvajanja) tako da uvijek dolazi do izračunavanja izraza s desne strane prije obavljanja odgovarajuće operacije: int a=1, b=2, c=3; a+=b*c; //7

4.5 Hijerarhija operatora , najnižiprioritet, a :: imaju najvišis desna na lijevododjela tipa ()

s lijeva na desnorazdvajanje,s desna na lijevotip objekta typeid izbriši objekt

stvori objekt

unarni operatori

umanji prije

uvećaj prije

veličina objekta

umanji nakon

uvećaj nakon

poziv funkcije

indeksiranje

izbor člana

područje (scope)

područje (scope)

značenje

s desna na lijevo

s desna na lijevo

s desna na lijevo

s desna na lijevo

s desna na lijevo

s desna na lijevo

s lijeva na desno

s lijeva na desno

s lijeva na desno

s lijeva na desno

s lijeva na desno

s lijeva na desno

s desna na lijevo

pridruživanje

s desna na lijevo pridruživanja = *= /= += -= &= delete s desna na lijevouvjetni izraz?:new s lijeva na desnologički ili||* & + - s lijeva na desnologički i&&-- s lijeva na desnobitovni ili|++ s lijeva na desnobitovno isključivo ili ^sizeof s lijeva na desnobitovni i&-- s lijeva na desnooperatori jednakosti== !=++ s lijeva na desnoporedbeni operatori < > <= >=() s lijeva na desnobitovni posmak << >>[] s lijeva na desnozbrajanja+ --> s lijeva na desnomnoženja* / %:: s lijeva na desnopokazivači na član ->* .*::

pridruživanjeznačenjeoperatoroperator

Page 17: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

17

5 Naredbe za kontrolu toka programa Teme obrađene u ovom dijelu:

• Blokovi naredbi • Uvjetno grananje – naredba if • Uvjetni operator ? : • Naredba switch • Petlja for • Naredba while • Blok do-while • Naredba break • Naredba continue

5.1 Blokovi naredbi Tok programa nije linearan i ovisi o različitim uvjetima dijelove programa koji se izvode uvjetno ili se ponavljaju grupiraju se u blokove koji se tretiraju kao jedna logička cjelina ili jedna naredba: void main(void) {

int a; {

cin>>a; int b=a-3; cout<<b<<endl;

} cout<<b<<endl; //greška

}

Korištenjem naredbi za kontrolu toka programa moguće je uvjetovati izvođenje bloka (if-else naredba), ponoviti izvođenje određeni broj puta (for petlja) ili ponoviti izvođenje dok neki od uvjeta nije zadovoljen (while-do, do-while petlje). Varijable definirane unutar bloka vidljive su samo unutar njega, jer se tretiraju kao lokalne varijable čiji je domet (scope) jednak bloku unutar kojeg su definirane.

5.2 Uvjetno grananje – naredba naredba if Omogućava uvjetno grananje toka ovisno da li ispunjen uvjet

Opći oblik: if ( uvjet1 ) false

{ blok 1 } else if ( uvjet2 )

{ blok 2 } else if ( uvjet3 )

{ blok 3 } ... else

{ blok n } Uvjeti predstavljaju logičke izraze poput 5-6 > 0, a == b, itd.

Moguće je ugnježđivanje if naredbi.

Page 18: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

18

Na slici 1 prikazan je blok dijagram if-else naredbe.

Slika 1 Blok dijagram if-else naredbi

5.3 Uvjetni operator ? : Ne spada u naredbe za kontrolu toka programa, ali je po značenju vrlo sličan if-else bloku

Opći oblik: uvjet ? izraz1 : izraz2 ; Ako je logički izraz uvjet istinit izvršava se izraz1, a u suprotnom izraz2.

Na slici 2 prikazan je blok dijagram ?: naredbe.

Slika 2 Blok dijagram ?: naredbe

5.4 Naredba switch Ukoliko ispitivani izraz može poprimiti više različitih vrijednosti, zbog preglednosti se umjesto naredbe if koristi naredba switch.

Opći oblik: switch ( cjelobrojni_izraz ) {

case konstantan_izraz_1 : //prvi blok naredbi break; case konstantan_izraz_2 : //drugi blok naredbi break; ... default: //n-ti blok naredbi

}

Prvi korak je izračunavanje vrijednosti cjelobrojni_izraz, što može biti rezultat aritmetičkog ili logičkog izraza (4 > 2 zamjenjuje se s 1) jednakost uvjeta s nekim izrazom se ispituje redoslijedom navođenja u programu. Ukoliko je jednakost ispunjena izvodi se slijed naredbi

Page 19: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

19

naveden iza : (ne upotrebljavaju se oznake {} bloka!). Nakon izvođenja odgovarajućeg bloka prelazi se na sljedeći ispitivanje jednakosti sljedećeg uvjeta. Ako se želi osigurati prekid daljnjeg ispitivanja nakon što je izveden odgovarajući blok koristi se naredba break na kraju bloka koja uzrokuje prekid izvršavanja naredbe switch. Ako cjelobrojni_izraz nije jednak ni jednom od uvjeta izvodi se blok naredbi iza default naredbe. Navođenje default bloka nije obavezno.

Na slici 3 prikazan je blok dijagram switch naredbe.

Slika 3 Blok dijagram switch naredbe

5.5 Petlja for Koristi se za ponavljanje bloka poznati broj puta

Opći oblik: for (početni_izraz ; uvjet_izvođenja ; izraz_prirasta ){

// blok naredbi }

početni_izraz je najčešće pridruživanje početne vrijednosti brojača. Rezultat ispitivanja uvjet_izvođenja mora biti bool. Ako je rezultat ispitivanja istinit izvršava se blok naredbi i izračunava izraz_prirasta (npr. povećanje brojača). Ako je rezultat ispitivanja neistinit izlazi se iz petlje.

Na slici 4 prikazan je blok dijagram for petlje.

Slika 4 Blok dijagram for petlje

5.6 Naredba while

Page 20: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

20

Koristi se za ponavljanje bloka čiji broj ponavljanja nije poznat. Uuvjet izvođenja se ispituje prije izvođenja bloka.

Opći oblik: while ( uvjet_izvođenja ){

//blok naredbi } Ako je rezultat izračunavanja uvjet_izvođenja logička istina izvodi se blok_naredbi. Nakon toga se ponavlja ispitivanje uvjeta. Ako je je rezultat izračunavanja uvjet_izvođenja logička neistina preskače se blok_naredbi i nastavlja se izvođenje od prve naredbe iza bloka. blok_naredbi utječe na uvjet_izvođenja (inače je petlja beskonačna).

Na slici 5 prikazan je blok dijagram while petlje.

Slika 5 Blok dijagram while petlje

5.7 Blok do while Uvjet ponavljanja se ispituje nakon što je blok izvršen – blok se izvršava barem jedanput.

Opći oblik: do{

//blok naredbi

}while ( uvjet_ponavljanja )

Na slici 6 prikazan je blok dijagram do while naredbe.

Slika 6 Blok dijagram do while petlje

3.8. Naredba break Koristi se samo u petljama i switch naredbi uzrokuje prekid izvođenja tijela petlje i skok na prvu sljedeću naredbu izvan petlje. petlja {

naredba_1;

Page 21: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

21

naredba_2;

. . .

break;

. . .

naredba_n;

}

// naredbe iza bloka

Na slici 7 prikazan je blok dijagram naredbe break.

Slika 7 Blok dijagram naredbe break

5.8 Naredba continue continue Koristi se u istim uvjetima kao i naredba break, ali uzrokuje skok na kraj petlje pri čemu se petlja ponovno izvršava. petlja {

naredba_1; naredba_2; . . . continue; . . . naredba_n;

}

Upotreba naredbi break i continue nije preporučljiva (osim u iznimnim slučajevima kao kod naredbe switch), jer se time narušava strukturiranost programa.

Na slici 1 prikazan je blok dijagram naredbe continue.

Slika 8 Blok dijagram naredbe continue

4. Polja, pokazivači i reference 4. Polja, pokazivači i reference Teme obrađene u ovom dijelu:

• Polja podataka • Definicija polja • Znakovni niz

Page 22: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

22

• Operacije nad znakovnim nizovima • Pokazivači (pointeri) • Pridruživanje adrese pokazivaču • Aritmetika pokazivača • Veza polja i pokazivača • Dvodimenzionalna polja i pokazivači • Dinamička alokacija memorije – operator new • Dinamička alokacija polja • Reference

4.1. Polja podataka 4.1. Polja podataka Struktura podataka koja se sastoji od konačnog broja istovrsnih podataka (članova polja) koji dijele isto ime (identifikator polja). Svi podaci u polju moraju biti istog tipa direktan pristup podacima preko indeksa koji može biti nenegativan cijeli broj:

x[ind] (jednodimenzionalno polje)

y[ind1][ind2] (dvodimenzionalno polje)

z[ind1][ind2]. . . [indn] (n-dimenzionalno polje)

pri čemu indeks pristupa ind… može biti jednak konstanti, varijabli ili nekom izrazu.

polje dimenzije n+1

polje dimenzije 2×(n+1)

polje dimenzije (m+1)×(n+1)

4.1.1. Definicija polja 4.1.1. Definicija polja Statička definicija polja mora se obavezno navesti dimenzija polja u obliku cjelobrojne, simboličke konstante ili fiksnog izraza: int ia[5], aiHuge[MAX]; char text[LINE+1]; float sqMatrix[ROW][COL];

Tip polja može biti bilo koji ugrađeni ili izvedeni tip: toBeOr notToBe[THAT][IS][THE][QUESTION];

Prilikom definicije moguće je i inicijalizirati članove polja. U tom slučaju nije potrebno navoditi dimenzije, jer se one određuju prema inicijalizacijskoj listi: int ia[] = {1,2,3,4,5}; //cjelobrojni niz od pet članova

Page 23: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

23

float fa[][] = { //matrica 2x3 {1.1,2.2,3.4}, {2.1,3.2,1.3}

}; float[2][3] = {1.1,2.2,3.4,2.1,3.2,1.3} //isto kao i gore

Svi neinicijalizirani članovi popunjaju se nulama.

Prvi član polja ima indeks 0, dok je indeks zadnjeg člana polja jednak duljini polja umanjenoj za 1 (jednodimenzionalna polja). Dimenzije polja moraju biti poznate prilikom prevođenja programa, te se ne mogu mijenjati tijekom izvođenja programa (statička definicija): int n; cout<<n; float fa[n] ; //greška – n nije poznat prilikom prevođenja

Prevodilac ne provjerava da li su indeksi polja unutar dimenzija polja – moguće je pristupiti memorijskoj adresi koja je izvan područja rezerviranog za polje.

5.8.1 Znakovni niz Deklaracija kao polje znakova char znNiz[MAXZN+1];

Preporučljivo je da dimenzija char polja bude za 1 veća od broja znakova koje sadrži, da bi se na zadnje mjesto moglo postaviti oznaka kraja znakovnog niza (stringa) ‘\0’ ili 0 (NULL character): char znakovniNiz[4] = {‘C’,’+’,’+’}; znakovniNiz[3]=0;

Drugi način inicijalizacije: char bestBuy[]=“Lada”;

Niz bestBuy sadrži 5 znakova jer je uključena oznaka za kraj ‘\0’.

NULL character je važan jer on označava završetak znakovnog niza u memoriji što je neophodan podatak za funkcije koje vrše transformacije nad znakovnim nizovima. Operacije nad znakovnim nizovima

Deklaracije funkcije koje obavljaju operacije nad znakovnim nizovima sadržane su u datoteci string.h koja se u program uključuje kao #include <string.h> ili #include <cstring> char strcat( char *pocetak, const char *nastavak );

• Nadovezuje niz nastavak na niz pocetak. Dodaje NULL znak na kraj niza. char strncat(char *pocetak, const char *nastavak, size_t duljina);

• Nadovezuje niz nastavak na niz pocetak u duljini od najviše duljina znakova. Dodaje NULL znak na kraj niza.

const char *strchr( const char *niz, int znak );

• Traži se prva pojava znaka znak. Ako nije pronađen vraća se NULL pokazivač. int strcmp( const char *niz1, const char *niz2 );

Page 24: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

24

• Aritmetički uspoređuje nizove niz1 i niz2 sve dok se ne dođe do različitog znaka. Vraća 0 ako su nizovi jednaki, >0 ako je niz1 veći, <0 ako je niz1 manji.

int strncmp( const char *niz1, const char *niz2, size_t duljina );

• Jednaka usporedba kao i strcmp, ali u duljini od max duljina znakova char *strcpy( char *odrediste, const char *izvornik );

• Kopira sadržaj niza izvornik u niz odrediste. Kopiranje se prekida nakon što je dosegnut NULL znak (NULL znak se kopira).

char *strncpy( char *odrediste, const char *izvornik, size_t duljina );

• Isto kao i strcpy ali se kopira najviše duljina znakova. size_t strlen( const char *niz );

• Vraća duljinu znakovnog niza niz (ne računajući NULL znak). const char *strpbrk( const char *niz, const char *znakovi );

• Pretražuje niz niz do pojave prvog znaka iz niza znakovi. Vraća se pokazivač na taj znak ili NULL pokazivač ako nije pronađen.

size_t strspn( const char *niz, const char *podniz );

• Traži u nizu niz prvi znak koji se ne pojavljuje u nizu podniz. Vraća pokazivač na taj znak ili NULL pokazivač ako nije pronađen.

const char *strstr( const char *niz, const char *podniz );

• Traži prvu pojavu niza podniz u nizu niz. Vraća pokazivač na mjesto prve pojave podniza ili NULL pokazivač ako podniz nije pronađen.

const char *strchr( const char *niz, int znak );

• Pretražuje niz niz i traži zadnju pojavu znaka znak. Vraća pokazivač na taj znak ili NULL pokazivač ako znak nije pronađen.

char *strtok( char *niz, const char *granicnici );

• Pretražuje niz niz i razbija ga na podnizove razdvojene nekim od znakova iz niza granicnici. Vraća pokazivač na početak sljedećeg podniza ili NULL pokazivač ako takav podniz ne postoji.

4.2. Pokazivači (pointeri) Objekti koji pokazuju na drugi objekt tako što sadrže njegovu memorijsku adresu veličina svakog pokazivača je 4 bytea koliko je dovoljno za spremanje adresibilne memorijske lokacije.

Definicija pokazivača: tip *pokazivac; //*pokazivac može pokazivati na tip tip

Pokazivač koji je samo definiran ne pokazuje ni na što – time je rezervirano samo mjesto u memoriji na koje će se spremiti adresa postojećeg objekta mogu pokazivati na bilo koji ugrađeni ili korisnički tip podataka: int *intP; char *charP; mojTip *mojTipP;

Operator * naziva se operatorom dereferenciranja (indirekcije)

Page 25: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

25

5.8.2 Pridruživanje adrese pokazivaču Prilikom definicije pokazivaču je moguće pridružiti adresu (postojećeg) objekta istog tipa. Isto je moguće učiniti bilo gdje u kodu programa: int a=5;

Operator & naziva se operatorom adrese i vraća adresu objekta u memoriji int *pa1 = &a, *pa2; ... pa2 = &a; *pa1=1; *pa2=2;

5.8.3 Aritmetika pokazivača Pokazivač sadrži adresu objekta u memoriji: int a=1, b=2; //objekt a je na mem. adresi 1000 byte, a objekt b na 1002 byte int *pa=&a; cout<<pa; //adresa objekta na kojeg pokazuje - 1000 cout<<*pa; //vrijednost objekta na kojeg pokazuje – 1

Povećanje pokazivača za 1 znači da on mora pokazivati na sljedeći objekt tog tipa u memoriji –to znači uvećanje adrese za veličinu tipa (char 1 byte, int 2 byte, float 4 byte, double 8 byte): pa++; //uvećanje adrese za veličinu tipa cout<<pa; //1002 cout<<*pa; //ispis sadržaja varijable b, jer su objekti a i b sukcesivni u memoriji

5.8.4 Veza polja i pokazivača Članovi polja dohvaćaju se preko pokazivača, pri čemu samo ime polja ima smisao pokazivača na prvi član polja: int ia[]={1,2,3,4,5}; cout<<*ia; //1 cout<<*(ia+2); //3 cout<<ia[2]; //3

Prilikom deklaracije polja ne dolazi do stvaranja pokazivača u memoriji – ime polja je samo sinonim za pokazivač, tj. može se koristiti kao pokazivač

5.8.5 Dvodimenzionalna polja i pokazivači Dvodimenzionalno polje se može shvatiti kao jednodimenzionalni niz jednodimenzionalnih polja: int ia[][]={{1,2,3,4}, {5,6,7,8}, {9,10,11,12}}

ia[0], ia[1] i ia[2] se tretiraju kao pokazivači na prvi član u recima 1, 2 i 3 i u ovom slučaju vrijedi aritmetika nad pokazivačima: cout<<*(ia[2]+2) //11

5.8.6 Dinamička alokacija memorije, operator new Operatorom new rezervira se memorijski prostor za objekt nekog ugrađenog ili korisničkog tipa. Može se rezervirati prostor i za strukture kao npr. polja povratna vrijednost je početna adresa memorijskog prostora rezerviranog za objekt ili NULL ako rezervacija memorije nije uspjela: int *pok=new int(2);

Pokazivač pok pokazuje na novostvoreni objekt tipa int koji je inicijaliziran na vrijednost 2.

Page 26: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

26

if (!pok) exit(1);

• provjera da li je alokacija memorije uspjela

Objekt stvoren operatorom new predstavlja dinamički objekt, te ne spada u automatske objekte, tj. neće biti uništen izlaskom iz bloka u kojem je deklariran dealokaciju memorije koju zauzima objekt stvoren na ovaj način mora obaviti programer operatorom delete i navođenjem početne adrese memorije koju taj objekt zauzima: delete pok;

Dinamičkim objektima pristupa se isključivo korištenjem pokazivača

5.8.7 Dinamička alokacija polja Omogućava da se stvori polje objekata tijekom izvršavanja programa, tj. da njegove dimenzije budu određene vrijednostima koje nisu poznate pri prevođenju programa: int n; cin>>n; float *fa=new float[n];

Oslobađanje zauzete memorije obavlja se korištenjem operatora delete: delete [] fa;

Moguće je alocirati i višedimenzionalno polje, no sve dimenzije osim prve moraju biti poznate pri prevođenju: int **a; a=new int*[3]; //polje tri int pokazivača a[0]=new int[2]; a[1]=new int[3]; a[2]=new int[5]; //svaki pokazivač pokazuje na novo jednodimenzionalno int polje int (*b)[3]=new int[n][3]; delete [] b;

5.9 Reference Poseban tip podataka sličan pokazivačima – predstavljaju drugo ime za objekt određenog tipa te se nazivaju i aliasima prilikom deklaracije moraju biti inicijalizirane: int a=1; int &ref=a; //varijabli a se može pristupiti preko imena a i ref a=2; ref=3;

Operator & se stoga naziva i operatorom referenciranja.

Page 27: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

27

6 Funkcije Teme obrađene u ovom dijelu:

• Deklaracija funkcije • Definicija funkcije • Argumenti funkcije • Argumenti funkcije (prijenos po vrijednosti) • Argumenti funkcije (prijenos kao pokazivač) • Argumenti funkcije (prijenos kao referenca) • Polja kao argumenti funkcije • Podrazumijevani argumenti • Neodređeni argumenti • Pokazivači na funkcije • Stog i rekurzija • Pokazivači i reference kao povratne vrijednosti • Globalni i statički objekti

6.1 Deklaracija funkcije Funkcija je korisnički definirana operacija koja je predstavljena imenom deklaracija funkcije sadrži ime funkcije, listu argumenata koji predstavljaju ulazno-izlazni tok podataka u funkciju te povratni tip koji predstavlja izlazni tip podataka: <povratni_tip> ime_funkcije (<tip1> arg1, <tip2> arg2,…);

Deklaracija funkcije ne opisuje što funkcija radi, ona daje samo prototip funkcije naziv argumenata može se ispustiti-kod deklaracije bitan je tip argumenata: int f1(int a, float b); int f2(int, float);

Moguće je deklarirati funkcije s neodređenim brojem argumenata broj, tip i poredak argumenata određuju potpis funkcije (funkcije f1 i f2 imaju jednake potpise) svaka funkcija mora biti deklarirana u izvornom kodu prije korištenja (ovo ne mora biti zadovoljeno u C-u).

6.2 Definicija funkcije Definicija funkcije sadržava uz naziv, povratni tip i listu argumenata tijelo funkcije, tj. programski kod koji opisuje što funkcija radi: int neg(int a){

return –a; }

Ključna riječ return obavlja prijenos povratne vrijednosti na mjesto poziva. Između povratnog tipa funkcije i tipa povratne vrijednosti obavlja se ugrađena ili korisnička konverzija ako je potrebno. Broj, poredak i tip argumenata u pozivu, definiciji i deklaraciji funkcije moraju biti jednaki.

Definicija funkcije je ujedno i njezina deklaracija, te nije dozvoljeno deklarirati funkciju nakon definicije. Deklaracija i definicija funkcije ne moraju biti u istoj datoteci. Definicija funkcije služi prevodiocu da bi mogao obaviti strogu sintaksnu provjeru pri pozivu funkcije.

Page 28: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

28

Povezivač će omogućiti izvršavanje funkcije pronalaskom njezine definicije ukoliko ona nije u istoj datoteci kao i poziv funkcije.

6.3 Argumenti funkcije Argument u definiciji funkcije zove se formalni argument funkcije i postoji samo unutar tijela funkcije: int f( int a ){ //a je formalni argument funkcije

… return a;

}

Pri pozivu funkcije formalni argument zamjenjuje se stvarnim argumentom, tj. stvarnom vrijednošću: int b=3; f(b); //b je stvarni argument funkcije

Funkcije bez argumenata: int f(){ //u C++ isto što i int f(void)

… return 1;

}

6.4 Argumenti funkcije (prijenos po vrijednosti) Kod prijenosa po vrijednosti (call by value) formalni i stvarni argument nisu međusobno povezani – unutar tijela funkcije se stvara interna kopija prenesenog objekta. Promjene nad kopijom objektom unutar funkcije nisu vidljive izvan nje, tj. objekt u pozivajućem kodu ostaje nepromijenjen: int f(int a){ //argument se prenosi po vrijednosti

a++; //a je lokalna kopija stvarnog argumenta return a;

} int b=1; int c=f(b); cout<<b<<endl; //1 – nema promjene u pozivajućem kodu cout<<c<<endl; //2 – promjena se u pozivajući kod prenijela preko povratne vrijednosti

6.5 Argumenti funkcije (prijenos kao pokazivač) Pri prijenosu kao pokazivača promjene nad formalnim parametrima se odražavaju na stvarne parametre jer se umjesto stvaranja kopije objekata koristi memorijska adresa stvarnog parametra: int f(int *a){ //argument se prenosi kao pokazivač

(*a)++; //a je lokalna kopija prenesene adrese, no *a pokazuje na istu lokaciju kao i stvarni argument return (*a);

} int b=1; int c=f(&b); //potrebno je prenijeti adresu objekta cout<<b<<endl; //2 –promjena u pozivajućem kodu cout<<c<<endl; //2

Prilikom prijenosa kao pokazivač ne postoji ugrađena konverzija pa tipovi argumenata moraju točno odgovarati

6.6 Argumenti funkcije (prijenos kao referenca)

Page 29: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

29

Prijenos argumenta kao reference ima isti efekt kao i prijenos argumenta kao pokazivač: int f(int &a){ //argument se prenosi kao referenca

a++; return a;

}

a se koristi kao normalna varijabla, no ona predstavlja drugo ime za stvarni parametar, a ne njegovu kopiju int b=1; int c=f(b); //nije potrebno eksplicitno naglasiti prijenos adrese cout<<b<<endl; //2 –promjena u pozivajućem kodu cout<<c<<endl; //2

6.7 Polja kao argumenti funkcije Jednodimenzionalna polja (nizovi) se u funkciju prenose kao pokazivači jer je ime polja sinonim za pokazivač na prvi član polja. Sljedeće deklaracije su stoga jednake: void f1(int polje[]); void f2(int *polje);

I u jednu i u drugu funkciju se može predati polje kao argument: int p[10]; f1(p[]); f2(p);

U funkciju nije moguće prenijeti višedimenzionalna polja čije su dimenzije poznate prilikom prevođenja, jer se kao nepoznata može ostaviti tek prva dimenzija: void f3(int polje[][10]); //argument je polje s proizvoljnim brojem redaka, ali fiksnim brojem stupaca

Problem prijenosa dvodimenzionalnih polja (matrica) rješava se prijenosom polja kao niza jednodimenzionalnih polja (redaka) koji su u memoriji smješteni jedan iza drugog. Elementu polje[i][j] je moguće pristupiti i kao polje[i*brStupaca+j] odnosno *(polje + i*brStupaca+j): void f4(int *polje, int r, int s){

for(int i=0; i<r;i++){ for(int j=0;j<s;j++) cout<<polje(i*s+j)<<endl;

} }

6.8 Podrazumijevani argumenti Podrazumijevana vrijednost argumenta navodi se u deklaraciji funkcije: int f (int a, int b=1){ //argument b ima podrazumijevanu vrijednost 1

… }

Funkcija f se može pozvati navođenjem oba argumenta, ili ispuštanjem drugog argumenta pri čemu se on zamjenjuje podrazumijevanom vrijednošću: f(3,1); f(3); //ekvivalentni pozivi funkcije f

Podrazumijevana vrijednost se ne smije navesti u definiciji, ako su deklaracija i definicija odvojene argumenti s podrazumijevanom vrijednošću moraju se nalaziti na kraju liste argumenata.

Page 30: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

30

6.9 Neodređeni argumenti Navođenje neodređenog argumenta znači da se funkcija može pozvati sa proizvoljnim brojem argumenata proizvoljnog tipa, uz one koji su eksplicitno navedeni (potpis funkcije se ne provjerava): float f(int a, …){ …}

Pri pozivu funkcije f obavezno je navesti jedan parametar tipa int (ili tipa koji ima konverziju na int), te proizvoljan broj drugih parametara: f(3); f(5,’e’,”C++”,89.3); f(“C++”); //greška – nedostaje argument tipa int

Argumentima funkcije moguće je pristupiti korištenjem funkcija iz standardne stdarg.h datoteke.

6.10 Pokazivači na funkcije Kao argument funkcije moguće je prenijeti i adresu funkcije, odnosno navesti formalni argument kao pokazivač funkcije formalni argument mora sadržavati povratni tip i argument funkcije: povratni_tip *f(tip1, tip2,…)

Primjer: int f1(int, double); int f2(double, int); float f3(int *f(int, double), float b){…} void main(){

f3(f1, 6);//samo ime fun. bez zagrada sinonim je za pok. na nju f3(f2,6); //greška–arg. preneseni fun. ne odgovaraju deklaraciji

}

6.11 Stog i rekurzija Pri svakom pozivu funkcije povratna adresa, lokalni objekti i argumenti spremaju se na sustavski stog. int f(int a){

int b=a-1; return b;

}

Učestali pozivi i prenošenje po vrijednosti glomaznih struktura (velika polja i klase) znatno opterećuju stog i usporavaju izvršavanje programa, te mogu dovesti do iscrpljenja računalnih memorijskih resursa.

Rekurzije zbog svog učestalog pozivanja funkcija nisu dobro rješenje, te ih treba izbjegavati kad god je moguće problem (jednostavno) sekvencijalno riješiti

…povratna adresa (0xFF3A)

lokalna kopija a stvarnog parametra (1)

lokalni objekt b (0)

stog

Page 31: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

31

void main(){

int c=f(1); }

6.12 Pokazivači i reference kao povratne vrijednosti Pokazivač kao povratna vrijednost: tip *f(tip arg1, tip arg2, …){ ...

return adresa; }

Povratna adresa pri tome nikada ne smije pokazivati na automatski lokalni objekt, jer se takvi objekti uništavaju pri izlasku iz funkcije.

Referenca kao povratna vrijednost: tip &f(tip arg1, tip arg2, …){

… return objekt;

}

Ako je povratni tip referenca nije potrebno eksplicitno navoditi da se u pozivajući kod vraća adresa. Ovdje vrijedi ista napomena kao i kod vraćanja pokazivača.

6.13 Globalni i statički objekti Globalni objekti su objekti definirani izvan funkcije (uključujući i funkciju main()), te su vidljivi u svim funkcijama čije definicije slijede: int globalni; int jaGaVidim (int, float); void *jaIsto (double); void main(void){…

Promjene nad globalnim objektima vidljive su u svim funkcijama koje ga koriste globalni objekti narušavaju principe dobrog strukturiranog programiranja. Statički lokalni objekti inicijaliziraju se samo jednom prilikom prevođenja, te se ne uništavaju pri izlasku iz funkcije. Statički se objekti deklariraju ključnom riječi static i tipom: int brojilo(int prviPoziv){

static int brojac=0; //statička varijabla tipa int čija brojac++; //se inicijalizacija se provodi samo jednom

}

Page 32: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

32

7 Ulazni i izlazni tokovi Teme obrađene u ovom dijelu:

• Što su ulazni i izlazni tokovi • Ispis pomoću cout • Kontrola ispisa • Učitavanje pomoću cin • Učitavanje znakovnih nizova • Datoteke i ulazno-izlazni tokovi • Čitanje i pisanje iz/u datoteke (C i C++) • Modovi otvaranja datoteke • dređivanje i postavljanje položaja unutar datoteke

7.1 Što su ulazni i izlazni tokovi Ulazni i izlazni tokovi omogućavaju ulazno-izlaznu komunikaciju programa s vanjskim jedinicama (zaslon, pisač, datoteka).

Jezik C++ ne definira ulazno-izlazne tokove, već se koriste klase definirane u standardnoj biblioteci iostream.h. Pokretanjem svakog C++ programa koji ima uključenu datoteku iostream.h stvaraju se i inicijaliziraju 4 objekta:

cin obrađuje unos sa standardne ulazne jedinice (tipkovnica) cout obrađuje ispis na standardnu izlaznu jedinicu (zaslon) cerr obrađuje ispis na standardnu izlaznu jedinicu za ispis pogrešaka (zaslon) clog obrađuje ispis pogrešaka s međupohranjivanjem

Stanje toka:

eof() vraća true ako je s toka vraćen znak za kraj datoteke bad() vraća true ako operacija nije uspjela zbog nepopravljive greške fail() vraća true ako operacija nije uspješno obavljena zbog bilo kojeg razloga good() vraća true ako ni jedan od gornjih uvjeta nije true

7.2 Ispis pomoću cout Objekt cout usmjerava ispis podataka na zaslon pomoću operatora << koji se naziva operatorom umetanja (insertion operator). S desne strane operatora umetanja se može naći bilo koji tip podataka (char, short, int, long int, char *, float, double, long double, void *): cout<<“C++”<<endl; //endl je znak za kraj reda cout<<3879<<endl; int a=5; cout<<a<<endl;

Moguće je ulančavanje ispisa: cout<<“Iz ovoga cu sigurno imati “<<a<<‘.’<<endl;

7.3 Kontrola ispisa

Page 33: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

33

Funkcijom width određuje se širina ispisa: cout.width(10); //podešena širina traje jedan ispis

Funkcijom fill određuje se znak kojim će se popuniti praznine pri ispisu: cout.fill(‘*’); //postavljeni znak ostaje do sljedeće promjene

Zastavice za formatiranje ispisa omogućuju precizno određivanje izgleda ispisa: cout.setf(ios::zastavica); Zastavica Značenje uppercase zamjena malih slova velikim showbase ispis baze kao prefiksa showpoint ispis dec. točke za realne brojeve showpos forsira ispis predznaka + scientific ispis brojeva u exp notaciji fixed ispis brojeva s fiksnom dec. točkom dec/hex/oct pretvorba u dekadsku/hex/oct bazu internal predznak lijevo, ostatak desno left/right poravnavanje ulijevo/udesno

7.4 Učitavanje pomoću cin Učitavanje se obavlja pomoću objekta cin i ulaznog operatora >> koji se zove operator izlučivanja. S desne strane operatora izlučivanja mogu se naći isti tipovi kao i kod operatora umetanja: int i; double d; cin>>i; cin>>d;

Dozvoljeno je ulančavanje naredbi za učitavanje (redoslijed učitavanja s lijeva na desno): int prvi; double drugi; float treci; cin>>prvi>>drugi>>treci;

Praznina, tabulator i znak za novi red predstavljaju standardne graničnike koji se ignoriraju, a učitavanje u sljedeću varijablu se nastavlja gdje je prethodno učitavanje prekinuto.

7.5 Učitavanje znakovnih nizova Za učitavanje znakovnih nizova mogu se koristiti sljedeće funkcije: get(char &znak);

Učitava jedan znak s ulaznog toka i pohranjuje ga u varijablu znak. Nakon što se učita znak za kraj datoteke tok se postavlja u stanje eofbit. char niz[100]; int i=0; while (cin.get(niz[i])) if(niz[i++]==‘\n’) break; niz[i-1]=‘\0’; get();

Učitava jedan znak s ulaznog toka te vraća kod učitanog znaka. Nakon što učita znak za kraj datoteke vraća vrijednost –1. while (niz[i]=cin.get()) if(niz[i++]==‘\n’) break; niz[i-1]=‘\0’; get(char *znNiz, int duljina, char granicnik=‘\n’);

Page 34: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

34

Učitava niz znakova do najviše duljina-1 znakova ili do nailaska na znak graničnik ili EOF. Učitani niz pohranjuje u znNiz pri čemu na kraj dodaje NULL znak. Ostavlja znak ‘\n’ na toku. cin.get(niz, sizeof(niz));

7.6 Datoteke i ulazno izlazni tokovi Za rad s datotekama programer mora sam stvoriti objekt (ulazni ili izlazni tok) i vezati ga na datoteku. Za rad s datotekama definirane su tri klase:

• ifstream učitavanje podataka iz datoteke,

• ofstream upis podataka u datoteku, i

• fstream omogućuje i upis i čitanje podataka.

Sve tri klase deklarirane su u fstream.h koja sadrži i ostream.h pa ju nije potrebno zasebno navoditi. Svaka operacija s datotekama sastoji se od tri dijela:

• otvaranje datoteke,

• ispis ili učitavanje podataka, i

• zatvaranje datoteke. 7.6.1 Čitanje i pisanje iz/u datoteke Prvo je potrebno inicijalizirati tok otvaranjem datoteke, za što se koristi klasa ifstream (čitanje) odnosno ofstream (pisanje) čiji su konstruktori: ifstream (char *ime_datoteke, openmode mod=in); ofstream (char *ime_datoteke, openmode mod=out);

Otvaranje datoteke može se obaviti pri deklaraciji objekta: ifstream dat(ime_datoteke,ios::mod); ofstream dat(ime_datoteke,ios::mod);

Datoteku je moguće otvoriti i nakon deklaracije objekta: ifstream datUlaz; ofstream datIzlaz; datUlaz.open(ime_datoteke,ios::mod); datIzlaz.open(ime_datoteke,ios::mod);

Nakon obavljenih operacija datoteka se mora zatvoriti funkcijskim članom close(): datUlaz.close(); datIzlaz.close();

7.6.2 Modovi otvaranja datoteke Prilikom otvaranja datoteke moguće je navesti opcije vezane uz njezino postojanje, način čitanja i upisivanja:

Pri otvaranju može se navesti više modova: Mod Značenje app upis na kraj datoteke (append) ate datoteka se otvara i skače se na kraj binary upis i učitavanje u binarnom modu in otvaranje za čitanje nocreate pogreška ako datoteka ne postoji

Page 35: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

35

noreplace pogreška ako datoteka postoji out datoteka se otvara za pisanje trunc sadržaj datoteke se briše prilikom otvaranja ofstream dat(ime_datoteke,ios::mod1|ios::mod1);

datoteku je moguće otvoriti i za čitanje i pisanje korištenjem klase fstream: fstream datUI(ime_datoteke,ios::in|ios::out|ios::mod);

7.6.3 Određivanje i postavljanje položaja unutar datoteke Pokazivač datoteke (file pointer) pokazuje na poziciju u datoteci na kojoj se operacija obavlja, te je jednak udaljenosti pozicije u byteovima od početka datoteke.

U klasi ostream postoje dva funkcijska člana koja dohvaćaju pokazivač datoteke:

• tellp() vraća trenutnu vrijednost pokazivača datoteke, i

• seekp() pomiče se na zadani položaj unutar datoteke.

Analogne funkcije tellg() i seekg() postoje u klasi istream. Ove funkcije deklarirane su kao: ostream &ostream::seekp(streampos &pozicija); istream &istream::seekg(streampos &pozicija); ostream &ostream::seekp(streamoff &pomak, ios::seekdir smjer); istream &istream::seekg(streamoff &pomak, ios::seekdir smjer);

Prve dvije inačice prihvaćaju apsolutnu vrijednost pozicije u oktetima, dok druge dvije prihvaćaju relativnu vrijednost pomaka u oktetima u odnosu na pobrojenje seekdir koje može poprimiti vrijednosti: seekdir vrijednost značenje beg pomak u odnosu na početak datoteke cur pomak u odnosu na trenutnu poziciju u datoteci end pomak u odnosu na kraj datoteke

7.6.4 Rad s datotekama (C) Sučelje prema datoteci pomoću file pointera: FILE *f;

Otvaranje datoteke: f=fopen(ime_datoteke,način_otvaranja);

Načini otvaranja: Oznaka Značenje r čitanje w pisanje, ako datoteka postoji briše se w+ čitanje + pisanje, datoteka se stvara ako ne postoji r+ čitanje + pisanje, datoteka mora postojati a+ čitanje + pisanjena kraj datoteke Pisanje u datoteku int fprintf( FILE *f, const char *format [, argument ]...); int fputs( const char *string, FILE *stream );

• iz datoteke se čita dok se ne naiđe na znak za novi red, kraj datoteke ili se ne pročita n-1 znak

Čitanje iz datoteke

Page 36: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

36

int fscanf( FILE *f, const char *format [, argument ]... ); char *fgets( char *string, int n, FILE *stream );

• ako se pročita znak za novi red, uključuje se u string

Primjer: FILE* f=fopen(“mojaDat.txt”,”w+”); char* str=new char[10];strcpy(str,”c++”); fprintf(f,”%s”,str); fscanf(f,”%s”,str); fclose(f);

Primjer: FILE* f=fopen(“mojaDat.txt”,”w+”); char* str=new char[10];strcpy(str,”c++”); fputs(“abc”,f); fscanf(f,10,str); fclose(f);

7.6.5 Rad s datotekama (C++) Pisanje u datoteku basic_ostream& put(E c); objekt tipa ofstream/fstream << podaci Čitanje iz datoteke basic_istream& get(basic_streambuf<E, T> *sb); basic_istream& get(basic_streambuf<E, T> *sb, E delim); objekt tipa ifstream/fstream >> podaci

Primjer: ifstream datUlaz; datUlaz.open(“dat1.txt”,ios::in); ofstream datIzlaz; datIzlaz.open(“dat2.txt”,ios::out); char c; while(datUlaz.get(c)!=EOF) datIzlaz.put(c); datUlaz.close(); datIzlaz.close();

Primjer: ifstream datUlaz; datUlaz.open(“dat1.txt”,ios::in); char c[50]; datUlaz >> c; cout<<c; datUlaz.close();

7.7 Primjeri zadataka Zadaci u ovom poglavlju odnose se na sva predhodno obrađena područja.

1. Pročitajte niz riječi sa standardnog ulaza. Riječ "kraj" služi kao oznaka za kraj unosa. Nakon što je unesena riječ "kraj" ispišite učitane riječi u redoslijedu u kojem su učitane, i sortirane po abecedi.

Uputa: Koristite funkcije. Koristite globalne varijable gdje je potrebno.

2. Učitajte rečenicu sa standardnog ulaza (rečenica završava s točkom što je oznaka za kraj unosa). Ispišite statistiku rečenice (broj riječi, broj slova/brojeva/praznina/dijakritičkih znakova). Ispišite istu rečenicu ali s obratnim poretkom riječi.

Page 37: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

37

Uputa: Koristite funkcije. Koristite globalne varijable gdje je potrebno.

3. Učitajte sa standardnog niz parova oblika string cijeli_broj, pri čemu je string niz znakova bez praznina. String i cijeli broj razdvojeni su jednom prazninom. Oznaka za kraj unosa je xxx 000. Ispišite broj učitanih parova, srednju vrijednost aritmetičkog ekvivalenta stringa (sume svih znakova), srednju vrijednost numeričkog dijela unosa, te srednju vrijednost cijelog unosa.

Uputa: Koristite funkcije. Koristite globalne varijable gdje je potrebno.

4. Implementirajte funkcije strlen (duljina stringa), strcpy (kopiranje jednog stringa u drugi), strcat (dodavanje jednog stringa drugom), strcmp (usporedba dva stringa), int sum(const char*, const char*) koja vraća zbroj zbroja znakova svakog stringa, te char* rev(const char*) koja kao povratnu vrijednost vraća ulazni string obrnutog poretka. Prototipovi funkcija koji nisu navedeni jednaki su onima iz C++.

Uputa: Koristite funkcije. Koristite globalne varijable gdje je potrebno.

5. Napišite program koji čita ulaznu C++ datoteku te uklanja sve komentare iz nje. Komentari mogu biti jednolinijski (//), ili blok komentari (/* */). Ime datoteke navodi se u komandnoj liniji.

Uputa: Koristite funkcije. Koristite globalne varijable gdje je potrebno.

6. Napišite program koji čita proizvoljan broj ulaznih datoteka, te upisuje njihov sadržaj tih datoteka jedan iza drugog u datoteku koja je zadnja navedena u ulaznoj listi datoteka. Imena datoteka zadaju se iz komandne linije.

Uputa: Koristite funkcije. Koristite globalne varijable gdje je potrebno.

7. Napišite program koji čita ulaznu cpp datoteku i zamjenjuje sve pojave naredbe printf C++ ekvivalentom cout. Novi program pohranjen je u novu datoteku. Imena obje datoteke navode se iz komandne linije. Ulazni program je pisan tako da se u svakom retku nalazi jedna naredba.

Uputa: Koristite funkcije. Koristite globalne varijable gdje je potrebno.

Page 38: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

38

8 Uvod u OOP Razvoj programskih jezika i pristupa programiranju pratio je razvoj složenosti zadataka koji su se postavljali pred programere:

1. strojni kod,

2. assembler (mašinski kod),

3. jezici višeg stupnja (FORTRAN),

4. strukturirano programiranje (PASCAL, C), i

5. objektno orijentirano programiranje (C++).

Svi objektno orijentirani jezici imaju tri zajedničke karakteristike:

1. enkapsulacija (klase i objekti),

2. polimorfizam, i

3. nasljeđivanje.

Page 39: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

39

9 Enkapsulacija Teme obrađene u ovom dijelu:

• Objekti i klase • Definicija klasa • Dodjela prava pristupa • Prijateljske funkcije • Umetnute (inline) funkcije • Deklaracija objekta • Ključna riječ this • Konstruktori • Podrazumijevani konstruktor • Konstruktor s parametrima • Konstruktor kopije • Destruktor • Polja objekata • Statički članovi • Pokazivači na podatkovne članove klase • Pokazivači na funkcijske članove klase • Strukture • Unije

9.1 Objekti i klase C odvaja podatke (varijable, strukture) od operacija koje se na njima primjenjuju (funkcije). Objekt je logički entitet koji ujedinjuje podatke i operacije (metode) nad njima. Povezivanje podataka i operacija u logičku cjelinu naziva se enkapsulacija.

U C++ kodu objekt predstavlja varijablu korisnički definiranog tipa koji se zove klasa. Objekt je dakle konkretna realizacija klase koja definira njegovu strukturu. Klase su zapravo strukture iz programskog jezika C proširene funkcijama.

9.2 Definicija klasa Prije deklariranje objekta potrebno je definirati klasu, odnosno tip objekta: class imeKlase{ private:

privatni podaci i funkcije public:

javni podaci i funkcije protected:

zaštićeni podaci i funkcije } lista_objekata;

Pri definiciji klase postoji mogućnost određivanja prava pristupa pojedinim podacima i funkcijama unutar klase pomoću riječi private, public i protected. Ukoliko se ne navede način pristupa smatra se da se radi o private dijelu. Bilo koji pristup može se ispustiti.

Page 40: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

40

Pri definiciji klase nije potrebno uvijek dati definiciju funkcija, njih je moguće definirati i kasnije uporabom scope resolution operatora: class imeKlase{ pristup: //ukoliko se pristup ispusti radi se o private

povratni_tip imeFunkcije(); } povratni_tip imeKlase::imeFunkcije(){…} //:: je scope resolution operator

Scope resolution operator određuje područje (scope) gdje se funkcija definira, jer se radi o području klase, a ne globalnom području na isti način moguće je pristupiti i bilo kojem podatku unutar klase, no on se ne smije inicijalizirati jer ne postoji konkretna realizacija te klase (objekt): class klasa{

int var; } klasa::var=1; //greška – var ne postoji u memoriji

9.3 Dodjela prava pristupa Ideja klase je objedinjavanje podataka i operacija nad njima radi lakšeg korištenja. public, private i protected određuju prava pristupa pojedinim elementima klase preko konkretne realizacije klase – objekta. Korisnik klase ne mora znati implementaciju klase, već ju koristi preko definiranog javnog sučelja u obliku javnih funkcija i podataka koji se nalaze u public dijelu definicije. U private i protected dijelu se nalazi dio implementacije koji nije javno dostupan preko objekta i predstavlja za korisnika nevažne implementacijske detalje. class student{

int ocjena; public:

void upis(){cin>>ocjena;} void ispis(){cout<<ocjena<<endl;}

};

Ocjena spada u private dio i može joj se pristupati samo metodama definiranim unutar klase (upis() i ispis()) koje predstavljaju javno sučelje. Korisnik klase ne mora znati ime varijable u koju se spremaju ocjene, dovoljno je da zna imena funkcija sučelja.

9.4 Prijateljske funkcije Funkcije koje su članovi klase mogu pristupati bilo kojem podatku unutar klase bez obzira na tip pristupa, funkcije izvan klase (free functions) mogu pristupati samo javnim podacima. Prijateljske funkcije su funkcije definirane izvan klase koje mogu pristupati svim podacima klase: class klasa{

int privateVar; public:

friend int prijatelj(klasa); //prijatelj(klasa) je prijateljska funkcija klasi klasa }; int prijatelj(klasa obj){

return obj.privateVar; //pristup privatnoj varijabli klase }

9.5 Umetnute (inline) funkcije Tijela kratkih funkcija je efikasnije umetnuti pri prevođenju umjesto poziva funkcije. Ključnom riječi inline daje se uputa prevodiocu da poziv zamjeni tijelom funkcije i ima smisla za kratke

Page 41: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

41

funkcije koje se često pozivaju. Svaka funkcija koja je definirana unutar klase automatski se smatra umetnutom, te nije potrebno navoditi riječ inline: class klasa{

int var; public:

int getVar(void){return var;} //getVar je inline funkcija inline float f(void);

};

Umetnutu funkciju je moguće definirati i izvan klase, ali je tada potrebno navesti riječ inline: klasa::inline float f(void){…}

9.6 Deklaracija objekta Objekt se deklarira kao bilo koja druga varijabla u programu: class klasa{

int a; public:

int b; int f(void){return a};

}; void main(void){

klasa objekt; klasa *pokObjekt=new klasa;

…}

Članovima klase pristupa se pomoću operatora pristupa:

1. operator . ako se na objekt referira pomoću naziva objekta ili reference: objekt.b=1; int c=objekt.f(); objekt.a=1; //greška – a je varijabla s private pristupom

2. operator -> ako se na objekt referira pomoću pokazivača: pokObjekt->b=2; int d=pokObjekt->f();

9.7 Ključna riječ this Prilikom poziva svakog funkcijskog člana kao skriveni parametar se prenosi pokazivač na objekt kojem taj član pripada: class klasa{

int a; public:

int f(int b){a=a*b;} }; void main(void){

klasa obj; obj.f(2);

}

Ključna riječ this najčešće se koristi kada treba vratiti pokazivač ili referencu na objekt koji je pozvan. class klasa{

int a; public:

int f(int b, klasa *this) {(this->a)=(this->a)*b;} };

Page 42: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

42

void main(void){ klasa obj; obj.f(2,&obj);

}

9.8 Konstruktori Konstruktor je dio koda koji izvodi inicijalizaciju podataka prilikom deklaracije objekta konstruktor je funkcija čije je ime jednako imenu klase i koja nema povratne vrijednosti jer samo obavlja operacije: class klasa{

int a,b,c; public:

klasa(void); //konstruktor }; klasa::klasa(void):a(1),b(2){ //definicija konstruktora

cout<<“Konstruktor poceo”<<endl; c=a+b; cout<<“Konstruktor zavrsio”<<endl;

}

Prije navođenja tijela konstruktora moguće je navesti inicijalizacijsku listu u obliku: imeKlase::imeKlase(…):var1(vrijednost1),var2(vrijednost2){…}

Konstruktor se automatski poziva pri deklaraciji objekta: klasa objekt; cout<<objekt.a<<endl; //1

9.8.1 Podrazumjevani konstruktor Konstruktor bez parametara se naziva podrazumjevani konstruktor (default constructor). Ako klasa nema konstruktora prevoditelj automatski stvara podrazumjevani konstruktor koji poziva podrazumijevane konstruktore klasa čiji se objekti nalaze unutar promatrane, a njezine podatkovne članove ostavlja neinicijalizirane: class klasa{

int a, b; public:

klasa(void){a=1; b=2;}; }; class klasaBezKonstruktora{

klasa c; int d, e;

}; void main(void){

klasaBezKonstruktora f; //poziva se podrazumjevani konstruktor klasa::klasa(void), dok varijable d i e ostaju neinicijalizirane

}

9.8.2 Konstruktor s parametrima Pri deklaraciji objekta moguće je konstruktoru prenijeti i neke inicijalizacijske parametre: class klasa{

char *polje; public:

klasa(int velicina){ polje=new char[velicina+1];

} };

Page 43: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

43

Vrijednost tih parametara potrebno je navesti pri deklaraciji objekta (u protivnom će se pozvati podrazumjevani konstruktor ili će se takav stvoriti ako ne postoji): klasa objekt(20);

9.8.3 Konstruktor kopije Konstruktor kopije opisuje način inicijalizacije članova objekta kojem se pridružuje drugi objekt: klasa obj1(…); klasa obj2=obj1; //pozivanje konstruktora kopije

Razlikuje se od drugih konstruktora po tome što ima kao argument referencu na konstantan objekt iste klase: imeKlase::imeKlase(const imeKlase &ref){…}

Ukoliko se konstruktor kopije ne definira prevoditelj će ga sam generirati pri čemu se elementi jednog objekta inicijaliziraju vrijednostima elemenata drugog objekta. To može predstavljati problem kod dinamičkih objekata: class klasa{ char *polje; public:

klasa(int d); klasa(const klasa &refKlasa)

}; klasa::klasa(int d):polje(new char[d]){} klasa::klasa(const klasa &refKlasa):polje(new char[sizeof(refKlasa.polje)]){

for(int i=0;i<sizeof(refKlasa.polje);i++) polje[i]=refKlasa.polje[i];

}

9.8.4 Destruktor Destruktor se poziva prilikom uništavanja (uklanjanja) objekta iz memorije ima naziv jednak imenu klase ispred kojeg stoji ~ (tilda). Destruktor ne može imati parametre.

Automatski se poziva kod

• automatskih objekata na kraju bloka u kojem su definirani • statičkih i globalnih objekata nakon izlaska iz funkcije main() • dinamičkih objekata prilikom uništenja sa delete

Destruktor se ne mora definirati za statičke objekte koji ne zauzimaju nikakve dodatne resurse: class obj{ public:

int a; }; klasa obj; //ne treba destruktor jer se automatski uništava pri izlasku iz bloka

Dinamičke objekte potrebno je eksplicitno uništiti sa delete klasa *pokObj=new klasa; … delete pokObj; //ne treba imati definiran destruktor jer se ne alocira nikakva dinamička struktura unutar klase

Ako se unutar klase dinamički rezervira memorijski prostor potrebno ga je eksplicitno osloboditi unutar destruktora jer takvi objekti ne spadaju u automatske:

Page 44: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

44

class klasa{ int *polje;

public: klasa(int d):polje(new int[d]); //dinamička alokacija polja ~klasa(void); //deklaracija destruktora

}; klasa::klasa(void){delete [] polje}; //destruktor uništava dinamički alocirano polje u konstruktoru

Poziv destruktora može biti automatski (izlazak iz bloka) ili eksplicitan (delete) što ovisi o deklaraciji objekta.

9.9 Polja objekata Moguće je definirati polja objekata slično kao i polja ugrađenih tipova: klasa poljeObjekata[duljina_polja];

Prilikom deklaracije polja poziva se podrazumjevani konstruktor ili konstruktor s parametrima ako je navedena inicijalizacijska lista: klasa polje[2]={klasa(…), klasa(…)}

Kod dinamičke alokacije polja mora postojati podrazumjevani konstruktor, jer tu nema mogućnosti navođenja inicijalizacijske liste: klasa *polje=new klasa[5];

Pri oslobađanju polja potrebno je pozvati destruktore za sve članove polja: delete polje; //poziva se destruktor prvog člana – loše rješenje delete [] polje; //pozivaju se destruktori svih članova polja

Pomoću delete [] mogu se osloboditi samo članovi alocirani operatorom new.

9.10 Statički članovi Statički su članovi zajednički svim objektima jedne klase (globalni su za cijelu klasu) i slični su globalnim članovima samo što su vidljivi isključivo u području klase: class klasa{

static int staticki; //deklaracija statičke varijable static int f(void); //deklaracija statičke funkcije

};

Deklaracija statičkog člana služi samo kao najava programu da će u memoriji postojati član zajednički za sve objekte te klase. Stvarno zauzeće memorije odvija se pri inicijalizaciji člana. Statički članovi mogu se dohvatiti bez objekta pomoću imena klase i scope resolution operatora: klasa::staticki=0; //inicijalizacija statičkog člana

Statičkim funkcijama se pristupa slično kao i statičkim podatkovnim članovima: imeKlase::imeFunkcije

Statičkim članovima moguće je pristupiti i preko objekata: klasa objekt; objekt.staticki=0;

9.11 Pokazivači na podatkovne članove klase Moguće je deklarirati pokazivač na član klase kao:

Page 45: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

45

tipčlana imeKlase::*imePokazivača;

Pokazivači na član klase razlikuju se od običnih pokazivača jer ne pokazuju na neku konkretnu memorijsku lokaciju, već samo jednoznačno identificiraju član klase: class klasa{ public:

int a; }; void main(void){

int klasa::*pok1=&klasa::a; //pok1 je pokazivač na član klase klasa obj; int *pok2=&obj.a; //pok2 je pokazivač na memorijsku lokaciju obj.a=1; cout<<obj.a<<endl; cout<<obj.*pok1<<endl; //pok1 je drugo ime za član a

}

9.12 Pokazivači na funkcijske članove klase Osim pokazivača na podatkovne članove moguće je definirati i pokazivač na funkcijske članove klase: povratni_tip (imeKlase::imePokazivača)(tip1, tip2, …)

Pri pridjeljivanju vrijednosti pokazivaču nije potrebno naglasiti da se uzima adresa: class klasa{ public:

int f(int, double); }; … void main(void){

klasa *obj=new klasa; int (klasa::pokF)(int, double)=klasa::f; cout<<obj->pokF(1,1.2);

}

Prilikom pridruživanja vrijednosti pokazivaču provjerava se potpis i pripadnost funkcije – potrebno je podudaranje povratnog tipa, argumenata i klase funkcijskog pokazivača i pridjeljene funkcije.

9.13 Strukture U C++ struktura ima ista svojstva kao i klasa – može sadržavati podatkovne i funkcijske članove. Razlika je u tome što je kod klase podrazumjevani pristup privatni (private), a kod strukture javni (public): struct struktura{

int velicina; void pisi(int nova){velicina=nova;}; int citaj(void){return velicina};

}; void main(void){

struktura objekt; //nije potrebno navoditi struct kao u C-u objekt.pisi(2); int b=objekt.citaj();

}

9.14 Unije

Page 46: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

46

Slične su strukturama i klasama samo što podaci dijele isti memorijski prostor. Veličina unije jednaka je veličini najvećeg podatkovnog člana. Deklaracija ista deklaraciji klase (mogućnost navođenja pravila pristupa, funkcijske članove, podatke, …): union Unija{

long kapacitet; int brojStr;

}; void main(void){

Unija objekt; //nije potrebno navoditi union kao u C-u Unija.kapacitet=12345; cout<<Unija.brojStr; //ispis će ovisiti o načinu pohranjivanja u

memoriji }

Unije se koriste za očuvanje memorijskog prostora ukoliko je potrebno koristiti više atributa različitog tipa, ali pri čemu samo jedan atribut u nekom trenutku ima neko značenje.

Unija može sadržavati objekte klasa pod uvjetom da oni nemaju definiran ni konstruktor ni destruktor. Unija može sadržavati više konstruktora i destruktor, ali ne može sadržavati statičke članove. Ukoliko se prvo pristupa ne navede podrazumijeva se javni (public) pristup.

Za određivanje koji je član unije aktivan često se koristi podatkovni član koji se naziva diskriminantom unije: class Klasa{

enum { kapacitet, stranice} tip; //tip je diskriminanta unije union{

long kapacitet; int brojStr;

} objektUnije; public:

void ispis(void); }; Klasa::ispis(void){

if(tip==kapacitet) cout<<objektUnije.kapacitet<<endl; else cout<<objektUnije.brojStr<<endl;

};

Moguće korištenje anonimne unije kojoj nije dodijeljeno ime za izravan pristup članovima - unije koje nemaju niti naziv unije niti naziv varijable koja se deklarira: class Klasa{

enum { kapacitet, stranice} tip; //tip je diskriminanta unije union{ //anonimna unija

long kapacitet; int brojStr;

};

public: void ispis(void);

}; Klasa::ispis(void){

if(tip==kapacitet) cout<<kapacitet<<endl; else cout<<brojStr<<endl;

};

9.15 Primjeri zadataka 1. Implementirajte jednostruko povezanu listu cijelih brojeva.

Page 47: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

47

Metode: dodavanje elementa na početak/kraj/iza nekog elementa, brisanje zadanog elementa, ispis liste

Naputak: koristite klasu koja implementira atomarni element liste (vrijednost, pokazivač na sljedeći član)

2. Implementirajte dvostruko povezanu sortiranu listu cijelih brojeva.

Metode: dodavanje elementa, brisanje zadanog elementa, ispis liste, kretanje kroz listu (prethodni, sljedeći element)

Naputak: koristite klasu koja implementira atomarni element liste (vrijednost, pokazivač na sljedeći i prethodni član)

3. Implementirajte rep cijelih brojeva organiziran po LIFO principu.

Metode: dolazak elementa, odlazak elementa, ispis repa, kretanje kroz rep (prethodni, sljedeći element)

Naputak: koristite klasu koja implementira atomarni element repa (vrijednost, potrebni pokazivači)

4. Implementirajte stog cijelih brojeva.

Metode: stavljanje elementa (push), skidanje elementa (pop), ispis stoga, kretanje kroz stog (prethodni, sljedeći element)

Naputak: koristite klasu koja implementira atomarni element stoga (vrijednost, potrebni pokazivači)

5. Implementirajte niz cijelih brojeva kojem se dinamički mijenjaju dimenzije (npr. korisnik može pristupiti bilo kojem članu bez deklaracije dužine – polju se automatski povećava dužina da može realizirati zahtjev). Zadatak riješiti uz minimalan broj dealokacija (slijedna struktura)

Metode: pristup članu (getAt(int)), upisivanje vrijednosti člana (putAt(int))

Naputak: Koristite implementaciju pomoću liste. Koristite klasu koja implementira atomarni element liste (vrijednost, potrebni pokazivači).

6. Implementirajte spremnik sustava posluživanja s prioritetima. Ukupno postoji 5 prioriteta paketa (1 do 5), pri čemu je 1 najmanji prioritet paketa. Svaki prioritet ima svoj red čekanja. Svaki paket ima svoj prioritet i jedinstveni ID.

Metode: dolazak novog paketa, odlazak paketa, ispis reda svakog prioriteta, ispis broja paket a u redu svakog prioriteta.

Naputak: Koristite implementaciju pomoću liste. Koristite klasu koja implementira atomarni element liste (vrijednost, potrebni pokazivači).

Page 48: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

48

7. Implementirajte spremnik WDM multipleksera iz kojeg izlazi 16 kanala (valnih duljina). Svaki paket ima svoj jedinstveni identifikacijski broj kao i cjelobrojnu oznaku kanala kojem pripada (1-16). Za svaki kanal se stvara poseban red čekanja organiziran po FIFO načelu.

Metode: dolazak novog paketa, odlazak paketa, ispis reda čekanja svakog kanala, ispis broja paketa u redu čekanja svakog kanala.

Naputak: Koristite implementaciju pomoću liste. Koristite klasu koja implementira atomarni element liste (vrijednost, potrebni pokazivači).

Page 49: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

49

10 Polimorfizam Teme obrađene u ovom dijelu:

• Što je polimorfizam • Preopterećenje funkcija • Korisnički definirane konverzije • Konverzija konstruktorom • Operator konverzije • Preopterećenje operatora • Definicija operatorske funkcije • Operator = • Operator [] • Operator () • Operator -> • Prefiks i postfiks operatori ++ i –– • Operator umetanja <<

10.1 Što je polimorfizam Jedno se ime koristi za više različitih ali međusobno srodnih operacija, tj. za definiranje skupa srodnih operacija. Ovisno o prenesenim parametrima ili ulaznom tipu poziva se specifična realizacija, no korisnik mora znati samo jedinstveno ime i tipove podataka koji se s njime mogu koristiti.

Mehanizam polimorfizma čini

• preopterećenje funkcija (function overloading), i

• preopterećenje operatora (operator overloading).

10.2 Preopterećenje funkcija Jedno ime dijeli više funkcija koje obavljaju sličnu ili istu operaciju nad različitim tipom podataka: int umanjiZaJedan(int); float umanjiZaJedan(float);

Koja će se implementacija funkcije pozvati ovisi o prenesenom parametru: float a=umanjiZaJedan(5.2); //poziv funkcije float manjiZaJedan(float);

Vrlo često se preopterećuju i konstruktori, tako da se uz podrazumjevani navede i konstruktor s parametrima: class vrijeme{

int t; public:

vrijeme(void):t(0); vrijeme(int offset):t(offset);

}; void main(void){

vrijeme obj1; //poziv podrazumijevanog konstruktora

Page 50: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

50

vrijeme obj2(5); //poziv konstruktora s parametrom }

10.3 Korisnički definirane konverzije Ugrađene konverzije su konverzije između nekih ugrađenih parametara (unsigned int->int, int->float, float->double). Korisnik može definirati konverziju između ugrađenih i korisničkih tipova u oba smjera, pri čemu proširuje skup mogućih konverzija na korisnički definirane tipove.

Primjer korisnički definirane konverzije je konverzija char pokazivača u objekt klase string koji sadrži polje znakova i operacije nad njima, te obratno pretvorbu objekta klase string u char pokazivač na prvi član polja znakova.

Struktura klase string: class string{

char *znNiz; public:

string(char *niz=“”); string(const string &ref); ~string(void); char *pok(void) {return znNiz};

};

10.3.1 Konverzija konstruktorom Konstruktor s jednim parametrom može se upotrijebiti za konverziju podatka tipa parametra konstruktora u objekt klase: string::string(char *niz=“”):znNiz(new char[strlen(niz)+1]){

strcpy(znNiz,niz); } string::string(const string &ref):znNiz(new char[strlen(ref.znNiz)+1]){ //konstruktor kopije

strcpy(znNiz, ref.znNiz); } string::~string(void){delete [] znNiz;}

Konverzije konstruktorom se koriste pri inicijalizaciji objekta ili prijenosu podatka u funkciju: void f(znNiz){…} void main(void){

string obj=“C++”; //konverzija pri inicijalizaciji - isto kao i string obj(“C++”);

f(“TKI”); //konverzija prilikom prijenosa parametra

Prilikom ulaska u funkciju stvara se privremeni objekt pomoću konstruktora s jednim parametrom, pri čemu on odgovara stvarnom parametru prenesenom u funkciju.

10.3.2 Operator konverzije Operatorom konverzije definiraju se pravila pretvorbe podatka u neki drugi tip, pri čemu se konverzija obavlja pomoću posebnog funkcijskog člana: operator tip(); //tip se zamjenjuje nazivom tipa u kojeg se obavlja pretvorba

Operator konverzije nema povratni tip i parametre. U klasi string moguće je definirati operator konverzije koji vraća pokazivač na prvi član znakovnog niza ili vraća duljinu znakovnog niza: class string{ …

Page 51: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

51

public: operator char*(){//operator konverzije koju

return znNiz; //vraća pokazivač na prvi član znakovnog niza } operator int(){

return strlen(znNiz); }

};

Pri pozivu funkcije prvo se traži ugrađena konverzija, a zatim korisnički definirana konverzija u odgovarajući tip ili tip na kojem postoji ugrađena konverzija u odgovarajući tip: void f1(int a); void f2(long a); void f3(char *a); void main(void){

string obj(“C++”); f1(obj); //string->int f2(obj); //string->int->long – prvo korisnička pa ugrađena konverzija f3(obj); //string->char*

}

Nisu moguće dvije uzastopne korisničke konverzije (samo korisnička pa ugrađena). Moguće je i direktno navesti tip u koji se obavlja konverzija: void f(int a); void f(char *a); void main(void){

string obj(“C++”); f((int) obj); // ili f(int(obj));

}

10.4 Preopterećenje operatora U C++ jeziku se ne mogu preopteretiti svi operatori:

• operatori koji se ne mogu preopteretiti, i

• operatori koji se mogu preopteretiti.

Nije moguće uvesti novi operator. Svaki operator zadržava broj argumenata po standardnoj C++ sintaksi. Neki operatori (+ - & *) mogu se preopteretiti u obje (unarnoj i binarnoj) inačici.

Operatore je moguće preopteretiti za korisničke tipove, ali ne i za ugrađene tipove sizeof ?: :: .* . delete new ->* -> () [] >>= <<= *= |= &= ^= %= /= -= += || && != == >> << -- ++ >= <= > < = , ! - | & ^ % / * - +

10.4.1 Definicija operatorske funkcije Izraz c=a+b; prevodi se kao poziv operatorske funkcije s dva argumenta: c=operator+(a,b);

Preopterećenje operatorske funkcije se obavlja korištenjem dužeg oblika poziva operatorske funkcije: class kompl{

friend kompl operator+(kompl &a, kompl &b); int re, im;

Page 52: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

52

public: kompl(int a=0, int b=0):re(a),im(b);

}; kompl operator+(kompl &a, kompl &b){

return kompl(a.re+b.re, a.im+b.im); }

Operacija zbrajanja je deklarirana kao prijateljska funkcija da bi se mogla pozivati u globalnom području bez operatora razlučivanja područja i da bi mogla pristupati podacima unutar klase: kompl a(1,2), b(3,4), c; c=a+b; //c.re=1+3, c.im=2+4

Operatorska funkcija može biti definirana kao član klase ili samostalna funkcija. Operatorska funkcija definirana unutar klase ima za jedan manje parametar od operatorske funkcije definirane izvan klase koja obavlja operaciju nad istim brojem podataka: class klasa{ public:

klasa operator-(const klasa &desni); }; klasa operator+(const klasa &lijevi, const klasa &desni); … klasa obj1, obj2; obj1-obj2; //interpretira se kao obj1.operator-(obj2) obj1+obj2; //interpretira se kao operator-(obj1, obj2)

Za operatorske funkcije definirane izvan klase svi operandi se prosljeđuju kao argumenti operatorske funkcije. Za operatorske funkcije definirane unutar klase poziva se funkcijski član objekta lijevo od operatora kojem se prosljeđuje član desno od operatora. Kad je operator definiran kao funkcijski član na lijevi operand se ne primjenjuju korisničke konverzije.

Klasu kompl možemo proširiti operatorom – kao funkcijskim članom: class kompl{ … public:

kompl operator-(const kompl &b){ return kompl(re-b.re, im-b.im);

}; … kompl a,b,c; c=a+b; c=a-b; c=1+b; //na 1 se obavlja korisnička konverzija konstruktorom c=1-b; //na 1 se ne obavlja konverzija : 1.operator-(b) nema smisla

Nije moguće definirati binarni operator koji kao argument nema ni jedan objekt klase jer bi se time promijenilo značenje ugrađenog operatora.

10.4.2 Operator = Operatori = i & automatski su definirani za svaku klasu. Operator = potrebno je definirati unutar klase, ako se želi izbjeći generiranje podrazumijevanog operatora pridruživanja koji pridružuje vrijednosti istovrsnih elemenata jednog objekta drugom.

Podrazumjevani operator pridruživanja nije dobar u svim slučajevima: class string{

… public string& operator=(const string &desno);

}; string& string::operator=(const string &desno){

Page 53: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

53

if(&desno!=this){ delete [] znNiz; znNiz=new char(strlen(desno.znNiz)+1) <

} return *this;

}

Istu funkciju obavljao je i konstruktor kopije klase string. Pri ovakvoj definiciji operatora pridruživanja moguće je obaviti ulančavanje jer je povratni tip referenca: obj a,b; a=b=“C++”; //”C++” se pridjeljuje objektu b, a objekt b objektu a

10.4.3 Operator [] [] Operator [] (operator indeksiranja) koristi se za dohvat članova polja.

Poziv obj[ind] interpretira se kao obj.operator[](ind);

klasu string nadopunjujemo operatorom indeksiranja koji će vratiti član polja znNiz: class string{ … public:

char& operator[](int poz){return znNiz[n]} }; … string obj(“C++”); cout<<obj[1]<<endl; //+

10.4.4 Operator () () Operator za poziv funkcije () može imati proizvoljan broj argumenata.

Poziv obj(arg) se interpretira kao obj.operator()(arg);

U klasi string možemo preopteretiti operator () tako da vraća podniz određen pozicijom prvog člana i duljinom podniza: class string{ … public:

string operator()(int, int); }; string string::operator()(int start, duljina){

char pom[100], *pok1=pom, *pok2=znNiz+start; while(duljina-- && *pok2) *(pok1++)=*(pok2++); return string(pom);

}

10.4.5 Operator -> -> se uvijek preopterećuje kao unarni i mora biti definiran kao funkcijski član. Izraz a->b interpretira se kao

1. pristup članu b ako je a pokazivač na objekt neke klase, ili

2. ako je a obični objekt ili referenca poziva se (a.operator->())->b. class A{ public:

Page 54: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

54

int clan; A(int init):clan(init);

}; class B{

A &refA; B(A &ref) : refA(ref); A *operator ->() {return &refA;}

}; class C{

B &refB; C(B &ref) : refB(ref); B &operator ->() {return refB;}

}; void main(void){

A objA(1); B objB(objA); C objC(objB); cout<<objC->clan<<endl; //1. objC->clan => 2. objB->clan =>

objA->clan =>1

10.4.6 Prefiks i postfiks operatori ++ i -- Prefiks i postfiks oblik operatora ++ i –- razlikuju se u potpisu: unutar klase izvan klase prefix verzija void operator++()

void operator--() void operator++(int) void operator--(int)

postfix verzija void operator++ (klasa &obj) void operator-- (klasa &obj)

void operator++ (klasa &obj, int) void operator-- (klasa &obj, int)

Prilikom poziva postfiks verzije int parametar ima neku podrazumijevanu vrijednost koju određuje prevoditelj, ili je moguće tu vrijednost eksplicitno navesti: obj.operator++(15);

10.4.7 Operator umetanja <<

Operator umetanja (insertion operator) koji se koristi pri ispisu je obični bitovni posmak u lijevo (varijabla << broj mjesta) koji je preopterećen tako da s lijeve strane prihvaća referencu na objekt klase ostream, a s desne strane bilo koji ugrađeni tip (preopterećenje za sve ugrađene tipove): ostream& operator<< (ostream&, const tip&);

Na izlaz se ispisuje objekt tipa tip. Preopterećenje je moguće napraviti za bilo koji izvedeni tip, pri čemu se obično operatorska funkcija navodi kao prijateljska: class klasa{ protected:

int a; public:

friend ostream& operator<< (ostream&, const klasa&); }; ostream& operator<< (ostream& os, const klasa& obj){

os<<obj.a; return os;

};

Potrebno je vratiti referencu na izlazni tok os ako se želi omogućiti ulančavanje. Inače bi pri svakom pozivu operatorske funkcije došlo do stvaranja kopije objekta: klasa obj1, obj2; cout<<obj1<<obj2<<endl; //operatorska funkcija se poziva dva puta

10.5 Primjeri zadataka Implementirajte kvadratnu matricu realnih brojeva proizvoljnih dimenzija.

Page 55: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

55

Metode: preopterećenje operatorskih funkcija:

konstruktor kopije Matrica(double ** ,int brRedaka,int brStupaca) (x,y) dohvat elementa na određenoj poziciji (npr. m(1,2) dohvaća element u 1 retku i 2 stupcu)

[x] ispis cijelog retka x

= pridruzivanje matrice

+ zbrajanje matrica

- oduzimanje matrica

| transponiranje matrice

* množenje matrica

<< ispis matrice na standardni izlaz

Naputak: matricu implementirajte kao 2 dim polje .

2. Implementirajte vektor realnih brojeva proizvoljne dimenzije (duljine).

Metode: preopterećenje operatorskih funkcija:

konstruktor kopije Vektor(double *,int duljina)

[x] dohvat komponente vektora na poziciji x

= pridruzivanje vektora

* skalarno množenje vektora

| vektorski umnožak vektora (samo dimenzije 3)

+ zbrajanje vektora

- (binarni)oduzimanje vektora

- (unarni)vektor suprotnog smjera

<< ispis vektora na standardni izlaz

Naputak: vektor implementirajte kao 1 dim polje.Zadani operatori trebaju raditi i u kombinaciji sa konstantama(pretvorba u vektor odgovarajuće dimenzije)

3. Implementirajte prikaz kompleksnih brojeva. Realni i imaginarni dio su realni brojevi.

Metode: preopterećenje operatorskih funkcija:

= prdruzivanje kompleksnog broja, realne konstante

+ zbrajanje kompleksnih brojeva

- (binarni)oduzimanje kompleksnih brojeva

- (unarni)negativan kompleksni broj

* množenje kompleksnih brojeva

Page 56: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

56

/ dijeljenje kompleksnih brojeva

<< ispis kompleksnog broja u obliku Re+j*Im na standardni izlaz

Naputak: Za implementaciju prikaza kompleksnih brojeva koristite klase. Zadani operatori moraju raditi i u kombinaciji sa konstantama.

4. Implementirajte polje za prikaz brojeva u heksadekadskom sustavu.

Metode: preopterećenje operatorskih funkcija:

= pridruzivanje polja, heksadekadskog broja

[] dohvat znamenke na zadanoj poziciji (indeksiranje kao u poljima)

+ zbrajanje brojeva

- (binarni) oduzimanje brojeva

- (unarni) negativan broj

* množenje brojeva

<< ispis broja na standardni izlaz

Naputak: Za implementaciju prikaza brojeva koristite klase. Riješite zadatak primjenom polja znakova (char) zbog prikaza znamenaka 10-15 (A-F).

5. Implementirajte prikaz stringa (niza znakova).

Metode: preopterećenje operatorskih funkcija:

= pridruzivanje objekta, konstantnog znakovnog niza(const char *)

[] dohvat znaka na zadanoj poziciji (indeksiranje kao u poljima)

+ konkatenacija (spajanje) stringova (ana+na+as=ananas)

- presjek stringova (ostaju samo zajednički znakovi ZAGREB-FER=RE)

* shuffle dva stringa (C++ * FER=CF+E+R)

<< ispis stringa na standardni izlaz

Naputak: Za implementaciju prikaza stringa koristite klase. Implementaciju provedite korištenjem niza znakova (char). Operatorske funkcije moraju raditi i u kombinaciji konstante (znaka ('.') ili stringa ("...")) i objekta u bilo kojem poretku.

6. Implementirajte jednostruko povezanu nesortiranu listu cijelih brojeva.

Metode: preopterećenje operatorskih funkcija:

konstruktor Lista(int * polje,int duljina)

= pridruzivanje objekta

[] element na zadanoj poziciji (indeksiranje kao u poljima)

Page 57: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

57

+ konkatenacija (spajanje) dvije liste (A+B=AB)

- presjek lista (ostaju samo zajednički elementi, npr. A->B->C->D->E – R->E->F->A=A->E)

<< ispis liste

Naputak: Za implementaciju prikaza liste koristite klase.

7. Implementirajte prikaz kompleksnih brojeva u trigonometrijskom obliku

Metode: preopterećenje operatorskih funkcija:

= pridruzivanje objekta, realne konstante

+ zbrajanje kompleksnih brojeva

- (binarni) oduzimanje kompleksnih brojeva

- (unarni) negativan kompleksni broj

* množenje kompleksnih brojeva

/ dijeljenje kompleksnih brojeva

<< ispis kompleksnog broja u obliku (|Z| | kut) na standardni izlaz

Naputak: Za implementaciju prikaza kompleksnih brojeva koristite klase. Zadani operatori moraju raditi i u kombinaciji sa konstantama.

Page 58: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

58

11 Nasljeđivanje Teme obrađene u ovom dijelu:

• Što je nasljeđivanje • Zadatak • Sveoptička mreža s WDM • Optičke komponente • Prijenos znanja u C++ • Odnosi među komponentama (taksonomija) • Bazna klasa • Specificiranje nasljeđivanja • Nasljeđivanje i prava pristupa • Nasljeđivanje i relacije • Višestruko nasljeđivanje • Virtualna osnovna klasa • Karakteristike virtualnog nasljeđivanja • Pristup članovima izvedenih klasa • Ugrađene pretvorbe i nasljeđivanje • Konstruktori i destruktori izvedenih klasa • Područje pretraživanja • Ugniježđeni tipovi • Klase kao argumenti funkcija (ugrađene konverzije) • Klase kao argumenti funkcija (korisničke konverzije) • Nasljeđivanje i preopterećeni operatori • Polimorfizam i tipovi vezivanja • Pozivi virtualnih funkcijskih članova (nastavak) • Čiste virtualne funkcije • Virtualni destruktori • Polimorfizam i proširivost koda

11.1 Što je nasljeđivanje? Svaki objektno orijentirani jezik mora imati implementiran mehanizam nasljeđivanja. U dobrom definiranju pojma i mehanizama nasljeđivanja leži temelj razumijevanja ideje objektno orijentiranog programiranja. Zbog kompleksnosti i apstraktnosti koju uvodi sustav nasljeđivanja, u njegov opis krećemo postavljanjem konkretnog problema u kojem će biti vidljivi i neki koraci objektno orijentiranog dizajna (OOD).

11.2 Zadatak Zadatak

Simulator sveoptičke mreže s valnim multipleksom. Rješenje

Page 59: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

59

Očito je da rješenje ovog problema neće biti izloženo u cjelini zbog ekstenzivnosti I nedefiniranosti problema. No neki temeljni koraci se ipak mogu provesti bez obzira na općenitost zadatka.

Tekstu zadatka treba pristupiti analitički, tj. probati (korektno) izvući što više informacija. Pri tome imamo dva ključna pojma:

1. simulator – potrebno je modelirati pojave s nekim ulaznim veličinama u svrhu analize njihovog izlaza, i

2. sveoptička mreža s valnim multipleksom – predmet simulacije će biti sveoptička mreža s valnim multipleksom (WDM).

Jasno je da iako imamo neku intuitivnu i iskustvenu sliku o simulatoru nije moguće krenuti u njegovo projektiranje bez detaljnije specifikacije zahtjeva na njegove funkcije. No neke ključne stvari vezane uz simulatore će sigurno biti potrebne i u ovom slučaju. Svaka mreža, pa tako i sveoptička s WDM se sastoji od čvorova i linkova. U čvorovima se nalaze neki fizički uređaji, a linkove u ovom slučaju čine optički vodovi. Mogli bi dakle specificirati tipove podataka koji će sadržavati parametre fizičkih elemenata koji će sačinjavati mrežu, kao i metode kojima ćemo se služiti pri njihovoj uporabi.

11.2.2 Sveoptička mreža s WDM Očito je da drugi korak ne možemo učiniti bez znanja o sveoptičkoj mreži s WDM, tj. o optičkim komponentama koje bi željeli opisati. Probajmo prvo definirati sveoptičku mrežu:

Sve-optička mreža je telekomunikacijska transportna mreža u kojoj se prijenos informacije izvodi potpuno u optičkoj domeni. Signal koji prenosi informaciju je električni samo u pristupnim točkama takove mreže, tj. optičkim prijemnicima i predajnicima.

Zatim odredimo što je to sveoptička mreža s valnim multipleksom:

Specijalan slučaj sve-optičke mreže je mreža s valnim multipleksom (engl. Wavelength Division Multiplex, WDM, ili Dense WDM, DWDM). U takvoj se mreži potencijalno ogroman kapacitet vlakna iskorištava frekvencijskim multipleksom, tj. više se signala s informacijama kroz vlakno prenosi na različitim frekvencijama. S obzirom da se u optici umjesto frekvencije češće koristi pojam valne duljine, taj je način optičkog prijenosa nazvana valnim multipleksom, između ostalog i da se naglasi optički karakter multipleksa.

Pojam sveoptičke mreže sada je jasniji, no još uvijek ostaju nedefinirani elementi takve mreže iako naslućujemo da se radi o prijamnicima, predajnicima, multiplekserima, optičkim vlaknima…

11.2.3 Optičke komponente Probajmo sada popisati komponente uz kratak opis njihove funkcije koji će biti nužan za definiranje atributa tih komponenata:

• predajnik. Pretvara signal iz električne domene u optičku. Postoji mnoštvo vrsta optičkih predajnika, no u sve-optičkim mrežama se koriste isključivo laserski predajnici i to većim udjelom poluvodički, odnosno u manjoj mjeri plinski. Dakle, radi se o komponenti koja ima jedan električki ulaz, te jedan optički izlaz.

Page 60: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

60

• prijemnik. Pretvara optički signal u električni. Kao i u slučaju predajnika, možemo reći da se radi o tzv. opto-električnoj komponenti. Sadrži jedan optički ulaz, te jedan električki izlaz.

• transponder. U današnjim sve-optičkim mrežama s valnim multipleksom se isključivo koristi 3. optički prozor za prijenos informacija (valne duljine oko 1500nm), no često je signal koji ulazi u sve-optičku mrežu već u optičkom obliku, no nepogodan za direktan prijenos preko sve-optičke mreže. Primjerice, može se raditi o optičkom signalu na valnoj duljini od 1300nm (2. optički prozor). S druge pak strane, problem može ležati I u formatu (primjerice linijskom kodu) takvog optičkog signala. Upravo je prilagodba zadatak transpondera. Dakle, radi se o komponenti koja ima jedan optički ulaz i jedan optički izlaz, no isto tako valja imati na umu da se u transponderu ulazni signal pretvara prvo u električnu domenu, te nakon prilagodbe ponovno pretvara u optičku domenu.

• multiplekser. Zadatak je multipleksera da više ulaznih signala s različitih vlakana, te na različitim valnim duljinama, utisne u jedno optičko vlakno. Sadrži više optičkih ulaza, te jedan optički izlaz.

• demultiplekser. Komponenta funkcije komplementarne onoj u multipleksera. Jedan optički ulaz, te više optičkih izlaza. pojačalo. Pojačava optički signal. Jedan optički ulaz i jedan optički izlaz.

• pretvarač valnih duljina. Valna duljina nosioca signala se pretvara u drugu. Jedan optički ulaz i jedan optički izlaz.

• komutator (prospojnik). Izvodi prostorno prospajanje signala. Sadrži više optičkih ulaza i izlaza, a signal se prespaja s određenog optičkog ulaza (vlakna) na određeni optički izlaz (vlakno). vlakno.

Ovim popisom je samo utvrđena funkcija pojedinog elementa unutar sveoptičke mreže. Iako se odnosi među komponentama mogu naslutiti oni nisu formalno definirani. Taj korak slijedi iz znanja, tj. poznavanja njihove funkcije.

11.3 Prijenos znanja u C++ Nakon što je usvojeno sve potrebno znanje, potrebno ga je izraziti u C++ jeziku koristeći objektni pristup koji se sastoji (u ovom našem stupnju razmatranja) od tri dijela:

1. odnosi između komponenata,

2. atributi komponenata, i

3. metode (funkcije) komponenata.

Modeliranje elemenata ovakve mreže u značajnoj mjeri ovisi o potrebnoj razini apstrakcije, a koja opet ovisi o zadatku koja naša aplikacija mora obaviti. Ukoliko je naš cilj dizajn mreže, razina apstrakcije će biti najviša (ili najniža, ovisno o pogledu). U tom slučaju će količina informacije koje objekti moraju sadržavati u velikoj mjeri biti reducirana. Primjerice, svakoj komponenti je potrebno pridružiti cijenu, možda raspoloživost, odnosno komplementarnu vrijednost kvara ili sl. U slučaju da nam je cilj simulacija takve mreže, razina apstrakcije je minimalna.

Page 61: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

61

Svakoj komponenti je potrebno pridružiti mnoštvo atributa, koji će opisivati fizikalno ponašanje odgovarajućeg objekta. Primjeri atributa su gustoće slobodnih nosioca u određenim energetskim stanjima, faktori refleksivnosti, fizičke dimenzije poluvodičkih komponenata i sl.

11.4 Odnosi među komponentama (taksonomija) Najjednostavnije (gledajući po vremenu koje bi utrošili na razmišljanje) bi bilo napraviti onoliko klasa koliko ima komponenata i u njih poslagati potrebne atribute i metode. Takav pristup bi imali u ne-objektnim jezicima kao što je C.

Bolje rješenje je korištenje inherentnih prirodnih svojstava fizičkih komponenata. One naime nisu u potpunosti nezavisne, štoviše sve se mogu svesti na jednu ili više istih općenitijih pojmova. Kažemo da se sve mogu naslijediti od klase koja je semantički (sadržajno) na višoj razini apstrakcije.

Ovakav pristup je bolji jer se prirodni odnosi direktno preslikavaju u programski jezik olakšavajući razvoj složenijih programa, odnosno dodavanje elemenata koji nisu možda u prvom koraku bili važni, ili su se pojavili tijekom vremena. Novi tipovi multipleksera će sigurno imati ulaze i izlaze, kao i pouzdanost i cijenu. Te parametre je koristeći objektni pristup lako naslijediti od već definiranih klasa, a nove atributi se moraju samo dodati postojećima. Time se eliminira potreba za ponovnim pisanjem svih atributa i metoda koje možda već postoje.

11.5 Bazna klasa Dizajn stabla nasljeđivanja obično se započinje definiranjem najopćenitijeg pojma (klase) koja sadrži atribute i metode koji će biti zajednički svim klasama koje iz nje želimo naslijediti. Najčešće se radi o klasi koja nedovoljno definira bilo koji stvarni element (u našem slučaju komponentu u mreži), pa niti nema smisla stvarati objekte takve klase. U ovom slučaju bi najviši stupanj apstrakcije ako se ograničimo na fizikalne elemente mreže bio element. Definirajmo stoga klasu Element s atributima koji će biti zajednički za sve ostale elemente mreže: class Element{ private:

long Cijena; //cijena elementa double VjKvara; //vjerojatnost kvara elementa

public: void setCijena(long unos){Cijena=unos;} long getCijena() {return Cijena;} void setVjKvara(double unos){VjKvara=unos;} double getVjKvara(){return VjKvara;} Element(void) : Cijena(0), VjKvara(1){} Element(long cj, double vj) : Cijena(cj), VjKvara(vj){}

};

Očito je da su cijena i vrijeme kvara nezaobilazni atributi svakog elementa.

11.6 Specificiranje nasljeđivanja Svaki element mreže se može svrstati u jednu od dvije skupine:

1. optički element, i

2. elektronički element.

Definirajmo stoga dvije nove klase koje će naslijediti baznu klasu i njezine atribute.

Općenito se nasljeđivanje izvodi na sljedeći način:

Page 62: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

62

class imeIzvedeneKlase : pravo_pristupa imeOsnovneKlase;

Pravo pristupa određuje način preslikavanja prava pristupa atributima i metodama bazne klase.

Iz bazne klase nasljeđujemo dvije klase: class Oelement : public Element{ //optički element protected:

int BrOUlaza; //0 do beskonačno int BrOIzlaza; //0 do beskonačno

public: void setBrOUlaza(int ulaz){BrOUlaza=ulaz;} void setBrOIzlaza(int izlaz){BrOIzlaza=izlaz;}

}; class Eelement : public Element{ //elektronički element protected:

int BrEUlaza; //0 do beskonačno int BrEIzlaza; //0 do beskonačno

public: void setBrEUlaza(int ulaz){BrEUlaza=ulaz;} void setBrEIzlaza(int izlaz){BrEIzlaza=izlaz;}

};

11.7 Nasljeđivanje i prava pristupa Pravo pristupa određuje način preslikavanja prava pristupa atributima i metodama bazne klase po sljedećem pravilu: klase Oelement i Eelement javno (public) nasljeđuju baznu klasu. To znači da će sve javne metode (Set…, Get…) biti dio javnog sučelja objekata ovih klasa, tj. moći će im se pristupiti iz programa: Oelement oe; oe.setCijena(1000);

Svi privatni atributi i metode osnove klase (Cijena, VjKvara) ostaju nedostupne i preko objekta: izvedene klase i unutar klase: oe.Cijena=1000; //greška class Oelement : public Element{ … public:

void postavi(long ulaz){cijena=ulaz;} //greška – iako je cijena dio klase Oelement, ona nije izravno vidljiva unutar klase }

Pravo pristupa u osnovnoj klasi Tip nasljeđivanja public protected private

public public protected private

protected protected protected private

private private private private

11.8 Nasljeđivanje i relacije Prilikom nasljeđivanja služili smo se relacijom biti između klase Element i Eelement, koristeći činjenicu da je svaki elektronički element ujedno i element u općenitom (generičkom) smislu. Osim relacije biti, moguća je i relacija sadrži koja može donijeti probleme ukoliko se realizira relacijom biti. Primjer takve pogreške bi bilo nasljeđivanje tipa: class Tranzistor{ private:

long cijena;

Page 63: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

63

double VjKvara; }; class Eelement : public Tranzistor{…}

Ovakva implementacija dovodi kasnije do velikih problema jer nije zadovoljen uvjet stroge hijerarhije. Naime tranzistor nije semantički viši pojam od elektroničkog elementa (kao dijela mreže), već je jedan od gradivnih dijelova elektroničkog elementa: class Eelement {

Tranzistor skupTran[100]; //skup tranzistora u (jako) pojednostavljenom Eelementu … };

Treći mogući odnos je koristiti, kada neka apstrakcija (klasa) koristi drugu apstrakciju (klasu), ali ju ne posjeduje.

11.9 Višestruko nasljeđivanje Definirajmo sada dvije nove klase – Opojacalo koja nasljeđuje Oelement i Oprijemnik koja nasljeđuje i Oelement i Eelement, jer se radi o komponenti koja je istovremeno i optička i elektronička : class Opojacalo : public Oelement{ private:

double Pojacanje; double SirinaSpektra;

public: void setPojacanje(double unos){pojacanje=unos;} void setSS(double unos){SirinaSpektra=unos;}

}; class Oprijemnik : public Oelement, Eelement{ private:

double Osjetljivost; public:

Oprijemnik(double unos) : Osjetljivost(unos) {} void setOsjetljivost(double unos){Osjetljivost=unos;}

};

Preko objekata klase Oprijemnik moguće je javnim pristupiti metodama klasa Oelement i Eelement: class Oprijemnik op; op.setBrOUlaza(10); op.setBrEIzlaza(15);

Pogledajmo u grafičkom obliku kako izgleda “genealogija” našeg nasljeđivanja:

Slika 9 Genealogija nasljeđivanja

Page 64: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

64

Sada je jasno da klasa Oprijemnik sadrži dvije klase Element. Iako je ovaj način formalno prihvatljiv i neće se prijaviti pogreška prilikom prevođenja, problemi će nastati ako se pokuša pristupiti bilo kojoj javnoj metodi ili atributu klase koja je dva ili više puta naslijeđena na istu izvedenu klasu: class Oprijemnik op; op.setCijena(100);

Nastaje dvosmislenost (ambiguity) jer nije jasno kojoj se metodi pristupa (iako su one iste). Višestruko nasljeđivanje iste klase treba izbjegavati i zbog činjenice da u tom slučaju nisu podržane ugrađene konverzije.

11.10 Virtualna osnovna klasa Virtualna osnovna klasa rješava spomenuti problem dvosmislenosti kada se jedna klasa uključi više puta u izvedenu klasu. Osnovna klasa se pretvara u virtualnu navođenjem riječi virtual pri nasljeđivanju: class Oelement : virtual public Element{…} class Eelement : virtual public Element{…} class Oprijemnik : public Oelement, public Eelement{…}

Ovim nasljeđivanjem se dobiva struktura prikazana na slici. Osnovna klasa Element sadržana je samo jednom u klasi Oprijemnik. Kako unutar objekta izvedene klase postoji samo jedna osnovna virtualna klasa, njezinim se članovima može pristupati bez opasnosti od nejasnoća. Virtualne osnovne klase se inicijaliziraju konstruktorom u najdalje izvedenoj klasi. Svi konstruktori pozvani prije toga se zanemaruju.

Slika 10 Genealogija nasljeđivanja uz primjenu virtualnog nasljeđivanja

11.11 Karakteristike virtualnog nasljeđivanja Mehanizam virtualnog nasljeđivanja se ostvaruje pomoću pokazivača. Dijelovi izvedenog objekta koji se odnose na elemente osnovne klase ne sadrže te elemente već pokazivače na memorijski prostor koji je zajednički svim podobjektima koji su virtualno naslijedili tu klasu. Problemi ovakvog mehanizma nastaju kada se virtualno nasljeđuje ista klasa s različitim pravom pristupa: class Oelement : virtual private Element{…} class Eelement : virtual public Element{…} class Oprijemnik : public Oelement, public Eelement{…}

Page 65: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

65

Razrješavanje prava pristupa elementima klase Element se obavlja po principu prevladavanja javnog puta do nekog člana: Oprijemnik op; op.setCijena(1000); //prevladava javni pristup

Drugo pravilo je pravilo dominacije koje kaže da uvijek prevladava član koji je definiran u hijerarhijski bližoj klasi: class Oelement : virtual public Element{…} class Eelement : virtual public Element{ public:

void setCijena(long ulaz){cout<<“Ne mogu!”<<endl;} } class Oprijemnik : public Oelement, public Eelement{…} class Oprijemnik op; op.setCijena(1000); //ispis “Ne mogu!” jer definicija funkcije setCijena u Eelement prekriva definiciju u Element koja se također nezavisno nasljeđuje preko Oelement

11.12 Pristup članovima izvedenih klasa Članovima izvedenih klasa se preko njihovog objekta pristupa kao i članovima osnovnih klasa: Opojacalo op; op.setPojacanje(1000.1); //pristup metodi definiranoj u izvedenoj klasi op.setBrOUlaza(5); //pristup metodi osnovne klase

Pristup metodama osnovne klase se može ostvariti i preko scope resolution operatora: op.Oelement::setBrOUlaza(5); //pristup metodi osnovne klase

Nije potrebno eksplicitno navoditi područje definicije pozvane metode ili atributa osim ako u osnovnim klasama postoji dva ili više članova istog imena (tako se ipak ne može izbjeći virtualno nasljeđivanje) ili ako je u izvedenoj klasi definirana metoda ili atribut istog imena koji uvijek prekriva onaj iz osnovne klase: class Opojacalo : public Oelement{ public:

void setBrOUlaza(ulaz){cout<<“Greška!”<<endl;} … }; Opojacalo op; op.setBrOUlaza(100); // ”Greška!” op.Oelement:setBrOUlaza(100); //ispravno postavljanje u osnovnoj klasi

11.13 Ugrađene pretvorbe i nasljeđivanje Jedno od najsnažnijih svojstava koje omogućuje veliku fleksibilnost koda su ugrađene pretvorbe koje se mogu sažeti u 4 pravila:

1. objekt izvedene klase može se implicitno pretvoriti u objekt javne osnovne klase,

2. referenca na objekt izvedene klase može se implicitno pretvoriti u referencu na objekt javne osnovne klase,

3. pokazivač na objekt izvedene klase može se implicitno pretvoriti u pokazivač na objekt javne osnovne klase. Pokazivač na objekt izvedene klase može se implicitno pretvoriti na pokazivač void* i obratno ali uz eksplicitnu pretvorbu, te

4. pokazivač na član izvedene klase može se implicitno pretvoriti u pokazivač na član javne osnovne klase.

Page 66: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

66

Navedene pretvorbe su jasne ako uzmemo u obzir na svaki izvedeni objekt sadrži podobjekt osnovne klase. Ako se radi o pokazivačima dolazi jednostavno do pomaka pokazivanja: Opojacalo *op = new Opojacalo; Oelement *oe=op;

Prilikom pridruživanja oe=op provodi se implicitna konverzija tako da oe pokazuje na memorijski prostor koji pripada klasi Oelement. Očito je da obratna konverzija mora biti eksplicitna, jer osnovni objekt ne mora sadržavati elemente izvedenog i može doći do pristupa nerezerviranom memorijskom prostoru.

Pojacanje SirinaSpektra

dio klase Opojacalo

BrojOulaza BrojOizlaza

dio klase Oelement

cijena vjKvara

dio klase Element

11.14 Konstruktori i destruktori izvedenih klasa Konstruktor izvedene klase zadužen je za inicijalizaciju elemenata definiranih u izvedenoj klasi, dok elemente osnovne klase inicijalizira konstruktor osnovne klase. Ukoliko nije drugačije naznačeno poziva se podrazumjevani konstruktor osnovne klase. Konstruktor osnovne klase se može i eksplicitno pozvati u konstruktoru izvedene klase ukoliko mu je potrebno prenijeti inicijalizacijsku vrijednost: class Oelement : public Element{ … public:

Oelement (int brUl, int brIz, long cj, double vj) : brOUlaza(brUl), brOIzlaza(brIz), Element(cj, vj);

};

Ukoliko se izostavi definicija konstruktora izvedene klase, automatski se definira podrazumjevani konstruktor koji poziva podrazumijevane konstruktore osnovne(ih) klase(a) koji u tom slučaju mora(ju) postojati.

Redoslijed poziva konstruktora:

1. konstruktori osnovnih klasa pozivaju se prema redoslijedu iz liste izvođenja klasa,

2. svaki se konstruktor izvodi u redoslijedu navođenja objekata (a ne inicijalizacijske liste), te

3. na kraju se izvodi konstruktor izvedene klase.

Ovakav redoslijed pozivanja konstruktora uvijek vrijedi bez obzira na prevodioc. Prilikom uništavanja objekata pozivaju se destruktori izvedene klase koji uništavaju samo dio definiran u izvedenoj klasi, te destruktori osnovnih klasa koji uništavaju dijelove koji pripadaju osnovnim klasama. Poziv destruktora obratan je pozivima konstruktora.

11.15 Područje pretraživanja Kod nasljeđivanja dolazi do ugnježđivanja područja deklaracija. Prilikom poziva metode ili pristupa varijabli, prvo se pretražuje područje klase kojoj objekt pripada (Opojacalo), a zatim

Page 67: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

67

nadređenih klasa (Oelement i zatim Element). Treba zato biti oprezan priliko redefinicije funkcijskog člana. Svaka redefinicija prekriva definiciju u nadređenoj klasi, pa čak i ako je samo korišten isti identifikator funkcije, a različiti potpisi. To je posljedica činjenice da nasljeđivanje nije vrsta preopterećenja: class Opojacalo : public Oelement{ … public:

void setBrOUlaza(void){cout<<“Greška!”<<endl;} }; Opojacalo op; op.setBrOUlaza(100); //greška – funkcija nadređene klase prekrivena je funkcijom istog imena, ali različitog potpisa

Obje funkcije mogu biti uključene ponavljanjem definicije funkcije istog potpisa kao u osnovnoj klasi: class Opojacalo : public Oelement{ public:

void setBrOUlaza(int ulaz){Oelement::setBrOUlaza(ulaz);} void setBrOUlaza(void){cout<<“Greška!”<<endl;}

};

11.16 Ugniježđeni tipovi Ugniježđeni tipovi koriste se za realizaciju relacije biti dio od i nemaju veze s nasljeđivanjem, ali se ovdje navode zbog potpunosti. Za ugniježđene tipove vrijede sva pravila nasljeđivanja i pristupa: class Oprijemnik : public Oelement, public Eelement{ … protected:

class Stabilizator { double Faktor;

public: double getFaktor(void);

};

Za pristup elementima i metodama potrebno je koristiti scope resolution operator: Oprijemnik::Stabilizator::getFaktor(void){return Faktor;}

Ugniježđena klasa se može naslijediti: class IzvStab : public Oprijemnik::Stabilizator{…}

11.17 Klase kao argumenti funkcija (ugrađene konverzije) Principi implicitne ugrađene pretvorbe najviše dolaze do izražaja prilikom prijenosa objekata kao argumenta funkcije. Do konverzije ne dolazi ako se tipovi objekata (stvarni i formalni parametri) u potpunosti podudaraju ili ako se radi o trivijalnoj konverziji objekt ->referenca na objekt:

Element

Oelement

Opojacal

redoslijed pretraživanjapodručja

Page 68: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

68

void f(Opojacalo &opFormalni); Opojacalo opStvarni; f(opStvarni); //trivijalna konverzija

Ukoliko tipovi parametara ne odgovaraju provodi se ugrađena konverzija i to na hijerarhijski najbliži tip: void f(Element el); void f(Oelement el); void f(Eelement el); void f(void *el); … Opojacalo opo; Oprijemnik opr; f(opo); //konverzija na Oelement f(opr); //greška – nejasnoća oko poziva konverzije f((Eelement) opr); //eksplicitna konverzija na Eelement

void * se pri tom smatra najudaljenijim članom u hijerarhiji.

11.17.1 Klase kao argumenti funkcija (korisničke konverzije) Pri prijenosu parametara moguće je obaviti i korisničku konverziju konstruktorom ako se ne pronađe preopterećena funkcija s argumentom na kojeg je moguća ugrađena konverzija. Pri pozivu korisničke konverzije se (ako je to potrebno) može pozvati ugrađena konverzija tipa, ali samo jednom. Uzastopni poziv dvije korisničke konverzije nije moguć: void f2(Oelement el); float a=10; f2(10);

U gornjem pozivu se provodi ugrađena konverzija float -> double, pa double -> ugrađena konverzija preko konstruktora -> objekt Oelement.

11.18 Nasljeđivanje i preopterećeni operatori Preopterećeni operatori su zapravo obični funkcijski članovi i za njih vrijede ista pravila kao i za ostale funkcijske članove: class Element{ … public:

int operator==(double a); }; class Oelement : public Element{ … public:

int operator==(int a); }; Oelement oe; int s=(oe==3.4); //greška

Operator pridruživanja = se ne nasljeđuje, jer je potrebno inicijalizirati cijeli objekt, a ne samo dio koji pripada osnovnoj klasi. Inicijalizacija podrazumijevanim operatorom pridruživanja (kopiranje bit po bit) moći će se provesti ukoliko postoji korisnička pretvorba pridružene vrijednosti u tip objekta konstruktori su također isključeni iz nasljeđivanja, pa će gore spomenute konverzije konstruktorom biti moguće samo uz eksplicitno definiranje konstruktora s jednim parametrom u izvedenoj klasi.

11.19 Polimorfizam i tipovi vezivanja

Page 69: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

69

Polimorfizam je uz enkapsulaciju i nasljeđivanje osnovna karakteristika objektno orijentiranog jezika, a omogućuje definiranje operacija koje su ovisne o tipu. Mehanizmi ugrađenih konverzija omogućuju da se svakom objektu izvedene klase pristupi preko objekta osnovne klase proširimo osnovnu klasu Element metodom Aktiviraj(): class Element{ … public:

void Aktiviraj(void) {} };

Metoda Aktiviraj() ne radi ništa u osnovnoj klasi jer njezina implementacija ovisi o konkretnom elementu i ne može se općenito definirati: class Oelement : public Element{ … public:

void Aktiviraj(void) {implementacija} };

Ugrađenim konverzijama moguće su sljedeće pretvorbe: Oelement *oe=new Oelement; Element *e=oe; oe->Aktiviraj(); //poziva konkretnu implementaciju iz Oelement e->Aktiviraj(); //poziva implementaciju iz Oelement koja ne radi ništa

Vidljivo je da na ovaj način nismo postigli očekivan rezultat, a to je da se preko pokazivača tipa osnovne klase, a koji pokazuje na objekt izvedene klase ipak može pozvati metoda izvedene, a ne osnovne klase. Pokazivač na osnovni objekt je samo način promatranja na neki konkretni izvedeni objekt i on ne mijenja objekt na koji pokazuje u programskom smislu, niti mijenja smisao objekta na koji pokazuje u semantičkom smislu.

Svaki optički element možemo nazvati elementom (mreže) bez da promijenimo smisao, a time i metode nad optičkim elementom. Izložena potreba predstavlja smisao polimorfizma. Polimorfizam se ne može ostvariti na prethodni način, jer se radi o statičkom ili ranom povezivanju (static binding). Kod statičkog povezivanja prevodiocu je dostupan jedino tip pokazivača pomoću kojeg se poziva objekt (Element), a ne i tip na koji taj pokazivač pokazuje (Oelement). Pozvana metoda će stoga biti pozvana iz klase koja odgovara tipu pokazivača.

Rješenje problema leži u promjeni pogleda na način izvođenja metode – umjesto da se metoda određuje prilikom povezivanja (što je moguće preko tipa, ali ne i zadovoljavajuće), bolje je ostaviti određivanje metode za vrijeme izvođenja (run-time) kada će biti poznat tip objekta na koji pokazivač pokazuje. Zajedno s objektom je potrebno pohraniti i tip objekta, a prilikom prevođenja prevoditelj mora stvoriti kod koji će te informacije pročitati i pozvati odgovarajuću funkciju. Takav pristup se naziva dinamičkim ili kasnim povezivanjem (dynamic binding).

11.20 Dinamičko povezivanje i virtualni funkcijski članovi Dinamičko povezivanje se može načiniti “ručno” navođenjem tipa u osnovnoj klasi koji će određivati tip na koji pokazuje pokazivač osnovne klase.

C++ nudi elegantnije rješenje za ostvarivanje dinamičkog povezivanja pomoću virtualnih funkcijskih članova koji se deklariraju navođenjem ključne riječi virtual: class Element{ … public:

Page 70: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

70

virtual void Aktiviraj(void) {} }; class Oelement : public Element{ … public:

virtual void Aktiviraj(void); };

Prilikom definicije virtualnog funkcijskog člana izostavlja se riječ virtual: void Oelement::Aktiviraj(void){implementacija}

Svi članovi koji nisu navedeni kao virtualni povezuju se statički i nazivaju statičkim funkcijskim članovima. Oelement *oe=new Oelement; Element *e=oe; e->Aktiviraj(); //poziva se funkcija Aktiviraj() iz klase Oelement

Kada je neki funkcijski član deklariran kao virtualan, on je automatski virtualan i u svim izvedenim klasama, pa nije potrebno eksplicitno navoditi ključnu riječ virtual.

Članovi sa statičkim pozivom mogu biti redeklarirani kao virtualni u izvedenim klasama.

11.21 Pozivi virtualnih funkcijskih članova Za sve virtualne funkcijske članove prevodilac rezervira u memoriji tablicu vtable u koje ih pohranjuje i veže uz odgovarajući objekt pomoću pokazivača *vptr. Prilikom poziva virtualnog funkcijskog člana preko pokazivača tipa osnovne klase neće doći do poziva funkcije iz osnovne klase, već će se iz tablice vtable pozvati implementacija koja odgovara izvedenom tipu. Ovakav način pozivanja funkcijskih članova zahtijeva dodatno procesorsko vrijeme i memorijski prostor u usporedbi sa statičkim povezivanjem.

Kod statičkog povezivanja uz objekt su bili direktno vezani atributi, dok su se metode pozivale prema tipu koji se određivao prema tipu pokazivača koji je pokazivao na objekt. Zato je pristup atributima bio dobar, a metodama neodgovarajući poziv virtualne funkcije će biti virtualan ako se vrši preko pokazivača ili reference: Oelement oe; Element *pokE=&oe, &refE=oe; pokE->Aktiviraj(); refE.Aktiviraj(); //oba poziva su virtualna

Postoje tri slučaja kada se poziv virtualnog funkcijskog člana obavlja statički:

1. poziv preko objekta klase, jer se pri prevođenju točno zna tip za koji se član poziva: Oelement oe; oe.Aktiviraj();

2. eksplicitno navođenje klase iz koje se poziva funkcijski član preko scope resolution operatora: oe.Element::Aktiviraj();

Eksplicitno navođenje područja izvedene klase nije moguće ako se radi o pokazivaču tipa osnovne klase zbog nepoznavanja objekta na koji pokazivač pokazuje prilikom prevođenja:

atributi objekta

vptr

Aktiviraj

vtable

Page 71: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

71

Element *pokOe=&oe; pokOe->Oelement::Aktiviraj(); //greška

Statičko pozivanje se odnosi samo na onaj član koji je eksplicitno naveden. Svi članovi koji se pozivaju iz njega zadržavaju svoj način povezivanja: Oe.Element::Aktiviraj(); //sve funkcije koje bi se eventualno pozvale iz Aktiviraj() zadržale bi svoje povezivanje

3. poziv funkcije iz konstruktora (poziv virtualne funkcije može rezultirati pristupom još neinicijaliziranom dijelu objekta) ili destruktora (poziv virtualne funkcije može rezultirati pristupom već uništenom dijelu objekta).

11.22 Čiste virtualne funkcije Klasa Element zapravo nema neko stvarno značenje i nema smisla definirati objekt te klase. Takve klase koje samo definiraju javno sučelje nazivaju se apstraktnim klasama (abstract classes). To javno sučelje biti će specificirano tek u izvedenim klasama.

Funkcije koje služe samo za definiranje javnog sučelja preporučljivo je specificirati kao čiste virtualne funkcije (pure virtual functions). Apstraktne klase (termin C++ jezika) sadrže barem jednu čistu virtualnu funkciju: class Element{ … public:

virtual void Aktiviraj(void)=0; //Aktiviraj je čista virtualna funkcija };

Sada smo i formalizirali u C++ jeziku činjenicu da nema smisla definirati objekt klase Element. Takav pokušaj rezultirati će pogreškom prilikom prevođenja. Reference i pokazivače tipa apstraktne klase je naravno moguće definirati (polimorfizam!). Čiste virtualne funkcije se normalno nasljeđuju i mogu se definirati u izvedenoj klasi (time te funkcije prestaju biti čistima). Ukoliko se to ne učini i izvedena klasa postaje apstraktnom. Poziv čiste virtualne funkcije koja nije definirana završava pogreškom.

11.23 Virtualni destruktori Smisao virtualnih destruktora je (ispravno) uništavanje objekta izvedene klase pozivom operatora delete na pokazivaču osnovne klase koja pokazuje na izvedenu: Oelement *oe=new Oelement; Element *e=oe; delete e;

Ukoliko bi destruktor imao statičko povezivanje gornji poziv bi rezultirao pozivom destruktora bazne klase (jer je to tip pokazivača e). Rješenje je proglašavanje destruktora virtualnim u baznoj klasi Element: class Element{ … public:

virtual ~Element(void){…} }

Destruktori svih izvedenih klasa će automatski biti virtualni, pa to nije potrebno posebno specificirati riječju virtual. U principu je dobro uvijek destruktor apstraktne klase proglasiti virtualnim jer destruktor sa statičkim povezivanjem tu niti nema smisla.

Page 72: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

72

Ukoliko se klasa ne nasljeđuje nema smisla koristiti riječ virtual, jer ona samo proširuje objekt pokazivačem vptr na tablicu virtualnih funkcija i zauzima u memoriji prostor za tablicu vtable.

11.24 Polimorfizam i proširivost koda Sada kada je objašnjen princip polimorfizma pokažimo na našem primjeru njegovu praktičnu upotrebu. Definirajmo funkciju koja obavlja neku operaciju nad optičkim pojačalima: class Opojacalo : public Oelement{ … public:

virtual void Aktiviraj(void) {implementacija} }; void f(Element *oPoj){ …

oPoj->Aktiviraj(); }

U funkciju je moguće prenijeti bilo koji objekt bilo koje izvedene klase iz apstraktne osnovne klase Element. Nakon nekog vremena odlučimo poboljšati model i podijeliti skup optičkih pojačala i skup optičkih prijamnika. Optička pojačala se naime mogu podijeliti u dvije skupine: erbijem dopirana optička pojačala (EDFA), te poluvodička optička pojačala (SOA) koja su istovremeno i elektronički elementi. Isto tako, u optici se kao optički prijemnici najčešće koriste dvije vrste poluvodičkih dioda: lavinske (APD, Avalanche Photo Diode) i PIN fotodiode.

Deklaracije novih funkcija: class EDFA : public Opojacalo{ public:

virtual void Aktiviraj(void){implementacija} }; class SOA : public Opojacalo, public Eelement{ public:

virtual void Aktiviraj(void){implementacija} }; class APD : public Oprijemnik{ public:

virtual void Aktiviraj(void){implementacija} }; class PIN : public Oprijemnik, public Eelement{ public:

virtual void Aktiviraj(void){implementacija} };

Dodavanje novih elemenata bi kod neobjektno orijentiranih jezika nužno prouzročilo potrebu pisanja novih funkcija koje možda nad tim elementima rade istu operaciju kao i nad već postojećima. Ovdje proširenje stabla nasljeđivanja članovima koji su naslijeđeni iz već postojećih ne donosi nikakvu potrebu za mijenjanjem koda. Prilikom prijenosa objekata novih klasa u funkciju f obaviti će se konverzija na tip apstraktne osnovne klase Element, a poziv funkcije Aktiviraj() će uredno pozvati implementaciju danu u novim klasama.

Možda ovo nakon poznavanja mehanizma ugrađene konverzije i dinamičkog vezivanja ne predstavlja neku novost, ali sjetimo se da prilikom pisanja funkcije f nismo niti slutili da ćemo imati nove klase EDFA, SOA, APD i PIN a da implementacija ipak radi zahvaljujući korištenju funkcije Aktiviraj() koja je dio javnog sučelja, i zato ima smisla na svim izvedenim klasama. Proširivost C++ koda leži dakle u činjenici da je jednom napisane algoritme nad elementom univerzalnog sučelja (definiranog u apstraktnoj osnovnoj klasi) moguće primjenjivati na sve

Page 73: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

73

objekte koji to sučelje dijele (koji su iz te apstraktne osnovne klase naslijeđeni). Na slici 11 prikazana je taksonomija do sada izvedenih elemenata.

Slika 11 Taksonomija nakon dodavanja novih klasa

11.25 Primjeri zadataka 11.25.1 Listing datoteke List.h #include <iostream.h> typedef unsigned long DWord; class lAtom { protected: int data1; int data2; lAtom* Next; lAtom* Prev; public: lAtom(int some1, int some2=0); ~lAtom(void); int getData1(void); int getData2(void); int setData1(int some); int setData2(int some); lAtom* GetNext(void); lAtom* GetPrev(void); lAtom* SetNext(lAtom* some); lAtom* SetPrev(lAtom* some); }; //lista class list { protected: lAtom* Head; DWord cnt; public: list(void); ~list(void); DWord getCnt(void); lAtom* RemoveHead(void); lAtom* RemoveTail(void); lAtom* RemoveElem(int some1, int some2=0);

Page 74: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

74

lAtom* AddToList(int some1, int some2=0); void printAll(void); }; 11.25.2 Listing datoteke List.cpp #include "list.h" lAtom::lAtom(int some1, int some2){ data1=some1; data2=some2; Next=NULL; Prev=NULL; }; lAtom::~lAtom(void){ }; int lAtom::getData1(void){ return data1; }; int lAtom::getData2(void){ return data2; }; int lAtom::setData1(int some){ int retVal=data1; data1=some; return retVal; }; int lAtom::setData2(int some){ int retVal=data2; data2=some; return retVal; }; lAtom* lAtom::GetNext(void){ return Next; }; lAtom* lAtom::GetPrev(void){ return Prev; }; lAtom* lAtom::SetNext(lAtom* some){ lAtom* retVal=Next; Next=some; return retVal; }; lAtom* lAtom::SetPrev(lAtom* some){ lAtom* retVal=Prev; Prev=some; return retVal; }; list::list(void){ cnt=0; Head=NULL; }; list::~list(void){

Page 75: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

75

if(Head==NULL ||cnt==0) return; lAtom* p; while(Head!=NULL){ p=Head->GetNext(); delete Head; Head=p; } Head=NULL; cnt=0; }; DWord list::getCnt(void){ return cnt; }; lAtom* list::AddToList(int some1, int some2){ if(Head==NULL){ Head=new lAtom(some1, some2); cnt++; return Head; } lAtom* p=Head; while(p->GetNext()!=NULL){ p=p->GetNext(); } lAtom* pom=new lAtom(some1, some2); p->SetNext(pom); pom->SetPrev(p); cnt++; return pom; }; lAtom* list::RemoveHead(void){ if(Head==NULL) return NULL; lAtom* retVal=Head; if(cnt==1){ cnt=0; Head=NULL; return retVal; } Head=retVal->GetNext(); Head->SetPrev(NULL); cnt--; return retVal; }; lAtom* list::RemoveTail(void){ if(Head==NULL) return NULL; lAtom* retVal=Head; if(cnt==1){ cnt=0; Head=NULL; return retVal; } lAtom* p=Head; while(p->GetNext()!=NULL){ p=p->GetNext(); } p->GetPrev()->SetNext(NULL); cnt--;

Page 76: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

76

return p; }; lAtom* list::RemoveElem(int some1, int some2){ if(Head==NULL) return NULL; lAtom* p=Head; while(p->GetNext()!=NULL && p->getData1()!=some1 && p->getData2()!=some2){ p=p->GetNext(); } if(p->getData1()==some1 && p->getData2()==some2){ if(cnt==1) Head=NULL; else if(Head==p){ p->GetNext()->SetPrev(NULL); Head=p->GetNext(); }else if(p->GetNext()==NULL) //tail p->GetPrev()->SetNext(NULL); else{ p->GetPrev()->SetNext(p->GetNext()); p->GetNext()->SetPrev(p->GetPrev()); } cnt--; return p; } return NULL; }; void list::printAll(void){ lAtom* p=Head; if(p==NULL) { cout << "List is empty" << endl; return; } while(p!=NULL){ cout<<p->getData1()<<"("<<p->getData2()<<")"<<" "; p=p->GetNext(); } cout << endl; return; };

1. Implementirajte rep cijelih brojeva organiziran po FIFO principu.

Metode: dolazak elementa, odlazak elementa

Preopterećene operatorske funkcije: << ispis repa

[] dohvat elementa repa na zadanoj poziciji (indeksiranje kao u poljima)

- (unarni) dohvat trenutnog promatranog elementa u repu

iterator repa:

++ kretanje kroz rep (sljedeći element)

-- kretanje kroz rep (prethodni element)

Page 77: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

77

Naputak: zadatak riješite korištenjem mehanizma nasljeđivanja iz bazne klase i korištenjem implementiranih osnovnih metoda.

2. Implementirajte stog cijelih brojeva.

Metode: stavljanje elementa (push), skidanje elementa (pop)

Preopterećene operatorske funkcije:

<< ispis stoga

[] dohvat elementa stoga na zadanoj poziciji (indeksiranje kao u poljima)

- (unarni) dohvat trenutnog promatranog elementa u stogu

iterator stoga:

++ kretanje kroz stog (sljedeći element)

-- kretanje kroz stog (prethodni element)

Naputak: zadatak riješite korištenjem mehanizma nasljeđivanja iz bazne klase i korištenjem implementiranih osnovnih metoda.

3. Implementirajte niz cijelih brojeva kojem se dinamički mijenjaju dimenzije (npr. korisnik može pristupiti bilo kojem članu bez deklaracije dužine – polju se automatski povećava dužina da može realizirati zahtjev). Zadatak riješiti uz minimalan broj dealokacija (slijedna struktura)

Metode: pristup članu (getAt(int)), upisivanje vrijednosti člana (putAt(int, int))

Preopterećene operatorske funkcije: << ispis polja

[] dohvat elementa polja na zadanoj poziciji (indeksiranje kao u klasičnim poljima)

- (unarni) dohvat trenutnog promatranog elementa u polju

iterator polja:

++ kretanje kroz polje (sljedeći element)

-- kretanje kroz polje (prethodni element)

Naputak: zadatak riješite korištenjem mehanizma nasljeđivanja iz bazne klase i korištenjem implementiranih osnovnih metoda.

4. Implementirati izlazni spremnik ATM switcha. Ćelije imaju CLP polja s vrijednostima 1 i 0. Vrijednost 1 ima veći prioritet. Sve ćelije imaju svoj cjelobrojni jedinstveni ID.

Metode: primanje nove ćelije, slanje ćelije na link, broj ćelija u repu

Preopterećene operatorske funkcije:

<< ispis ID svih ćelija koje su u spremniku

Page 78: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

78

[] dohvat ID ćelije na zadanoj poziciji u spremniku (indeksiranje kao u poljima)

- (unarni) dohvat ID trenutno promatrane ćelije u spremniku

iterator kompozicije:

++ kretanje kroz spremnik (sljedeća ćelija)

-- kretanje kroz kompoziciju (prethodna ćelija)

Naputak: zadatak riješite korištenjem mehanizma nasljeđivanja iz bazne klase i korištenjem implementiranih osnovnih metoda.

Napomena: Ćelije većeg prioriteta imaju i veći prioritet pri izlasku.

5. HT je naručio program za implementaciju pristupa E1 mux sustavu (30 korisničkih kanala + sinkronizacijski + signalizacijski kanal). Ukoliko je sustav zauzet odbijeni poziv se stavlja u listu čekanja. Oslobađanjem jednog od kanala, taj kanal odmah zauzima poziv koji je najduže čekao. Svaki poziv ima svoj jedinstveni cjelobrojni ID.

Metode: dolazak novog poziva, raskid poziva, ispis postotka zauzetosti sustava.

Preopterećene operatorske funkcije:

<< ispis ID svih poziva koji su u listi

[] dohvat ID poziva na zadanoj poziciji u listi (indeksiranje kao u poljima)

- (unarni) dohvat ID trenutno promatranog poziva u listi

iterator liste:

++ kretanje kroz listu (sljedeći poziv)

-- kretanje kroz listu (prethodni poziv)

Naputak: zadatak riješite korištenjem mehanizma nasljeđivanja iz bazne klase i korištenjem implementiranih osnovnih metoda.

6. Implementirajte spremnik servera koji obrađuje zahtjeve koji mogu imati prioritete 0,1,2. U jednom trenutku obrađuje se samo jedan zahtjev. Ukoliko dođe zahtjev prioriteta 2 za vrijeme obrade nižeg prioriteta tada se on hitno obrađuje, a trenutno obrađivani zahtjev ide na pocetak spremnika.

Metode: dolazak, odlazak, ispis broja paketa u spremniku

Preopterećene operatorske funkcije: << ispis ID svih paketa koje su u spremniku

[] dohvat ID paketa na zadanoj poziciji u spremniku (indeksiranje kao u poljima)

- (unarni) dohvat ID trenutno promatranog paketa u spremniku

iterator spremnika:

++ kretanje kroz spremnik (sljedeći zahtjev)

Page 79: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

79

-- kretanje kroz spremnik (prethodni zahtjev)

Naputak: zadatak riješite korištenjem mehanizma nasljeđivanja iz bazne klase i korištenjem implementiranih osnovnih metoda.

7. Implementirajte sustav koji se sastoji od procesora i ograničenog spremnika (maksimalni broj elemenata m). Procesor u jednom trenutku obrađuje najviše 1 paket (zahtjev).Prioriteti zahtjeva su 0 i 1. Ako zahtjev prioriteta 0 naleti na pun spremnik on će biti odbijen. Ako zahtjev prioriteta 1 naleti na pun spremnik tada će on doći na mjesto zadnje primljenog paketa prioriteta 0 koji će biti izbačen.

Metode: dolazak, odlazak, ispis broja

Preopterećene operatorske funkcije: << ispis ID svih paketa koje su u spremniku

[] dohvat ID paketa na zadanoj poziciji u spremniku (indeksiranje kao u poljima)

- (unarni) dohvat ID trenutno promatranog paketa u spremniku

iterator spremnika:

++ kretanje kroz spremnik (sljedeći paket)

-- kretanje kroz spremnik (prethodni paket)

Naputak: zadatak riješite korištenjem mehanizma nasljeđivanja iz bazne klase i korištenjem implementiranih osnovnih metoda.

Page 80: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

80

12 Predlošci (templates) Teme obrađene u ovom dijelu:

• Što su predlošci (templates) • Predlošci funkcija • Predlošci klasa • Definicija predloška klase • Instanciranje predloška klase i deklaracija objekta • Specijalizacija funkcijskih članova • Predlošci, statički članovi i ugnježđivanje • Predlošci i prijatelji klasa • Predlošci i nasljeđivanje

12.1 Što su predlošci (templates)? Predlošci (templates) relativno su kasno postali dio C++ sintakse i nisu podržani od starijih prevodioca. Upotreba predložaka vrlo je široka i može se koristiti u svim slučajevima kada tip ulaznih podataka nije poznat (odnosno ne spada u mehanizam ugrađene ili korisničke pretvorbe), a može se podvesti pod neki zajednički algoritam.

Predlošci se općenito dijele na:

1. predloške funkcija, i

2. predloške klasa.

12.2 Predlošci funkcija Preopterećenje funkcija dozvoljava upotrebu istog imena za opis algoritama koji obavljaju srodne operacije nad različitim tipovima podataka. Svaka od preopterećenih funkcija mora imati svoju definiciju premda se možda radi o istom algoritmu nad drugačijim tipom.

Predlošci funkcija (function templates) omogućuju pisanje definicije jedne funkcije, pri čemu se tip podataka ostavlja neodređen (otvoren) i određuje se prilikom poziva funkcije.

12.2.1 Definicija predloška funkcije Predložak se definira na sljedeći način: template <class tip1, class tip2, …> povratni_tip imeFunkcije(lista_argumenata){…}

Definicija predloška funkcije počinje ključnom riječi template. U <> zagradama navodi se lista formalnih parametara koja ne može biti prazna. Prije svakog formalnog parametra navodi se riječ class iza kojeg se navodiidentifikator. Vrijednost identifikatora određuje se prilikom prevođenja i zamjenjuje nekim ugrađenim ili korisničkim tipom što ovisi o pozivu funkcije: template <class tip> tip manji(tip a, tip b){

return a<b ? a : b; } void main(void){

int c=4; int d=manji(3,c);

}

Page 81: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

81

Prevodioc će iz gornjeg poziva stvoriti funkciju int manji(int a, int b){

return a<b ? a : b; }

Ime identifikatora predloška funkcije mora biti jedinstveno u listi jedne funkcije, ali se može ponavljati kod različitih funkcija. Identifikator se može upotrijebiti proizvoljno mnogo puta (i nijedanput). Identifikator može određivati povratni tip funkcije. template <class tip> tip f(int a, tip b);

Moguće je koristiti modifikatore & i * da bi se dobila referenca i pokazivač na argument. Dozvoljeno je eksplicitno navođenje tipa formalnog parametra u pozivajućem kodu, pri čemu <> zagrade moraju biti neposredno uz ime funkcije: int a=manji<int,int>(1,2);

Umjesto riječi class prije imena formalnog argumenta dozvoljeno je koristiti i ključnu riječ typename: template <typename tip> tip f(tip a);

Dozvoljeno je korištenje podrazumijevanih parametara predloška: template <class t1, class t2=int> t1 f(t2 a);

Iza navođenja riječi template može slijediti deklaracija ili definicija funkcije. U tom pogledu funkcije s predlošcima se ponašaju kao obične funkcije.

12.2.2 Implicitno instanciranje predložaka Instanciranjem predložaka navode se ugrađeni ili korisnički tipovi koji zamjenjuju formalne parametre i omogućuju prevodiocu stvaranje koda sa stvarnim parametrima implicitno instanciranje dozvoljava određivanje parametara iz tipa prenesenih argumenata, pri čemu se dio argumenata može i eksplicitno navesti: template <class t1, class t2, class t3> t1 f(t2 a, t3 b); … float b=5.5; double a=f<double>(4,b); //t1 je zamijenjen eksplicitno s double, dok su t2 i t3 zamijenjeni implicitno s int i float

Pri određivanju tipova u listi parametara ne primjenjuju se nikakva pravila konverzije. Ako se formalni argument u listi argumenata pojavljuje više puta, tada u pozivu funkcije na tim mjestima mora stajati točno isti tip: template <class t> int f(t a, t b); … int a=1, b=2; int c=f(a, b);

12.2.3 Eksplicitno instanciranje predložaka Eksplicitni instanciranjem predložaka deklariraju se sve inačice predloška koje će se koristiti u programu eksplicitnim navođenjem tipa, pri čemu je potrebno navesti puni potpis funkcije. Da bi prevodioc mogao instancirati željene funkcije iz cijelog skupa propterećenih funkcija: template <class t1, class t2> t1 f(t2 *a, int b);

Page 82: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

82

template double f<double, char>(char*, int); template int f<int, double>(double*, int);

Ako je lista parametara predloška funkcije prazna, a kao formalni parametar predloška se koristi podrazumjevani tip, potrebno je pri pozivu navesti prazne <> ako zadržavamo podrazumjevani tip: template <class t=float> t f(void); … double a=f<>();

Kao formalni parametar može se navesti i konstanta kojom funkcija barata kao da je prenesena argumentom: template <class t, int brElem> void f(void){t b[brElem];} … void f<float, 10> f(); //svaki poziv s drugačijom vrijednosti brElem uzrokuje instanciranje nove funkcije što dovodi do bujanja koda (code bloat)

12.2.4 Preopterećivanje predložaka funkcija Predlošci funkcija se mogu preopteretiti, ali se potpisi funkcija moraju razlikovati po broju i/ili tipu argumenata: template <class t> int f(t a); template <class t> int f(t a, t b); template <class t> int f(t *a);

Korištenjem mehanizma preopterećenja moguće je stvoriti specijalizacije predložaka funkcija, tj. definirati tijelo funkcije za tip koji se ne može svesti pod opće pravilo (čiji se algoritam razlikuje od ostalih slučajeva) tako da se navede uobičajena definicija funkcije, ali s imenom predloška: template manji <class t> t manji(t a, t b){

return a<b ? a : b; } char *manji(char *a, char *b){//specijalizacija predloška

return strcmp(a, b)<0 ? a : b; }

12.3 Predlošci klasa Omogućuju definiranje općih klasa koje se konkretiziraju stvarnim tipom. Najčešće se koriste za kontejnerske klase koje pohranjuju nizove objekata i nad njima. Vrše operacije koje ne ovise o tipu tih objekata.

Cilj upotrebe predloška klasa je olakšavanje održavanja i proširivanja koda. Programer ima gotove algoritme i podatke koji zajedno čine neku logičku cjelinu, ali se primjenjuju na različite tipove. Umjesto kopiranja koda onoliko puta koliko ima tipova na koje ih primjenjuje, može jednostavno primijeniti predložak klase. Time ispravak pogrešaka postaje puno jednostavniji jer je kod samo na jednom mjestu, a ne ponavlja se na nekoliko mjesta samo zbog promjene tipa.

Predlošci klasa omogućuju i upotrebu samo jednog imena bez obzira na tip elemenata te klase. Tako se klasa koja obavlja funkciju stoga može zvati stog bez obzira da li se na stog spremaju cjelobrojni ili realni brojevi.

12.3.1 Definicija predloška klase

Page 83: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

83

Definicija predloška klase slična je definiciji predloška funkcije: template <class t1, class t2, …> class imeKlase;

Lista parametara ne smije biti prazna, a parametar predloška može biti i izraz: template <class t1, class t2, int brElem…> class imeKlase;

Klase definirane kao predlošci obavezno se moraju eksplicitno instancirati navođenjem parametara u <> zagradama: imeKlase<int, double>

Unutar klase se identifikator imeKlase može navesti bez parametara: template <class t> class imeKlase{

t var; imeKlase *pok;

public: imeKlase *f(t arg);

};

12.3.2 Instanciranje predloška klase i deklaracija objekta Instanciranje predloška klase obavlja se pri deklaraciji objekta pri deklaraciji objekta obavezno se navodi puni naziv klase koji uključuje i parametre unutar <> zagrada: imeKlase<int, double> obj;

Sa instanciranim predloškom klase može se postupati kao s običnom klasom: imeKlase<int, double> obj1[10]; const imeKlase<int, double> obj2; extern imeKlase<int, double> obj3;

Pojedine instance neke klase definirane predloškom nije dopušteno miješati jer predstavljaju različite tipove. Nekad je potrebno instancirati predložak unutar definicije drugog predloška, pri čemu je moguće koristiti parametar predloška koji se definira kao stvarni parametar predloška kojeg se instancira: template <class t> void f(imeKlase<t> &a){…}

Klasu je moguće i eksplicitno instancirati: template class imeKlase<int>;

ili samo neki njezin funkcijski član: template class imeKlase<int>::f(int, int);

12.3.3 Specijalizacija funkcijskih članova Funkcijske članove predložaka klasa ponekad je potrebno specijalizirati za neki konkretni tip specijalizacija se provodi navođenjem konkretnog tipa u listi formalnih argumenata: template <class t> class klasa{ public:

t f(t) {return t}; }; klasa<int>::f(int a){return a-1} //posebno instanciranje funkcije f za tip int

Page 84: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

84

Specijalizacija predloška se može navesti samo nakon navođenja općeg predloška ako se instancira specijalizirani primjerak klase, potrebno je definirati sve funkcijske članove eksplicitno.

12.3.4 Predlošci, statički članovi i ugnježđivanje Pri definiranju moguće je navesti statičke članove pri čemu će svaka instanca tog predloška imati svoj zasebni skup statičkih članova. Predloške klasa koji nemaju smisla izvan predložaka neke druge klase moguće je uključiti u tu klasu, pri čemu se prilikom instanciranja klase sadržioca instanciraju sve klase unutar nje: template <class t> class klasa{ public:

template <class t> class klasa2{…} };

Klasa klasa je sada parametrizirana s dva argumenta i njezina inicijalizacija glasi: klasa<int>::klasa2<double> obj;

12.3.5 Predlošci i prijatelji klasa postoje tri načina na koji se jedan predložak može načiniti prijateljem drugog

predloška:

1. predložak se deklarira kao prijatelj druge funkcije i klase koja nije predložak: class a{…}; void f(void); template <class t> class b{

friend class a; //klasa a i funkcija f prijatelji su svih friend void f; //instanciranih klasa predloška b

};

2. svaka klasa jednog predloška veže se uz točno jednu klasu drugog predloška – vezano prijateljstvo (bound template friendship):

template<class t> class c{ friend class b<t>;

};

3. svaka klasa koja proizlazi iz predloška veže se sa svim klasama nekog drugog predloška – nevezano prijateljstvo (unbound template friendship):

template<class tip> class d{friend class b<t>;}{ template<t> friend c<t>;

};

12.3.6 Predlošci i nasljeđivanje Postoje tri načina nasljeđivanja predložaka:

1. predložak klase služi kao osnovna klasa za izvođenje konkretne klase koja nije predložak. Osnovna klasa pri tom mora biti parametrizirana konkretnim tipovima: class nasljednik : osnovna<int, double> {…}

2. predložak klase se izvodi iz obične klase: template <class t> class nasljednik : tip_pristupa osnovna{…} //sve varijante klase nasljednik imaju po jedan neparametrizirani podobjekt klase osnovna

Page 85: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

85

3. nasljeđivanje u kojem se predložak klase izvodi iz nekog drugog predloška. Pri tome je moguće iz svake varijante osnovne klase izvesti jednu varijantu izvedene klase:

template <class t> class nasljednik : tip_pristupa osnovna<t>{…}

ili izvući predložak iz točno određene varijante osnovne klase: template <class t> class nasljednik : tip_pristupa osnovna<int>{…}

12.4 Primjeri zadataka 1. Modificirajte postojeće klase list i atom tako da lista ima potpunu funkcionalnost (ispravan rad svih metoda) neovisno o klasi objekata koje sadrži (koristiti predloške). Nadopunite listu metodama sort i find čije su deklaracije dane u datoteci list.h. Implementirajte klasu vektor (3 dimenzije) čije ćete objekte stavljati u listu.

Metode: konstruktor, destruktor, konstruktor kopije

Preopterećene operatorske funkcije:

<< ispis komponenti vektora

== jednakost vektora

= pridruživanje vektora

<, > usporedba vektora (po apsolutnoj vrijednosti) Napomena: Metoda sort koristi operatore (<, >), a metoda find operator ==

2. Modificirajte postojeće klase list i atom tako da lista ima potpunu funkcionalnost (ispravan rad svih metoda) neovisno o klasi objekata koje sadrži (koristiti predloške). Nadopunite listu metodama sort i find čije su deklaracije dane u datoteci list.h. Implementirajte klasu kompleksni čije ćete objekte stavljati u listu.

Metode: konstruktor, destruktor, konstruktor kopije

Preopterećene operatorske funkcije: << ispis Re i Im dijela

== jednakost kopleksnih brojeva

= pridruživanje

<, > usporedba (po apsolutnoj vrijednosti) Napomena: Metoda sort koristi operatore (<, >), a metoda find operator ==

3. Modificirajte postojeće klase list i atom tako da lista ima potpunu funkcionalnost (ispravan rad svih metoda) neovisno o klasi objekata koje sadrži (koristiti predloške). Nadopunite listu metodama sort i find čije su deklaracije dane u datoteci list.h. Implementirajte klasu string čije ćete objekte stavljati u listu.

Metode: konstruktor (char *), destruktor, konstruktor kopije

Preopterećene operatorske funkcije: << ispis stringa

Page 86: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

86

== jednakost

= pridruživanje

<, > usporedba stringova (po abecednom redu) Napomena: Metoda sort koristi operatore (<, >), a metoda find operator ==

4. Modificirajte postojeće klase list i atom tako da lista ima potpunu funkcionalnost (ispravan rad svih metoda) neovisno o klasi objekata koje sadrži (koristiti predloške). Nadopunite listu metodama sort i find čije su deklaracije dane u datoteci list.h. Implementirajte klasu razlomak čije ćete objekte stavljati u listu.

Metode: konstruktor (brojnik, nazivnik), destruktor, konstruktor kopije

Preopterećene operatorske funkcije:

<< ispis

== jednakost

= pridruživanje

<, > usporedba Napomena: Metoda sort koristi operatore (<, >), a metoda find operator ==

5. Modificirajte postojeće klase list i atom tako da lista ima potpunu funkcionalnost (ispravan rad svih metoda) neovisno o klasi objekata koje sadrži (koristiti predloške). Nadopunite listu metodama sort i find čije su deklaracije dane u datoteci list.h. Implementirajte klasu trokut čije ćete objekte stavljati u listu.

Metode: konstruktor, destruktor, konstruktor kopije

Preopterećene operatorske funkcije:

<< ispis stranica trokuta

== jednakost (sukladnost)

= pridruživanje

<, > usporedba (obzirom na površinu) Napomena: Metoda sort koristi operatore (<, >), a metoda find operator ==

6. Modificirajte postojeće klase list i atom tako da lista ima potpunu funkcionalnost (ispravan rad svih metoda) neovisno o klasi objekata koje sadrži (koristiti predloške). Nadopunite listu metodama sort i find čije su deklaracije dane u datoteci list.h. Implementirajte klasu celija čije ćete objekte stavljati u listu.

Metode: konstruktor (char * sadrzaj, double vrijemeDolaska) , destruktor, konstruktor kopije

Preopterećene operatorske funkcije:

<< ispis sadržaja, vremena dolaska

Page 87: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

87

== jednakost (po sadržaju)

= pridruživanje

<, > usporedba (po vremenu) Napomena: Metoda sort koristi operatore (<, >), a metoda find operator ==

7. Modificirajte postojeće klase list i atom tako da lista ima potpunu funkcionalnost (ispravan rad svih metoda) neovisno o klasi objekata koje sadrži (koristiti predloške). Nadopunite listu metodama sort i find čije su deklaracije dane u datoteci list.h. Implementirajte klasu sinusoida čije ćete objekte stavljati u listu.

Metode: konstruktor (amplituda, frekvencija, pocKut), destruktor, konstruktor kopije

Preopterećene operatorske funkcije:

<< ispis podataka o sinusoidi

== jednakost (nema faznog pomaka, ista frekvencija , ista amplituda)

= pridruživanje objekta objektu

<, > usporedba (prema frekvenciji) Napomena: Metoda sort koristi operatore (<, >), a metoda find operator ==

Page 88: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

88

13 Imenici (namespaces) Teme obrađene u ovom dijelu:

• Što su imenici? • Deklaracija imenika • Pristup elementima imenika • Deklaracija i direktiva using • Klase i deklaracija using

13.1 Što su imenici? Prilikom razvoja složenog programa može doći do ponavljanja imena identifikatora npr. uključivanjem datoteka zaglavlja koje sadrže definicije, a koje su pisali različiti programeri (zagađenje globalnog područja). Imenici služe za uklanjanje identifikatora (imena) varijabli, funkcija, klasa iz globalnog područja imena, tako što sadrže logički povezane grupe identifikatora koji se po potrebi mogu uključiti u globalno područje.

13.2 Deklaracija imenika Deklaracija imenika obavlja se ključnom riječi namespace: namespace imeImenika{

lista identifikatora (imena varijabli i klasa, deklaracije funkcija, itd.)

} namespace covjek{

int godiste; float visina; float tezina; class podaciMUP; int daLiJeJeo(int sekOdPonoci) ;

}

Podaci unutar imenika mogu biti deklarirani i definirani. Imenik može biti ponovno deklariran, ali se ta deklaracija smatra nadopunom prijašnje: namespace covjek{

int celav; }

Ime imenika mora biti jedinstveno u području pojavljivanja. Imenici se mogu pojaviti u globalnom području ili unutar drugog imenika.

13.3 Pristup elementima imenika Elementima imenika pristupa se korištenjem operatora za razlučivanje područja :: : covjek::visina=186.3; int odg=covjek::daLiJeJeo(15231);

Unutar imenika nije potrebno navoditi područje jer se ono podrazumijeva: namespace covjek{

void popravi(void){ godiste+=10;

}

Page 89: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

89

}

Članovima imenika bez naziva pristupa se bez eksplicitnog navođenja imenika: namespace {

int a; int b; } int a; //a u globalnom području b=1; //b iz imenika ::a=2; //a iz globalnog područja a=1; //greška

13.4 Deklaracija i direktiva using Identifikator iz nekog imenika (deklaracija using) ili cijeli imenik (direktiva using) moguće je uvesti u područje imena pomoću ključne riječi using: using covjek::visina; //uvođenje identifikatora visina iz imenika covjek using namespace covjek; //uvođenje cijelog imenika

using ima funkciju deklaracije navedenih identifikatora. Nakon uvođenja identifikatora u područje imena, s tim identifikatorom se može koristiti kao da je u tom području normalno deklariran: using covjek::tezina; tezina=65;

Direktivom using deklariraju se samo oni identifikatori koji su do tog mjesta u kodu uključeni u imenik. Uvođenjem imena preopterećene funkcije uvode se sve instance te funkcije.

13.5 Klase i deklaracija using U klasu se korištenjem using mogu uključiti elementi neke druge klase, ali samo ako su oni dostupni klasi u koju se uključuju i ako je klasa u koju se uključuju izvedena iz klase čiji se elementi uključuju: class osnovna{ public:

void f(int a){cout<<a<<endl;} }; class izvedena:public osnovna{ public:

using osnovna::f; void f(char *s){cout<<s<<endl;} };

Bez korištenja using definicija funkcije f u izvedenoj klasi prebrisala bi definiciju funkcije f iz osnovne klase. Na ovaj način omogućeno je korištenje mehanizma preopterećivanja, tj. pozivi obje funkcije iz izvedene klase su mogući: izvedena obj; obj.f(1); //f iz osnovne klase obj.f(“C++”); //f iz izvedene klase

Page 90: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

90

14 Iznimke Teme obrađene u ovom dijelu:

• Što su iznimke • Blokovi pokušaja i hvatanja iznimaka • Identifikacija iznimaka • Tijek obrade iznimaka • Liste mogućih iznimaka

14.1 Što su iznimke? Iznimke (exceptions) su situacije u kojima program ne može nastaviti izvođenje normalnim tokom, već je potrebno prekinuti dretvu i izvođenje nastaviti nekim drugim odsječkom programa (koji će tu iznimku “obraditi”).

Umjesto izrade vlastitih mehanizama obrade iznimaka, jezik C++ pruža ugrađeni mehanizam za rukovanje iznimkama (exception handling). U tom ugrađenom sustavu iznimka (exception) se definira kao događaj u računalu koji onemogućava normalan nastavak izvođenja programa, te zahtjeva posebnu obradu. Pri detektiranju takve iznimke program podiže iznimku (raise an exception), izvođenje programa se prekida, a iznimka se predaje rutini za oporavak od iznimke (exception recovery routine).

14.2 Blokovi pokušaja i hvatanja iznimaka Ideja je razbijanje C++ koda u blokove pokušaja (try block) koji sadrže potencijalno opasne operacije. Blokovi pokušaja se specificiraju tako da se navede ključna riječ try iza koje se u vitičastim zagradama navode problematične instrukcije.

Svaki blok pokušaja mora biti praćen barem jednim blokom hvatanja (catch block) koji se specificira ključnom riječi catch: try{

blok pokušaja } catch(parametar){ blok hvatanja }

Za podizanje (bacanje) iznimke koristi se ključna riječ throw uz koju se navodi parametar koji ukazuje na tip iznimke i omogućuje pozivanje koda za obradu te vrste iznimke: throw 100; throw “Evo iznimke!”;

14.3 Identifikacija iznimaka Poželjno je definirati posebnu klasu koja će sadržavati opis iznimke i na taj način omogućiti jednostavnije pisanje catch blokova: class iznimkaKlase{ char *opis; public: iznimkaKlase(char *ulaz):opis(new char[strlen(ulaz)+1]){ strcpy(opis,ulaz);}

Page 91: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

91

iznimkaKlase(iznimkaKlase &ref):opis(new char[strlen(ref.opis)+1]){strcpy(opis,ref.opis);} ~iznimkaKlase{delete [] opis;} }; … throw iznimkaKlase(“Nesto krivo vezano uz objekt klase Klasa”); … catch(iznimkaKlase &izn){…}

catch blok sada prima samo iznimke koje kao poruku šalju objekt klase iznimkaKlase. Na taj način omogućeno je jednostavnije razgraničavanje blokova za obradu različitih iznimki.

14.4 Tijek obrade iznimaka Uništavaju se svi privremeni i lokalni objekti koji su stvoreni u bloku u kojem je došli do iznimke, a stvara se novi objekt (prethodni primjer) koji se prenosi prvom catch bloku iza bloka pokušaja koji prihvaća bačenu iznimku. Nakon završetka bloka hvatanja iznimke uništava se privremeni objekt koji je sadržavao podatke o iznimci, a kontrola se prenosi na prvu naredbi iza zadnjeg bloka hvatanja.

Ako u bloku pokušaja nije došlo do iznimke preskaču se svi blokovi hvatanja iza tog bloka. Ako bačena iznimka ne odgovara ni jednom catch bloku koji slijede iza tog bloka pokušaja, ona se prenosi u prvi nadređeni pokušajni blok – do mjesta obrade iznimke će se uništiti svi lokalni i privremeni objekti stvoreni do mjesta bacanja iznimke, odnosno unutar pokušajnog bloka (ili ugniježđenih blokova). Taj postupak se zove odmatanje stoga. Dinamička memorija se neće osloboditi nakon bacanja iznimke. Stoga ju je potrebno obrisati u bloku hvatanja ili osigurati destruktor.

Ako je kao parametar bloku navedena referenca, blok hvatanja može promijeniti proslijeđeni objekt. Ako on dalje prosljeđuje objekt, načinjene promjene biti će vidljive u proslijeđenim blokovima hvatanja.

14.5 Liste mogućih iznimaka Pomoću liste mogućih iznimaka funkcija određuje koje sve iznimke može baciti lista mogućih iznimaka navodi se iza liste parametara funkcije pomoću ključne riječi throw: povratni_tip imeFunkcije(lista_argumenata) throw(tip1, tip2, …){…}

lista mogućih iznimaka može biti i prazna: povratni_tip imeFunkcije(lista_argumenata) throw(){…}

Lista mogućih iznimaka sadrži samo one tipove koji mogu biti bačeni izvan funkcije, bilo da su generirani u samoj funkciji ili nekoj pozvanoj funkciji unutar nje. int f(int a) throw(int){

try{ throw(“Iznimka”);

} catch(char *) {

throw 0; //samo 0 može biti bačena izvan funkcije }

}

Lista mogućih iznimaka nije dio potpisa funkcije.

Page 92: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

92

15 Pretprocesorske naredbe Teme obrađene u ovom dijelu:

• Pojam pretprocesorskih naredbi • Pretprocesor, prevoditelj i povezivač • Naredbe #include, #define i #undef • Uvjetno prevođenje • Uvjetno prevođenje, assert i pronalaženje pogrešaka • Operatori za rukovanje nizovima

15.1 Pojam pretprocesorskih naredbi Uporaba pretprocesorskih naredbi je vrlo česta u programskom jeziku C, no u C++ je bitno smanjena zbog dodatnih mehanizama.

U C++ se pretprocesorske naredbe uglavnom koriste za uključivanje datoteka s deklaracijama funkcija i klasa i uvjetno prevođenje dijelova programa, dok su definiranje konstanti i makro funkcija zamijenili mehanizmi predložaka funkcija, umetnute funkcije i preopterećenje funkcija.

Pretprocesorske naredbe: #pragma #line #define #endif #elif #ifdef #error #undef #include #else #ifndef #if

Pretprocesorske naredbe uzrokuju modifikaciju koda neposredno pred prevođenje i bitno otežavaju nalaženje pogrešaka.

14.2. Pretprocesor, prevoditelj i povezivač

Slika 12 Pretvorba C++ programa u izvršni kod

15.2 Naredbe #include, #define i #undef Naredbom #include provodi se tekstovno povezivanje više datoteka u jednu datoteke je moguće uključiti iz tekućeg direktorija: #include “moj.h”

ili iz podrazumijevanog direktorija:

Page 93: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

93

#include <iostream.h>

Naredbom #define definiraju se vrijednosti simbola ili makro imena #define GENIJE I, me & myself #define PI 3.14

Sve pojave riječi GENIJE i PI će se prilikom prevođenja zamijeniti definiranim zamjenama. Vrijednost definicije može se promijeniti novom naredbom #define: #define GENIJE Homer Simpson

ili naredbom #undef kojom se poništava makro ime #undef GENIJE

Upotreba makro imena za konstante se izbjegava u C++ jer se time ne rezervira memorijska lokacija već samo definira zamjena, pa je debug programa otežan. Umjesto toga koriste se konstantni tipovi: const double pi=3.14;

15.3 Uvjetno prevođenje Makro naredbe za uvjetno prevođenje omogućuju da se u prevođenje uključe neki dijelovi koda ovisno o vrijednosti nekog logičkog izraza. Postoje tri naredbe za ispitivanje uvjetnog prevođenja #if, #ifdef i #ifndef, pri čemu svaka od njih mora završavati sa #endif. Moguće je korištenje više neovisnih grana unutar bloka proizvoljnim brojem grana #elif i maksimalno jednom granom #else: #define GODINA PRVA; … #if GODINA==PRVA char *tesko=“fizika2”; #elif GODINA==DRUGA char *tesko=“elektronika1”; #else char *tesko=“none”; #endif

Makronaredbom #ifdef ispituje se da li je neko ime definirano. Komplementarna makronaredbi #ifdef je naredba #ifndef: #ifndef GODINA char *tesko=“Ne studira!”;

15.4 Uvjetno prevođenje, assert i pronalaženje pogrešaka Ujetno prevođenje se često koristi pri razvoju programa za kontrolne ispise. Te je spise kasnije lako maknuti uklanjanjem makro imena: #define NDEBUG //NoDebug #ifndef NDEBUG

cout<<“Program je u fazi debugiranja”<<endl; #else

cout<<“Program je u fazi prodaje”<<endl; #endif

Ustandardnoj datoteci assert.h nalazi se deklaracija makro funkcije assert koja rovjerava da li je ispunjen neki uvjet, te prekida izvršenje programa ako uvjet nije spunjen: assert(uvjet);

Definiranjem simboličke konstante NDEBUG svi pozivi funkcije assert zamjenjuju se null

naredbom.

Page 94: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

94

15.5 Operatori za rukovanje nizovima Operatori # i ## nemaju nikakve veze s pretprocesorom već služe za rukovanje nizovima. Operator # pretvara simboličko ime varijable u znakovni niz: int varA=5; cout<<Vrijednost “<<#varA<<“ je “<<varA<<endl; //vrijednost varA je 5

Operator ## služi za stapanje simboličkih imena: int nova ## Var; //int novaVar;

Page 95: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

95

16 Organizacija koda Teme obrađene u ovom dijelu:

• Zašto je organizacija koda potrebna • Datoteke zaglavlja • Povezivanje • Specifikatori static i extern

16.1 Zašto je organizacija koda potrebna Kod složenijih programa poželjno je kod podijeliti u više datoteka iz nekoliko razloga:

1. promjenom podatka u jednoj datoteci treba prevesti samo tu datoteku, jer druge nepromijenjene datoteke nije potrebno prevoditi. Povezivač povezuje novo prevedenu datoteku i prije prevedene datoteke u izvršni kod,

2. prevođenje velikog koda zahtjeva veliku simboličku tablicu naziva objekata i funkcija u izvornom kodu i njihove veze s adresom u objektnom kodu,

3. preglednost – definicije klasa je najbolje navesti u zasebnoj datoteci što olakšava njihovo uključivanje u neki budući program, te

4. timski rad – najjednostavnije je da svaki programer piše svoj dio koda u zasebnoj datoteci.

Datoteke (moduli) koje sačinjavaju jedan program se na kraju povežu. Pretprocesorskom naredbom #include u glavnu datoteku (modul) koji sadrži funkciju main(): #include “prva.cpp” #include “druga.h” void main(void){…

16.2 Datoteke zaglavlja u posebne module (datoteke zaglavlja) koje se uključuju u sve ostale datoteke

korisno je staviti deklaracije koje omogućuju da klase i funkcije definirane u jednom

modulu budu vidljive i upotrebljive iz svih modula

u deklaracije zaglavlja najčešće se stavlja:

1. deklaracije (nestatičkih) klasa i struktura (definicije ovih članova se navode samo u datoteci izvornog koda ako nisu s unutarnjim povezivanjem) class kompl{ int re, im; public:

int Re(); int Im();

}

2. deklaracije funkcija extern int f(double, char*);

3. definicije funkcija i objekata s unutarnjim povezivanjem (umetnute funkcije i simboličke konstante),

Page 96: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

96

4. deklaracije globalnih podataka koji moraju biti vidljivi iz različitih modula: extern float verzija;

5. definicije konstanti koje moraju biti vidljivi iz različitih modula: const double pi=3.14;

6. pobrojenja: enum dobriAuti { audi, lada, folcvagen TAS}

7. makrodefinicije i makrofunkcije: #define PI 3.14

U datoteke zaglavlja ne treba stavljati definicije funkcija i objekata datoteke zaglavlja najčešće imaju ekstenziju .h (header) iako to nema nikakvu ulogu pri prevođenju. Datoteke zaglavlja nemaju funkcije main() – one ne pozivaju funkcije, niti ne inicijaliziraju objekte klasa i struktura.

16.3 Povezivanje Datoteke uključene pretprocesorskom naredbom #include uključuju se na početak datoteke i normalno prevode u objektni kod. Objektni kod podijeljen u više različitih datoteka povezuje povezivač (linker). Odgovarajuća definicija funkcije ili tipa se traži tek prilikom povezivanja (prevodioc je preveo kod na temelju deklaracija) pa tu mogu nastati greške ako potpisi deklarirane i definirane funkcije nisu isti.

Identifikatori (objekti i funkcije) koji su prilikom povezivanja vidljivi i u drugim modulima imaju vanjsko povezivanje (external linkage) te moraju biti jedinstveni na cijelom području (ne mogu postojati dvije funkcije istog potpisa).

Svi globalni identifikatori datoteke imaju vanjsko povezivanje. Identifikatori s unutarnjim (inline) povezivanjem vidljivi su samo unutar bloka u kojem su definirani. Identifikatori s vanjskim povezivanjem zaklanjaju isto ime identifikatora s unutarnjim povezivanjem (bez javljanja greške), osim u bloku gdje je identifikator s unutarnjim povezivanjem definiran.

16.4 Specifikatori static i extern Ključnom riječi static ispred deklaracije objekta ili funkcije eksplicitno se određuje interno povezivanje. Globalne objekte i funkcije je preporučljivo učiniti statičkima osim ako ne postoji potreba da oni budu vidljivi iz drugih modula. Ključnom riječi extern se podrazumijevano unutarnje povezivanje identifikatora pretvara u vanjsko.

Pri navođenju riječi extern moguće je objekt inicijalizirati i tada se radi o definiciji (u “izvornom modulu” za taj objekt): extern const float pi=3.14

Ako se inicijalizacija ispusti radi se o deklaraciji identifikatora te se prevodiocu ukazuje da se definicija (a time i rezervacija memorije) tog objekta nalazi negdje drugdje: extern const float pi; //definicija varijable pi se nalazi u nekoj drugoj datoteci

Page 97: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

97

17 C i C++ Teme obrađene u ovom dijelu:

• Usporedba C i C++ • Sličnost sintakse C i C++ • Razlike sintakse C i C++ • Novi mehanizmi u C++ • Nove ključne riječi u C++ • C++ kao objektno orijentirani jezik

17.1 Usporedba C i C++ C++ jezik je dugo ostao zanemarivan od C programera zbog predrasuda kao što je sporost izvršavanja koda i puno veća kompleksnost C++ programa u odnosu na njegov C ekvivalent. Nije postojala potreba za prelazak u novi jezik čija se sintaksa vrlo malo razlikuje od dobro poznate C sintakse pomoću koje je i onako bilo moguće sve riješiti.

C i C++ se mogu uspoređivati na dvije razine:

1. sintaktička razina, i

2. idejna razina.

Kod sintaktičke razine zaista će se pronaći malo razlika između C i C++, no pristup programiranju je kod C i C++ potpuno različit.

17.2 Sličnost sintakse C i C++ Ključni razlog brzog rasta popularnosti C++ je bila lakoća njegovog upotrebljavanja za C programere:

1. C programer C++ može koristiti kao i običan jezik C sa strogom provjerom tipa (strong type checking),

2. ulazno-izlazne funkcije iz C-a (printf(…), scanf(…)) se uključivanjem odgovarajućih datoteka zaglavlja (stdio.h) mogu koristiti i u C++ programima, i

3. umjesto klasa moguće je koristiti klasične C strukture koje su proširene funkcijama.

17.3 Razlike sintakse C i C++ Iako je C i C++ sintaksa vrlo slična postoje neke razlike između upotrebe istih dijelova koda u C i C++.

Sljedeće razlike proizlaze iz činjenice da je C++ jezik sa strogom provjerom tipa (strong type checking):

1. sve funkcije moraju prije upotrebe imati deklaraciju ukoliko ne postoji njihova definicija u istoj datoteci (kasnije je ovo usvojeno i u ANSI C standardu),

2. u C++ je riječ void suvišna: int f(); //u C-u ne govori ništa o parametrima funkcije f, a u C++ govori da funkcija f nema parametara

Page 98: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

98

int f(void); //u C-u govori da funkcija nema parametara, a u C++ je to isto kao i gornja deklaracija

3. u C++ se ne vraćanje vrijednosti iz funkcije koja ima povratni tip smatra greškom, a u C-u ne,

4. u C++ pridruživanje void* pokazivaču vrijednosti bilo kojeg drugog pokazivača zahtijeva eksplicitnu pretvorbu, a u C-u ne: int a=10; void *pok=&a; //u C++ greška, u C-u nije

5. inicijalizacija polja znakova string konstantom je u C++ pogrešna ako je duljina polja premala za cijeli string uključujući i NULL znak: char niz[3]=“C++”; //u C++ greška, u C-u nije

6. umjesto poziva malloc i free koriste se pozivi new i delete,

7. strukture su u C++ proširene funkcijama i mogu se koristiti umjesto klasa

8. deklaracija varijable u C++ (bez extern) smatra se definicijom te varijable. U C-u se radi o mogućoj definiciji pri čemu se pri povezivanju ako ima više mogućih definicija one stapaju u jednu,

9. u C++ podrazumijevano povezivanje povezivanje konstante je static, a u C-u extern,

10. u C++ znakovne konstante su tipa char, a u C-u tipa int,

11. C++ programer se puno manje oslanja na pretprocesor (povezivanje datoteka, uvjetno prevođenje, provjera grešaka) od C programera. U C-u se pretprocesor koristi u istu svrhu kao i u C++ ali i za deklaraciju simboličkih konstanti i makro funkcija koje se u C++ izbjegavaju.

17.4 Novi mehanizmi u C++ C++ uvodi neke nadogradnje i razlike u odnosu na poznate mehanizme iz C-a:

1. umetanje (inlining) i preopterećenje (overloading) funkcija,

2. prijenos argumenata kao referenci (pass-by-reference) za razliku od uobičajenog C prenošenja kao vrijednosti (pass-by-value),

3. operatori new i delete za dinamičku alokaciju umjesto malloc i free,

4. podrška za predloške (templates) funkcija i klasa,

5. podrška za apstraktne tipove podataka preko mogućnosti skrivanja informacija i definiranja javnog sučelja,

6. podrška objektno orijentiranom programiranju preko mehanizama nasljeđivanja i dinamičkog vezivanja (dynamic binding).

Ovi mehanizmi istiskuju neke često korištene mehanizme iz C-a kao definiranje makro funkcija i simboličkih konstanti.

17.5 Nove ključne riječi u C++ C++ uvodi neke nove ključne riječi: virtual this protected new delete

Page 99: Cpp programiranje

C++ i osnove objektno orijentiranog dizajna

99

try template private inline class throw public operator friend catch Neke od njih (try, throw, catch) su vezane uz nove mehanizme koji nemaju veze s idejom objektno orijentiranog programiranja (obrada iznimaka) ili su zamijenile neke riječi iz C-a (new i delete zamjenjuju malloc i free). Ipak većina novih riječi vezana je uz uvođenje pojmova vezanih uz objektno orijentirani dizajn (klase i objekti).

17.6 C++ kao objektno orijentirani jezik Razlike u sintaksi između C i C++ nisu toliko važne koliko je važna razlika u pristupu problemu.

C++ je objektno orijentirani jezik kojeg karakteriziraju tri temeljna obilježja:

1. upotreba klasa i objekata,

2. polimorfizam, i

3. nasljeđivanje.

Enkapsulacija podataka i metoda te njihovo korištenje preko javno definiranih sučelja i izvedenih objekata kroz dinamičko povezivanje predstavljaju temelj pristupa problemu. Novi objektni pristup spada na viši stupanj apstrakcije od čistog proceduralnog C pristupa, te je bio uvjetovan rastom složenosti programskih zadataka i veličine koda.

C++ omogućuje veliku modularnost i ponovnu iskoristivost koda (reusability) što je ključno kod razvoja velikih programa na kojima radi puno programera. Iako će možda većina C programa (uz manje zahvate) raditi i u C++, to ne znači da su oni pisani u C++. U C++ se može nastaviti kodirati kao i u C-u jednostavno zanemarujući ekstenzivnost koju donose nove ključne riječi (C++ jezik sintaktički gledano predstavlja nadogradnju (superset) na C), no u tom slučaju nema smisla prelaziti u C++.