objektno orijentisano programiranje - racunske vezbe (prezentacije)

Post on 27-Dec-2015

91 Views

Category:

Documents

2 Downloads

Preview:

Click to see full reader

DESCRIPTION

Slajdovi sa racunski vezbi iz predmeta Objektno orijentisano programiranje na Elektronskom fakultetu u Nisu. Elfak.

TRANSCRIPT

Objektno-orijentisano programiranje

Napredne tehnike programiranja u programskom jeziku C

Pokazivači

Pokazivači predstavljaju poseban izveden tip podataka.

Podatak tipa pokazivača sarži memorijsku adresu nekog drugog podatka.

Deklaracija pokazivača: <tip> *<ime>;

Operatori koji se koriste u radu sa pokazivačima

Operator referenciranja (&) - određuje adresu neke promenljive;

Operator dereferenciranja (*) - određuje vrednost promenljive na koju ukazuje neka pokazivačka promenljiva.

Primer

int i=0, j=0;//promenljive su tipa int

int *pi; //pi je pokazivac na int

pi=&i; //pi ukazuje na promenljivu I

*pi=2; //promenljiva na koju ukazuje pi

// dobija vrednost 2, tj. i=2

j=*pi; //j dobija vrednost 2 , j=I

pi=&j; //pi dobija adresu promenljive j,

//tj. ukazuje na j

Pokazivač tipa void

Pokazivač tipa void (generički pokazivač) može da primi adresu bilo kog objekta u memoriji.

Primer: int i=0;

int *pi1=&i, *pi2;

void *pv; //pokazivac na bilo koji tip

pv=pi1; //pv ukazuje na i

pi2=pv; //nemoguca dodela, greska

Konstanta NULL

Konstanta NULL je definisana u header fajlu stdio.h i ima vrednost 0. Dodeljuje se pokazivačkoj promenljivoj kada on ne ukazuje ni na jedan podatak u memoriji.

Korišćenje pokazivača u programu

U radu sa poljima,

Za pristup podacima u dinamičkoj zoni memorije,

Za prenos argumenata funkcije.

Veza pokazivača i polja

Ime polja u programskom jeziku C je pokazivaš na prvi element polja.

Primer: int polje[10], *pok_polja, a;

pok_polja=&polje[0]; //pok_polja=polje;

a=polje[0]; //a=*polje; ili a=*pok_polja;

Adresna aritmetika

Dozvoljene su sledeće operacije nad okazivačima: dodela vrednosti jednog pokazivača drugoma (samo

ako su istog tipa);

dodavanje ili oduzimanje celobrojnog podatka od vrednosti pokazivača (ako je pokazivač pa=&a[k], pa+1=&a[k+1] bez obzira kolika je dužina podatka a – ne dodaje se bukvalno 1 – već dužina podatka odgovarajućeg tipa);

uporedjivanje 2 pokazivača (samo ako ukazuju na elemente istog niza);

poredjenje pokazivača sa nulom (ispituje da li pokazivač uopšte uakzije na neki objekat – umesto 0 koristi se konstanta NULL);

Zadatak 1.

Napisati program na C-u za izračunavanje skalarnog proizvoda dva vektora sa po N elemenata. Elementima vektora pristupati korišćenjem pokazivača.

Dodela memorijskog prostora podacima

1. Statička – prostor za smeštanje vrednosti promenljive se rezerviše kada se počne sa izvršenjem bloka u kojem je ona definisana, a oslobadja se po završetku izvršenja tog bloka. Kada se polja pamte u statičkoj zoni memorije, potrebno je predvideti njegovu maksimalnu veličinu niza i toliki se prostor pri svakom izvršenju programa i za sve vreme izvršenja programa rezerviše za smeštanje njegovih elemenata.

Dodela memorijskog prostora podacima

2. Dinamička – prostor za smeštanje podatak u memoriji se zauzima u toku izvršenja programa. U tom slučaju u statičkoj zoni memorije definiše se jedan pokazivač na taj deo memorijskog prostora. U toku rada programa može da se rezerviše mem. prostor na koji taj pokazivač ukazuje, da se menja veličina tog mem. prostora i da se on oslobadja te da se u nastavku izvršenja programa koristi za pamćenje drugih podataka.

Funkcije za upravljanje dinamičkom zonom memorije

Deklarisane su u header fajlu stdlib.h. void* malloc(long velicina) –

rezerviše deo memorijskog prostora zadate veličine. Veličina se zadaje brojem bajtova. Funkcija vraća generički pokazivač (tipa void*) na rezervisani memorijski prostor, ili NULL ako rezervacija ne može da se izvrši. Sadržaj rezervisanog prostora je nedefinisan.

Funkcije za upravljanje dinamičkom zonom memorije

void* calloc(int broj,int velicina) –

rezerviše deo memorijskog za pamćenje navedeni broj elemenata zadate veličine. Funkcija vraća generički pokazivač (tipa void*) na rezervisani memorijski prostor, ili NULL ako rezervacija ne može da se izvrši. Rezervisani deo memorisjkog prostora se inicijalno popunava nulama.

Funkcije za upravljanje dinamičkom zonom memorije

void* realloc(void* pokazivac, int velicina) – Ova funkcija menja veličinu memorijskog prostora na koji ukazuje zadati pokazivač na zadati veličinu (tj. na zadati broj bajtova). Veličina memorisjkog prostora na koji ukazuje navedeni pokazivač se na ovaj način može i da se smanji i da se poveća.

void free(void* pokazivac) – oslobadja deo memorijskog prostora na koji ukazuje davedeni pokazivač.

Zadatak 2.

Napisati program na C-u za zamenu prve pojave podstringa s1 u stringu s stringom s2. Stringovi s1 i s2 mogu biti različitih dužina. Sve stringove pamtiti u dinamičkoj zoni meorije i za svaki od njih u svakom trenutku treba da bude rezervisano onoliko memorijskog prostora koliko je potrebno za pamćenje njihovih vrednosti.

Matrice u dinamičkoj zoni memorije U statičkoj zoni memorije matrica se pamti u

linearizovanom obliku (matrica reda mxn se pamti kao niz dužine m*n - linearizacija se vrši po vrstama).

U dinamičkoj zoni memorije matrica može biti

predstavljena na tri načina: linearizovano – matrica se zamenjuje vektorom

odgovarajuće dužine; tako što se u dinamičkoj zoni memorije rezerviše

prostor za onoliko nezavisnih vektora koliko vrsta ima matrica, a u statičkoj zoni memorije se matrica definiše kao vektor pokazivača na vrste;

tako što se i vrste i vektor pokazivača na vrste smeštaju u dinamičku zonu memorije – u statičkoj zoni memorije u tom slučaju postoji samo pokazivač na vektor pokazivača na vrste.

Linearizovano smeštanje matrice u dinamičkoj zoni memorije

Šematski prikaz na ovaj način zapamćene matrice:

statička zona memorije dinamička zona memorije

a

...

mxn elemenata

Linearizovano smeštanje matrice u dinamičkoj zoni memorije

Rezervacija prostora: int *a,m,n,i,j;

a=(int*)calloc(m*n,sizeof(int));

Pristup elementima matrice:

pomoću indeksa: …a[n*i+j]…

korišćenjem pokazivača: …*(a+n*i+j)

Kombinovano smeštanje matrice u statičkoj i dinamičkoj zoni Šematski prikaz smeštanja podataka:

statička zona dinamička zona

a

...

m pokazivača n elemenata u nizu

Kombinovano smeštanje matrice u statičkoj i dinamičkoj zoni

Rezervacija prostora: int *a[10],m,n,i,j;

for (i=0;i<m;i++)

a[i]=(int*)calloc(n,sizeof(int));

Pristup elementima matrice:

pomoću indeksa: …a[i][j]…

korišćenjem pokazivača: …*(*(a+i)+j)

Kompletno smeštanje matrice u dinamočkoj zoni memorije

Šematski prikaz smeštanja podataka: dinamička zona memorije

a

...

m pokazivača po n elemenata u nizu (vrsti)

Kompletno smeštanje matrice u dinamočkoj zoni memorije

Rezervacija prostora: int **a,m,n,i,j;

… a=(int**)calloc(m,sizeof(int*));

for (i=0;i<m;i++)

a[i]=(int*)calloc(n,sizeof(int));

Pristup elementima matrice: pomoću indeksa:

…a[i][j]… korišćenjem pokazivača:

…*(*(a+i)+j)

Prenos parametara korišćenjem pokazivača

U programskom jeziku C-u svaki parametar elementarnog tipa se prenosi funkciji po vrednosti. To podrazumeva da se pri pozivu funkcije pravi kopija stvarnog argumenta u OM, funkcija radi sa tom kopijom i u trenutku završetka rada funkcije kopija se briše iz operativne memorije. To automatski onemogućava da parametar funkcije bude promenjen u funkciji, a da to bude vidljivo u pozivajućem modulu.

Kada se javi potreba da se se promena parametara u funkciji vidi u pozivajućem modulu, za prenos parametara se koriste pokazivači.

Zadatak 3.

Napisati funkciju na C-u za zamenu

vrednosti dveju promenljivih.

Strukture

Struktura je složeni tip podataka koji sadrži elemente različitih tipova.

Definicija strukture

struct <ime>

{

<tip1> <ime1>;

<tip2> <ime2>;

… <tipN> <imeN>;

};

Primer

struct student

{

char prezime[20];

char ime[20];

char datumrodjenja[8];

int godina;

int ocena[30];

};

Definicija podataka tipa strukture

struct student student1,

*ptrstudent, druga_godina[250];

Operacije nad strukturama

pristupanje članovima strukture korišćenjem operatora . i ->

student1.godina = 3; ptrstudent->ocena[0]=8;

određivanje adrese strukture korišćenjem operatora & ptrstudent=&student1;

određivanje veličine strukture pomoću sizeof operatora

ptrstudent=(struct student*) malloc( sizeof(struct student));

dodeljivanje svih elemenata jedne strukture drugoj strukturi istog tipa

student1=treca[i];

Zadatak 4.

Napisati program na C-u za obradu podataka sa prijemnog ispita na jednom fakultetu. Program treba da štampa rang listu studenata koji su položili prijemni ispit. Rang lista sadrži imena, prezimena, matične brojeve i ukupne brojeve poena studenata i uredjena je u nerastućem redosledu prema ukupnom broju poena. Student polaže prijemni ispit iz dva predmeta i smatra se da je položio prijemni ispit ukoliko je iz oba predmeta osvojio najmanje polovinu od maksimalnog broja bodova.Ukupan broj bodova se dobija kao zbir bodova koje student ima na osnovu uspeha iz srednje škole i broja bodova osvojenih iz oba predmeta na prijemnom ispitu.

Objektno-orijentisano programiranje

(Vežbe – 1. čas)

Objekti svuda oko nas

U svakodnevnom životu svet spoznajemo kao skup objekata koji nas okružuju i koji medjusobno interaguju na različite načine.

Možemo li da definišemo objekat?

Šta je objekat?

Svaki objekat je odredjen:

skupom svojih osobina (karakteristika ili atributa),

skupom funkcija koje može da obavlja i

implementacionim detaljima (načinom realizacije funkcija).

Primer objekta

Posmatrajmo automobil kao objekat. Atributi objekta: - marka automobila, - boja, - maksimalna moguća brzina kretanja, - trenutna brzina kretanja,... Funkcije koje obavlja: - paljenje, - gašenje, - promena brzine,... Implementacioni detalji: U implementacione detalje spadaju postupci (algoritmi) za

realizaciju svake od navedenih funkcija.

Koncept objekto-orijentisanog programiranja

Cilj objektno-orijentisanog programiranja

je da softverski sistem modelujemo onako

kako doživljavamo svet oko nas. Umesto o tome kako rešiti problem, u

razvoju OO softvera razmišljamo: koji se objekti u programu obradjuju i

kako oni medjusobno interaguju.

Pojam klase

Klasa je šablon za kreiranje objekata u programu.

Klasa je korisnički definisan tip podataka koji se sastoji od atributa (kojima se opisuju karakteristike objekata koji će se kreirati) i funkcija (ili metoda) koje će objekti obavljati, tj. preko kojih će interagovati sa drugim objektima u programu. Atributi i funkcije se jednim imenom nazivaju članovi klase.

Definicja klase u programskom jeziku C++

Klasa se u programskom jeziku C++ obično definiše u posebnom fajlu koji ima sto ime kao i sama klasa i ekstenziju “.h”.

Format definicije klase u jeziku C++ je: class <ImeKlase>

{

<ListaClanovaKlase>

};

Lista članova sadrži definicije članova atributa i deklaracije funkcija.

Pravo pristupa članovima klase

Svaki član klase može biti dostupan: Samo sopstvenoj klasi (ako se može korisiti samo u

funkcijama članicama klase). Za takve članove se kaže da su privatni (private) članovi klase.

Sopstvenoj klasi i klasama izvedenim iz klase kojoj pripada – to su takozvani zaštićeni (protected) članovi.

Svim delovima aplikacije – to su javni (public) članovi klase.

NAPOMENA: Nepisano je pravilo da svi atributi

klase budu definisani kao private članovi.

Primer definicije klase // Vozilo.h class Vozilo { private: int stanje; //definise da li je vozilo // ukljuceno int max_brzina; int brzina; public: void ukljuci(); void iskljuci(); void ubrzaj(); void uspori(); };

Implementacija funkcija članica klase

Funkcije članice klase se implementiraju u posebnom fajlu koji nosi isto ime kao klasa sa ekstenzijom “.cpp”.

Format implementacije funkcije članice klase:

<povratni_tip> <ImeKlase>::<imeFunkcije>(<listaArgumenata>)

{

<teloFunkcije>

}

Primer implementacije funkcija klase Vozilo

// Vozilo.cpp

#include “Vozilo.h” void vozilo::ukljuci()

{

stanje = 1;

}

void vozilo::iskljuci()

{

stanje = 0;

}

void vozilo::ubrzaj()

{

if ( brzina < max_brzina)

brzina++;

}

void vozilo::uspori()

{

if ( brzina > 0 ) brzina--;

}

Kreiranje objekata u programu

Objekat klase, kao i svaki drugi podatak, može biti zapamćen u statičkoj ili dinamičkoj zoni memorije. Objekat zapamćen u statičkoj zoni memorije (statički objekat)

definiše se naredbom: <ImeKlase> imeObjekta;

i kreira se kada se pri izvršenju programa dodje do tačke gde je objekat definisan, a briše se kada se završi izvšenje bloka u kojem je definisan.

Za pristup objektu koji se pamti u dinamičkoj zoni memorije (dinamičkom objektu) koristi se pokazivač koji se definiše naredbom:

<ImeKlase> *pokazivac;

Dinamički objekat se kreira operatorom new: pokazivac = new <ImeKlase>;

a brise se operatorom delete: delete pokazivac;

Primer kreiranja objekata klase Vozilo

Vozilo bmw;

Vozilo* yugo;

yugo = new Vozilo;

...

delete yugo;

Referenca

Osim objekta i pokazivača na objekat, u programskom jeziku C++ može se definisati i referenca na objekat.

Referenca je drugo ime nekog objekta.

Referenca se u definiciji mora inicijalizovati.

Definicija reference:

<ImeKlase>& <imePromenljive>=<PocetnaVrednost>;

Primer: Vozilo& stranoVozilo = bmw;

Vozilo& domaceVozilo = *yugo;

Pristup članovima klase

Za pristup javnim članovima klase se koriste operatori . i ->.

Format izraza za pristup članovima klase je: <objekat>.<atribut>

<objekat>.<funkcije>(<stvarniArgumenti>

<pokazivacNaObjekat>-><atribut>

<pokazivacNaObjekat>-><funkcija>(<stvarniArgumenti>)

Primer korišćenja funkcija članica klase

// main.cpp

#include ”vozilo.h”; void main()

{

vozilo bmw;

vozilo *yugo;

yugo = new vozilo(); bmw.ukljuci(); yugo->ukljuci(); }

Pokazivač this

Da bi se zadržao princip da se prilikom pristupa članovima klase mora znati objekat čijem se članu pristupa, za pristup članovima klase iz funkcija članica klase može se koristiti pokazivač this.

this je pokazivač na objekat za koji je funkcija pozvana.

Primer korišćenja pokazivača this

// Vozilo.cpp

#include <vozilo.h>

void vozilo::ukljuci()

{

this->stanje = 1;

}

void vozilo::ubrzaj()

{

if ( this->brzina < this->max_brzina ) this->brzina++;

}

Korišćenje standardnog ulaza i standardnog izlaza u C++ programima

Za učitavanje podataka sa standardnog ulaza i prikaz podataka na standardni izlaz u

C++ aplikacijama se mogu koristiti:

Bibliotečke funkcije definisane u standardnoj biblioteci programskog jezika C (scanf, printf, ... )

Objekti cin i cout.

istream i ostream klase

Analogno standardnoj biblioteci funkcija

u jeziku C, u programskom jeziku C++

definisana je biblioteka klasa. U toj

biblioteci definisane su i klase:

istream – za učitavanje podataka iz različitih izvora;

ostream – za upis podataka u različite resurse.

Obe ove klase definisane su u fajlu i

iostream.h.

Objekti cin i cout

cin je objekat klase istream koji se kreira kao globalni objekat prilikom startovanja svake C++ aplikacije i služi za učitavanje podataka sa standardnog ulaza. Podaci standardnih tipova se učitavaju sa standardnog ulaza (tj. iz objekta cin) korišćenjem operatora >>.

cout je objekat klase ostream koji se kreira kao globalni objekat prilikom startovanja svake C++ aplikacije i služi za upis podataka na standardni izlaz. Podaci standardnih tipova se upisuju na standardni izlaz (tj. u objekat cout) korišćenjem operatora <<.

Prelaz na nov i red prilikom prikaza podataka na standarndi izlaz vrši se upisom konstante endl u objekat cout.

Primer korišćenja objekata cin i cout

int a,b;

// ucitavanje vrednosti promenljivih

// a i b sa standardnog ulaza

cin >> a >> b;

// prikaz zbira promenljivih a i b

// na standardni izlaz

cout << a+b << endl;

Objektno-orijentisano programiranje

(Vežbe – 2. čas)

Zadatak 1. Klase – atributi i funkcije

Na programskom jeziku C++ kreirati klasu Tacka koja sadrži: Privatne atribute: - x i y koordinate tačke u Dekartovom Koordinatnom sistemu. Javne metode za: - postavljanje tačke na zadatu poziciju, - pomeranje tačke na zadati pomeraj, - računanje rastojanja od druge tačke, - uzimanje x i y koordinate tačke. U funkciji main definisati jedan statički i jedan dinamički objekat klase klase Tacka, postaviti ih na pozicije čije se koordinate učitavaju sa standardnog ulaza, izračunati I prikazati na standardni izlaz rastojanje izmedju njih, izvršiti pomeranje prve tačke za pomeraj (2.5,2.5) i štampati nove koordinate te tačke.

Faze kreiranja C++ aplikcaje u Microsoft Visual C++ 6.0 okruženju

Kreiranje projekta;

Kreiranje Definicje klase;

Implementacija funkcija članica klase;

Implementacija funkcije main.

Inline funkcije

Kada se metode klase veoma često koriste, a kada su im tela sasvim jednostavna, takve metode treba definisati kao inline metode.

Prilikom prevodjenja C++ programa, kompilator će u tački poziva inline funkcije kompletno telo funkcije ugraditi u izvršni kod. Na taj način će se vreme izvršenja programa znatno smanjiti, dok će se veličina izvršnog koda neznatno povećati.

Definicja inline funkcije

Funkcija je tipa inline ukoliko je:

implementacija funkcije navedena u samoj definiciji klase, ili

u definiciji klase, ispred deklaracije funkcije, navedena ključna reč inline, a implementacija funkcije navedena u header fajlu gde je i klasa definisana.

Primer inline funkcija // Vozilo.h

class Vozilo

{

private:

int stanje;

int max_brzina;

int brzina;

public:

void ukljuci();

void iskljuci();

void ubrzaj();

void uspori();

int uzmiBrzinu() {

return brzina;

}

inline void postaviBrzinu( int brzina );

};

void Vozilo::postaviBrzinu( int brzina ) {

this->brzina = brzina;

}

Zadatak 1A. Inline funkcije

Funkcije za uzimanje koordinata x i y

definisati kao inline funkcije.

Prenos parametara

U programskom jeziku C++ parametri

se na funkciju mogu preneti:

po vrednosti i

po referenci.

Prenos parametara po vrednosti

Prilikom prenosa parametara po vrednosti, u trenutku

poziva funkcije pravi se kopija stvarnih argumenata,

funkcija radi sa tim kopijama, a u trenutku završetka

rada funkcije, kopije se brišu iz memorije. Nedostaci prenosa parametara po vrednosti:

- izmene ulaznih argumenata se nikada neće videti u pozivajućem modulu;

- pravljenje kopija za sve ulazne argumente troši veliku količinu dodatnog memorijskog prostora.

Prenos parametara po referenci

Ukoliko se kao argument funkcije ne navede podatak (objekat), već referenca na taj podatak (objekat), referenca će se u trenutku poziva funkcije inicijalizovati tako da će ona predstavljati “drugo ime” za odgovarajući stvarni argument. U tom slučaju i pozivajući modul i funkcija će raditi sa istom kopijom podatka (objekta).

Ovaj način prenosa parametara će se koristiti kada: - funkcija treba da promeni vrednost ulaznog argumenta, - kada je ulazni argument prilično glomazan pa nema smisla rezervisati novi memorijski prostor za pamćenje njegove kopije. Nepisano je pravilo da objekte treba uvek prenositi po referenci.

Zabrana promene parametra prenetog po referenci

Kada je parametar prenet funkciji po referenci ne zna se da li ce on u toj funkciji biti stvarno promenjen, ili je to uradjeno samo zbog uštede memorijskog prostora. Da bi se takva nedoumica eliminisala, parametre koje funkcija ne sme da menjati treba definisati kako konstantne za funkciju.

Primer: void f1( const T& a );

U ovom slučaju ukoliko u funkciji f1 pokušamo da promenim objekat a, kompilator će prijaviti grešku.

Zadatak 1B. Prenos parametara po referenci

U funkciji za računanje rastojanja, tačku do koje se računa rastojanje, preneti po referenci.

Objektno-orijentisano programiranje

(Vežbe – 3. čas)

Statički članovi klase

Ponekad se javlja potreba da postoji isti atribut u svim objektima (instancama) klase. U takvim slučajevima nema smisla da se isti podatak pamti u memoriji onoliko puta koliko se objekata te klase napravi u programu. Atributi koji su isti za sve instance klase, pa čak mogu da postoje i kada ni jedan objekat klase nije definisan, definišu se kao statički atributi klase.

Statička funkcija klase je funkcija čije dejstvo nije vezano za konkretne objekte (instance) klase. One se mogu pozivati i kada ni jedan objekat klase u programu nije kreiran.

Definicija statičkih članova klase

U definiciji klase, ispred definicije statičkog podatka, kao i ispred deklaracije statičke funkcije, navodi se ključna reč static.

Primer:

class Krug

{

float r;

public:

static float pi;

float obim();

… };

Pristup statičkim članovima klase

Statičkim članovima klase se može pristupiti na dva

načina: Korišćenjem nekog od kreiranog objekta klase i

operator . ili -> (na isti način kako smo i do sada pristupali članovima klase).

Navodjenjem imena klase i operatora pripadnosti :::

<ImeKlase>::<imeStatičkogAtributa> tj.

<ImeKlase>::<imeStatičkeFunkcije>( <StvarniArgumenti>)

Iz funkcija članica klase, statičkim članovima se može

pristupiti kao i ostalim članovima klase.

Primeri pristupa statičkim članovima klase

// pristup statickim clanovima klase iz

// funkcija clanica iste klase

float Krug::obim()

{

return 2*Pi*r;

}

// pristup statickim clanovima klase iz

// ostalih delova programa

void main()

{

Krug::pi = 3.14;

… }

Zadatak 2. Statički članovi klase

Na programskom jeziku C++, kreirati klasu Radnik čiji su članovi: privatni atributi: ime, prezime i koeficijent stručne spreme

radnika; javni statički atributi: broj radnika rpreduzeća, cena rada za tekući

mesec u preduzeću, visina naknade za prevoz po radniku; javne metode za: učitavanje vrednosti privatnih atributa sa

standardnog ulaza, za štampanje imena prezimena i plate radnika na standardni izlaz (pri čemu se plata računa kao proizvod koeficijenta stručne spreme i cene rada);

javna statička metoda za izračunavanje ukupno potrebnih sredstava za isplatu naknade za prevoz radnika ukoliko se svima isplaćuje ista svota.

U funkciji main, učitati broj radnika, visinu cene rada, visinu naknade za prevoz i podatke o svim radnicima sa standardnog ulaza i na standardni izlaz prikazati platni spisak radnika i veličinu sredstava potrebnih da se isplati naknada za prevoz svim radnicima.

Objektno-orijentisano programiranje

(Vežbe – 4. čas)

Konstruktori

Konstruktor je posebna funkcija klase koja ima isto ime kao i klasa i služi da se definišu početne vrednosti atributa klase.

Konstruktor nema povratni tip (ni void).

Konstruktor se poziva u trenutku kreiranja objekta klase.

U klasi je moguće definisati veći broj konstruktora pri čemu se oni medjusobno moraju razlikovati po broju i tipovima argumenata.

Ukoliko u klasi nije definisan ni jedan konstruktor kompilator će sam napraviti konstruktor bez argumenata (default konstruktor).

Primer konstruktora klase Vozilo

// Vozilo.h

class Vozilo

{

...

public:

Vozilo();

Vozilo( int max );

...

};

Inicijalizacija atributa u konstruktoru

Inicijalizacija atributa u konstruktoru se može izvršiti u telu konstruktora ili u delu za inicijalizaciju.

Deo za inicijalizaciju se piše iza zaglavlja konstruktora, a pre njegovog tela i od zaglavlja je odvojen simbolom :.

U delu za inicijalizaciju pozivaju se konstruktori atributa kao i konstruktor roditeljske klase (o tome će kasnije biti reči). Tu se obavezno inicijalizuju reference i konstantni atributi.

Implementacija konstruktora klase Vozilo

Vozilo::Vozilo()

{

this->brzina = 0;

this->max_brzina = 180;

this->stanje = 0;

}

Vozilo::Vozilo(int max)

: brzina(0), max_brzina(max), stanje(0)

{

}

Poziv konstruktora

Koji će se od konstruktora klase pozvati zavisi od broja i tipova argumenata koji su u pozivu navedeni.

Primer: // poziv konstruktora bez argumenata

Vozilo bmw;

Vozilo* yugo = new Vozilo;

// ili:

Vozilo* yugo = new Vozilo();

// poziv konstruktora sa argumentima

Vozilo bmw( 220 );

Vozilo* yugo = new Vozilo( 160 );

Kreiranje dinamičkih atributa u konstruktoru

Dinamički atributi klase se obično kreiraju u konstruktoru. Primer: Neka je Vektor klasa čiji su privatni atributi broj elemenata u

vektoru i dinamički niz elemenata tipa double. Definisati konstruktor klase koji inicijalizuje broj elemenata u vektoru.

class Vektor {

int n;

double* niz;

public:

Vektor( int velicina )

: n(velicina)

{

niz = new double[n];

}

}

Konstruktori za kopiranje

Konstruktori za kopiranje su specijalna vrsta konstruktora koji prave kopiju postojećeg objekta.

Primer: Konstruktor za kopiriranje klase Vektor izgledao bi: class Vektor

{

… public:

Vektor ( Vektor& v );

};

Vektor::Vektor( Vektor& v )

{

n = v.n;

niz = new double[n];

for ( int i=0; i<n; i++ )

niz[i] = v.niz[i];

}

Destruktori

Destruktor je specijalna funkcija klase koja se implicitno poziva u trenutku brisanja objekta iz memorije. U slučaju statičkih objekata to je trenutak završenja izvršavanja bloka u kojem su definisani, a u slučaju dinamičkih objekata to je trenutak izvršenja operatora delete nad pokazivačem koji ukazuje na taj objekat.

Destruktori su funkcije bez argumenata i bez tipa.

Identifikator destruktora je jednak identifikatoru klase sa prefiksom ~.

Brisanje dinamičkih atributa

Svi podaci (objekti) kreirani u dinamičkoj zoni memorije treba da budu obrisani kada se više u programu ne koriste. Ukoliko se obriše objekat koji sadrži pokazivače na podatke u dinamičkoj zoni memorije, deo dinamičke zone memorije gde su bili zapisani ti podaci biće blokiran do kraja izvršenja programa. Ovaj efekat se u programerskom žargonu naziva “curenje memorije”. Da bi se to sprečilo dinamičke atribute treba brisati pre brisanja samog objekta.

Dinamičke atribute klase obavezno brisati u destruktoru.

Primer destruktora – destruktor klase Vektor

class Vektor

{

int n;

double* niz;

public:

Vektor();

Vektor( int velicina );

Vektor ( Vektor& v );

~Vektor()

{

delete [] niz;

}

}

Pozivi konstruktora za kopiranje prilikom prenosa parametara po vrednosti

U trenutku poziva funkcije koja sadrži argumente koji se prenose po vrednosti, u memoriji se pravi kopija svih tako prenetih argumenata, tj. poziva se konstruktor za kopiranje za sve objekte koji se na taj način prenose funkciji.

Ukoliko u klasi nije definisan konstruktor za kopiranje vrednosti svih atributa objekta koji je naveden kao stvarni argument, biće prepisani u objekat koji se prilikom poziva funkcije kreira.

Ovo je veoma opasno kada klasa sadrži dinamičke atribute (tj. pokazivače na podatke u dinamičkoj zoni memorije).

Zašto?

Zašto?

Pretpostavimo da je negde u projektu definisana funkcija f1 na sledeći način: void f1( Vektor v );

i neka je ona pozvana sa: Vektor a(50); ...

f1(a);

Šta će se desiti prilikom izvršenj ovog dela programa ukoliko u klasi Vektor nije definisan konstruktor za kopiranje ?

Tok dogadaja prilikom izvršenja prethodnog primera

U trenutku poziva funkcije f1 kreira se objekat v tipa Vektor i njegovi atributi uzimaju vrednosti atributa objekta a iz pozivajućeg modula:

v.n =a.n, v.niz = a.niz. Ovo znači da pokazivači a.niz i v.niz ukazuju na isti

deo prostora u dinamičkoj zoni memorije. U trenutku kada se završi izvršenje funkcije f1,

objekat v se briše (poziva se njegov destruktor) i oslobadja se memorijski prostor na koji ukazuju oba pokazivača, i pokazivač u objektu v (koje se briše) i pokazivač u objektu a (koji nastavlja da živi).

Nakon završetka rada funkcije prilikom prvog pokušaja da se pristupi podatku na koji ukazuje a.niz doći će do “pucanja” (“pada”) aplikacije. Ako ne pre, onda prilikom brisanja objekta a.

Zaključak

Kad god u klasi postoje dinamički atributi, obavezno definisati:

destruktor i

konstruktor za kopiranje.

Objektno-orijentisano programiranje

(Vežbe – 5. čas)

Zadatak 3. Konstruktori i destruktori

Na programskom jeziku C++ kreirati klasu Matrica koja sadrži: Privatne atribute: - dimenzije matrice i dinamičku matricu elemenata tipa int. Javne metode: - konstruktor bez argumenata, - konstruktor koji inicijalizuje dimenzije matrice, - konstruktor za kopiranje, - destruktor, - za učitavanje elemenata matrice sa standardnog ulaza, - za prikaz elemenata matrice na standardni izlaz, - za množenje sa matricom odgovarajuće dimenzije. U funkciji main kreirati dva objekta klase Matrica, učitati njihove elemente sa standardnog ulaza i prikazati njihov proizvod na standardni izlaz. NAPOMENA: Za kreiranje klase koristiti class wizard (čarobnjak klasa).

Objektno-orijentisano programiranje

(Vežbe – 6. čas)

Prijateljske funkcije

Često se dešava da neka funkcija definisana van klase (na globalnom nivou ili kao funkcija neke druge klase) ima potrebu da koristi atribute klase. Ako je projektant klase siguran da ta druga funkcija nece poremetiti njegovu koncepciju promene atributa itd. on može dozvoliti da ta funkcija pristupa privatnim članovima klase tako što će u definiciji klase navesti da je odgovarajuća funkcija prijateljska funkcija klase.

Primer: Definisati funkciju na globalnom nivou koja poredi brzine dva

vozila. Funkciju definisati kao prijateljsku funkciju klase Vozilo. class Vozilo

{

...

friend int uporediBrzine( Vozilo& v1, Vozilo& v2 );

...

}

Implementacija funkcije uporediBrzine

int uporediBrzine(Vozilo& v1, Vozilo& v2)

{

if ( v1.max_brzina > v2.max_brzina )

return 1;

if ( v1.max_brzina == v2.max_brzina )

return 0;

return -1;

}

Zadatak 3A. Prijateljske funkcije

Funkciju za računanje proizvoda dve matrice definisati kao samostalnu (globalnu) i kao prijateljsku funkciju klase Matrica.

Prijateljske klase

Ponekad se javlja poreba da veći broj funkcija članica druge klase pristupa privatnim atributima posmatrane klase. Da se ne bi nabrajalo koja sve funkcija druge klase jeste prijateljica nase klase, moze se definisati da je ta druga klasa prijateljska klasa posmatrane klase. Sve funkcije prijateljske klase mogu pristupati privatnim članovima klase.

Primer: class A

{

...

friend class B;

...

}

Konstantne funkcije

Funkcije koje ne menjaju atribute klase treba definisati kao konstantne funkcije.

Funkcija je konstantna ukoliko su na kraju njene deklaracije u definiciji klase navede kljucna rec const.

Nad konstantnim objektom se mogu pozvati jedino konstantne funkcije.

Primer konstantne funkcije

Funkciu uzmiBrzinu u klasi klasi Vozilo definisacemo kao konstantu funkciju. Dodacemo klasi i konstantnu funkciju uzmiMaxBrzinu. class Vozilo

{

… public:

… int uzmiBrzinu() const

{

return brzina;

}

int uzmiMaxBrzinu() const

{

return max_brzina;

}

};

Primer konstantnog objekta

const Vozilo voziloNaIzlozbi(260);

// dozvoljeni pozivi funkcija:

… voziloNaIzlozbi.uzmiBrzinu() … // i

… voziloNaIzlozbi.uzmiMaxBrzinu()…

Objektno-orijentisano programiranje

(Vežbe – 7. čas)

Operatorske funkcije

Primer: Definišimo klasu za predastavljanje kompleksnih brojeva i u njoj dva ima privatna atributa: realan i imaginaran deo broja, konstruktor koji inicijalizuje vrednosti atributa i funkciju za sabiranje sa drugim kompleksnim brojem. class Complex

{

double real;

double imag;

public:

Complex(double r, double i) : real(r), imag(i) {}

Complex saberi( const Complex& c ) {

return Complex( real+c.real, imag+c.imag );

}

};

Operatorske funkcije

Poziv funkcije za sabiranje 2 kompleksna broja, definisane na prethodni nacin bio bi:

Complex a(3.5,-2.7), b(1.4,3.4);

… a.saberi( b ) …

Uobicajeni izraz za sabiranje 2 podatka:

… a+b …

Operatorske funkcije

Promenimo ime funkcije saberi u operator+ .

class Complex

{

double real;

double imag;

public:

Complex(double r, double i)

: real(r), imag(i) {}

Complex operator+( const Complex& c ) {

return Complex( real+c.real,imag+c.imag );

}

};

Poziv operatorske funkcije

Funkcija operator+ se moze pozvati:

kao i svaka druga funkcija clanica klase:

Complex a(3.5,-2.7), b(1.4,3.4);

… a.operator+( b ) … Kao i odgovarajuci operator definisan

u programskom jeziku:

… a+b …

Poziv operatorske funkcije definisane u klasi

Kada je operatorska funkcija definisana

u klasi ona se poziva:

u slucaju binarnih operatora:

nad levim operandom u izrazu;

u slucaju unarnih operatora:

nad jedinim operandom u izrazu.

Sta kada levi operand nije objekat klase koju mi projektujemo?

Primer:

Pretpostavimo da smo napravili klasu za predstavljanje vektora i da zelimo da napravimo operatorske funkcije:

* - koja mnozi skalar i vektor i

>> - za ucitavanje elemenata vektora iz ulaznog

tekstualnog toka podataka.

Ove funkcije ne mozemo definisati kao funkcije klase Vektor. Zbog toga cemo ih praviti kao samostalne (globalne) funkcije.

Samostalne operatorske funkcije

Operatorske funkcije definisane na globalnom nivou imaju onoliko argumenata koliko operanada imaju odgovarajuci operatori i redosled navodjenja argumenata odgovara redosledu navodjenja operanada u izrazu.

Primer samostalnih operatorskih funkcija

class Vektor

{

int n;

double* niz;

public:

Vektor( int velicina );

Vektor( const Vektor& v );

~Vektor();

friend Vektor operator*( double levi,

const Vektor& desni);

friend istream& operator>>( istream& ulaz,

Vektor& v);

};

Primer samostalnih operatorskih funkcija

Vektor operator*( double levi, const Vektor& desni)

{

Vektor rezultat( desni.n );

for ( int i=0; i<desni.n; i++ )

rezultat.niz[i] = levi * desni.niz[i];

return rezultat;

}

istream& operator>>( istream& ulaz, Vektor& v)

{

ulaz >> v.real >> v.imag;

return ulaz;

}

Poziv kreiranih operatorskih funkcija

Vektor a(50);

cin >> a;

Vektor b = 3.5 * a;

U prethodnoj naredbi pozvan je konstruktor za kopiranje jer je izvršena inicijalizacija vektora b.

Da je negde dalje u kodu navedena naredba:

b = 3.5 * a

bilo bi potrebno definisati operator dodele.

Specifičnosti operatora dodele

Dodajmo klasi Vektor operator dodele: class Vektor

{

int n;

double* niz;

public:

Vektor( int velicina );

Vektor( const Vektor& v );

~Vektor();

Vektor& operator=( const Vektor& v );

friend operator*( doble levi,

const Vektor& desni);

friend istream& operator>>( istream& ulaz,

Vektor& v);

};

Specifičnosti operatora dodele

Vektor na levoj i vektor na desnoj strani ne moraju biti iste dužine. Zbog toga, pre prepisivanja elemenata treba obriati niz u vektoru kojem se dodeljuje vrednost, a zatim krierati novi niz odgovarajuće dužine.

Pre nego da obrisemo niz u vektoru na levoj strani, treba proveriti da li se možda i na desnoj strani nije pojavio isti taj vektor.

Specifičnosti operatora dodele

Vektor& Vektor::operator=(const Vektor& v)

{

if ( this != &v )

{

n = v.n;

delete [] niz;

niz = new double[n];

for ( int i=0; i<n; i++ )

niz[i] = v.niz[i];

}

return *this;

}

Specifičnosti operatora ++ i -- Operatori ++ i -- u programskom jeziku C++

mogu biti prefiksni i postfiksni.

Ukoliko operatore ++ i –- definišemo na uobičajeni način (ako su članovi klase kao funkcije bez argumenata, a ako su samostalne funkcije kao funkcije sa jednim argumentom), tako definisan operator će se tretirati kao prefiksni.

U definiciji postfiksnih ++ i –- operatora treba dodati još jedan fiktivni argument tipa int koji se u implementacije nece koristiti, služiće samo da se definiše da se radi o postfiksnom operatoru.

Specifičnosti operatora ++ i -- Dodajmo klasi Compleks prefiksni i postfiksni operator ++, čiji će smisao biti da i realni i imaginarni deo broja poveća za 1. class Complex

{

double real;

double imag;

public:

Complex(double r, double i)

: real(r), imag(i) {}

Complex( const Complex& c )

: real(c.real), imag(c.imag) {}

Complex& operator++();

Complex operator++( int n );

Complex operator+( const Complex& c ) {

return Complex( real+c.real,imag+c.imag );

}

};

Specifičnosti operatora ++ i -- Complex& Complex::operator++()

{

re+=1;

im+=1;

return *this;

}

Complex Complex::operator++( int n )

{

Complex rezultat( *this );

re+=1;

im+=1;

return rezultat;

}

Pravila za pisanje operatorskih funkcija

Ne mogu da se pišu operatorske funkcije za operatore pretprocesora.

Ne mogu da se pišu operatorske funkcije za operatore ?:, ., .* i ::.

Ne mogu da se izmišljaju novi operatori.

Ne mogu da se menjaju asocijativnost, prvenstvo ili vrsta (binarni ili unarni) operatora.

Objektno-orijentisano programiranje

(Vežbe – 5. čas)

Zadatak 3B. Operatorske funkcije

U klasi Matrica: funkciju za učitavanje elemenata matrice sa standardnog ulaza

zameniti prijateljskom operatorskom funkcijom >> za učitavanje elemenata matrice iz bilo kog tekstualnog toka podataka,

funkciju za prikaz elemenata matrice na standardni izlaz zameniti prijateljskom operatorskom funkcijom za upis elemenata matrice u bilo koji tekstualni tok podataka,

funkciju za množenje matrica zameniti operatorskom funkcijom članicom klase,

dodati operatorsku funkciju = za dodeljivanje jedne matrice drugoj.

U funkciji main kreirati dva objekta klase Matrica, učitati njihove elemente sa standardnog ulaza, njihov proizvod dodeliti trećoj matrici i tu matricu prikazati na standardni izlaz.

Objektno-orijentisano programiranje

(Vežbe – 9,10,11. čas)

Nasleđivanje klasa

Pri razmatranju skupa objekata, pogodno je grupisati ih po sličnim atributima i zajedničkim operacijama. Na primer, hoćemo da napravimo klasu koja će predstavljati sve objekte koji služe za transport. Ona bi uključivala objekte aviona, objekte automobila ili čak objekte brodova. Drugim rečima, ona bi objedinjavala klase kopneniTransport, vazdusniTransport i morskiTransport. Nazovimo tu klasu Transport.

Nasleđivanje klasa

Klasa Transport je na vrhu hierarhije. Ona se zove superklasa (roditeljska klasa), dok se klase vazdusniTransport, morskiTransport i kopneniTransport nazivaju podklase.

Atribute klase Transport (npr. maksimalan broj putnika) kao i operacije te klase (transportuj, stani, kreni) su nasledile podklase. Može se takođe reći da su podklase izvedene iz roditeljske klase.

Objekti veoma retko pripadaju totalno različitim grupama – često postoji značajno preklapanje operacija među grupama objekata.

class Transport

{

int brzina;

int stanje_rezervoara;

protected:

void povecajDotokGoriva();

void ukljuciSistemKocenja();

public:

void ubrzaj();

void uspori();

};

Transport.h

Privatni atributi

Implementacioni deo

Interfejs (javni deo) klase

Transport.cpp

void Transport::povecajDotokGoriva(){ } void Transport::ukljuciSistemKocenja(){ } void Transport::ubrzaj(){ povecajDotokGoriva(); } void Transport::uspori(){ ukljuciSistemKocenja(); }

Definicija izvedene klase

Nasleđivanje se vrši tako što se pri deklaraciji klase, posle navođenja imena klase stavi znak “:“ (dve tačke), zatim tip nasleđivanja (private, protected ili public) i ime klase iz koje se vrši nasleđivanje:

(VazdusniTransport.h) class vazdusniTransport : public Transport { int visina; public: povecajVisinu(); smanjiVisinu(); }

VazdusniTransport.cpp void vazdusniTransport::povecajVisinu(){ visina += 10; uspori(); } void vazdusniTransport:: smanjiVisinu(){ visina -= 15; ubrzaj(); } (main.cpp) void main() { vazdusniTransport avion; avion.povecajVisinu(); avion.ubrzaj(); // nasledjena operacija }

Polimorfizam

Normalno, svaki tip vozila funkcioniše na drugačiji način – brod, avion i automobil usporavaju na sasvim drugačiji način. To znači da im se menjaju i implementacioni detalji, tj. svaki od njih bi trebalo da ima različite funkcije za povećanje dotoka goriva i sistema kočenja. Sa druge strane, interfejs bi ostao isti – usporavanje i ubrzavanje vozila.

Polimorfizam omogućava da klasa saopšti da ima isti interfejs kao i osnovna klasa, ali da je ponašanje izvedene klase različito od ponašanja osnovne klase.

Polimorfizam omogućava upravo to – da izvedene klase imaju svoje “verzije” operacija deklarisanih u osnovnoj klasi.

Virtualne funkcije

Polimorfizam se postiže deklarisanjem željenih operacija osnovne klase virtualnim. Ako izvedena klasa ne definiše virtualnu funkciju osnovne klase, ona jednostavno usvaja ponašanje osnovne klase.

Virtualna funkcija je funkcija (operacija) članica za koju se očekuje da bude redefinisana u nasleđenim klasama.

Primer: bazna i izvedena klasa

class Base { public: virtual void NameOf(); // Virtual void InvokingClass(); // Nonvirtual }; void Base::NameOf() { cout << "Base::NameOf\n"; } void Base::InvokingClass() { cout << "Invoked by Base\n"; }

class Derived : public Base { public: void NameOf(); void InvokingClass(); }; void Derived::NameOf() { cout << "Derived::NameOf\n"; } void Derived::InvokingClass() { cout << "Invoked by Derived\n"; }

Primer: glavna f-ja i rezultat

int main() { Derived c; Derived *a = &c; Base *b = &c; b->NameOf(); // Poziv virtualne f-je b->InvokingClass(); // Poziv ne-virtualne f-je a->NameOf(); // Poziv virtualne f-je a->InvokingClass(); // Poziv ne-virtualne f-je } (Rezultat) Derived::NameOf Invoked by Base Derived::NameOf Invoked by Derived

Poziv predefinisane funkcije

Ukoliko je potrebno pozvati predefinisanu funkciju (članicu roditeljske klase), koristi se operator pripadnosti “::”.

U funkcijama izvedene klase predefinisana funkcija f1 se poziva:

<OsnovnaKlasa>::f1()

Transport.h (osnovna klasa)

class Transport

{

int brzina;

int stanje_rezervoara;

public:

void povecajDotokGoriva();

virtual void ukljuciSistemKocenja() {

cout << “Kocenje inicirano” << endl; }

void ubrzaj() {povecajDotokGoriva();}

char* uspori() {ukljuciSistemKocenja();}

};

vazdusniTransport.h i main f-ja class vazdusniTransport : public Transport { public: virtual void ukljuciSistemKocenja(){ Transport::ukljuciSistemKocenja(); cout << “Vazdusne kocnice podignute”; }; }

(main.cpp) void main() { Transport *vozilo; vozilo = new vazdusniTransport; vozilo->koci(); } (Rezultat) Kocenje inicirano Vazdusne kocnice podignute

Kreiranje i brisanje objekata izvedene klase

Kada se kreira objekat izvedene klase, najpre se kreira objekat osnovne klase (za šta se poziva konstruktor osnovne klase), a zatim se kreiraju atributi izvedene klase i izvršava telo konstruktora izvedene klase. Zbog toga prva stavka u delu za inicijalizaciju konstruktora izvedene klase, treba da bude poziv konstruktora osnovne klase. Ukoliko je on izostavljen, automatski se poziva konstruktor bez argumenata.

Kada se briše objekat izvedene klase redosled operacija je obrnut. Poziva se destruktor izvedene klase, a on na kraju implicitno poziva destruktor osnovne klase.

Primer konstruktora i destruktora osnovne i izvedene klase

class Base

{

char* name;

public:

Base(int size)

{

name = new char[size];

}

virtual ~Base()

{

delete [] name;

}

};

class Derived : public Base

{

int* vec;

public:

Derived( int size1,

int size2 )

: Base( size1 )

{

vec = new int[size2];

}

~Derived()

{

delete [] vec;

}

};

Zadatak 4

Na programskom jeziku C++ definisati klasu Krug, pri čemu se za svaki krug pamte koordinate centra i poluprečnik kruga. Ova klase treba da sadrži konstruktor koji postavlja poluprečnik, virtuelnu funkciju za promenu poluprečnika koja treba da poluprečnik postavi na novu zadatu vrednost i virtuelnu funkciju za stampanje poluprecnika na standardni izlaz funkciju.

Definisati klasu ObojeniKrug (javno izvedenu iz klase Krug) koja sadrži zaštićene atribute kojima se kodira boja, konstruktor koji inicijalizuje sve atribute, virtualnu funkciju za promenu poluprečnika koja poluprečnik povećava za zadatu vrednost, i virutuelnu funkciju za stampanje svih atributa na standardni izlaz.

Za krugove čija je boja zelena definisati posebnu klasu ZeleniKrug koja je izvedena iz klase ObojeniKrug. Ova klasa nema default konstruktor, već samo konstruktor sa potrebnim argumentima i konstruktor za kopiranje. U okviru nje definisati operatorsku funkciju operator+=() za povećanje intenziteta boje.

U glavnom programu konstruisati po jedan objekat svake klase i pozvati sve implementirane funkcije.

Čiste virtualne funkcije

Postoje situacije kada je osnovna klasa jednostavno skelet ili okvir, dok se funkcionalnost dobija tek kod izvedenih klasa, tj. Situacije kaba bi svako pisanje tela funkcije u osnovnoj klasi bilo beskorisno, s obzirom da bi i onako bilo zamenjeno u izvedenoj klasi.

Čista virtualna funkcija je virtualna funkcija kojoj nije potrebno telo. Ona u deklaraciji iza imena sadrži notaciju “=0”. virtual void povecajDotokGoriva()=0;

Apstrakne klase

Ukoliko klasa ima bar jednu čistu virtualnu funkciju, ona se naziva apstraktnom.

Apstaktna klasa je klasa koja sadrži čistu virtualnu funkciju ili klasa koja nasleđuje virtualnu funkciju ali je ne definiše. Ne može da se kreira primerak apstaktne klase, mada može da postoji pokazivač ili referenca na apstaktnu klasu.

Postoje dva razloga da se klasa učini apstaktnom. Prvi je da se izbegne pisanje beskorisnih tela funkcija, a drugi da se korisnici spreče da kreiraju primerke te klase. Kada se kreira familija klasa, a samo izvedene klase treba da budu instancirane, treba u osnovnoj klasi koristiti čiste virtualne funkcije.

Osnovna *pok; //pokazivac na objekat klase Glavna

pok = new Osnovna; //nije ispravno

pok = new Izvedena; //ispravno

void uradiNesto(Osnovna & ref); /* funkcija koja uzima referencu na objekat klase Osnovna */

Ljubenovic
Replace
Ljubenovic
Replace
Osnovna

Zadatak 5

Kreirati: Apstraktnu klasu Ntougao koja ima:

privatne atribute: broj temena i vektor duzina stranica,

javne funkcije: konstruktor koji inicijalizuje broj temena, funkciju za izracunavanje obima I cistu virtuelnu funkciju za izracunavanje povrsine mnogougla.

Prijatelljsku funkciju >> za ucitavanje duzina stranica iz tekstualnog toka podataka.

Klasu Trougao javno izvedenu iz klase Ntougao koja nije apstraktna.

Višestruko nasleđivanje

Jednostruko nasleđivanje podrazumeva da izvedena klasa nasleđuje osobine jedne osnovne klase.

Višestruko nasleđivanje znači nasleđivanje osobina od više osnovnih klasa. Time se omogućava kombinovanje i proširivanje karakteristika nekoliko osnovnih klasa u izvedenoj klasi.

Višestruko nasleđivanje

class Izvedena : public Osnovna1, public

Osnovna2

{

...

}

Višestruko nasleđivanje

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

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

Zadatak 6.

Kreirati klasu Krug čiji je privatni atribut poluprečnik kruga, a javne funkcije: konstruktor koji inicijalizuje poluprečnik, funkcija za izračunavanje obima i virtuelna funkcija za izračunavanje površine.

Kreirati klasu Valjak javno iz klase Krug čiji je privatni atribut visina valjka, a javne funkcije: konstruktor koji inicijlizuje sve atribute, destruktor, virtuelna funkcija za izračunavanje površine i virtuelna funkcija za izračunavanje zapremine.

Kreirati klasu Kupa koja sadrži privatni atribut visina i javne funkcije: konstruktor koji inicijlizuje sve atribute, destruktor, virtuelna funkcija za izračunavanje površine i virtuelna funkcija za izračunavanje zapremine.

Zadatak 6.

Kreirati i klasu Visak javno izvedenu iz klasa Valjak i Kupa za predstavljanje tela oblika:

U klasi Visak predefinisati sve potrebne funkcije.

Virtuelne osnovne klase

U nekim slučajevima je

potrebno da imate samo jednu kopiju objeka roditeljske klase. Zato je potrebno da se A učini virtualnom osnovnom klasom. Kada prevodilac gradi klasu, on uzima sve virtualne primerke klase koji spaja u jedinstveni primerak.

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

Virtuelne osnovne klase

Svaki nevirtualni objekat sadrži kopiju članova osnovne klase. Ukoliko ne bi postojala virtualna osnovna klasa, došlo bi do dupliranja prostora, ali i do neodređenosti koja kopija treba da se koristi (U nekim slučajevima je dupliranje poželjno – primer Viska).

Kreiranje objekta virtuelne osnovne klase

Kada je klasa obična osnovna a ne virtualna osnovna, konstruktori izvedene klase kontrolišu koji se konstruktori osnovne klase pozivaju. Ali kada je osnovna klasa virtualna stvari postaju nejasne zato što ima mnogo izvedenih klasa od kojih svaka polaže jednaka prava na osnovnu klasu. Pošto virtualna osnovna klasa treba da se inicijalizuje jednom, potrebno je pravilo za određivanje izvedene klase koja zaista kontroliše aktiviranje konstruktora virtualne osnovne. Rešenje koje je usvojeno u jeziku C++ je da kontrolu ima klasa stvarnog objekta, koja se ponekad zove i najviše izvedena klasa.

Zadatak 6A.

Klasu Krug definisati kao virtuelnu osnovnu klasu za klase Valjak i Kupa, a zatim definisati i klasu Olovka javno izvedenu iz klasa Valjak i Kupa za predstavljanje tela oblika:

NAPOMENA: Korišćenjem debuger-a videti

način kreiranja objekta klase Visak i objekta klase Valjak.

Šabloni (Templates)

Objektno orijentisano programiranje – vežbe: časovi 15, 16, 17

Kako smo došli u situaciju da nam šabloni uopšte trebaju?

Recimo da je potrebno da napišemo funkciju koja će određuje koji je od data dva veći. Ukoliko se radi o celobrojnim brojevima, funkcija bi izgledala:

int max (int i, int j) { return i>j ? i : j; }

Međutim, šta ako umesto celobrojnih imamo realne

brojeve? Onda bi funkcija izgledala ovako:

float max (float i, float j) { return i>j ? i : j; }

Razlike između ove dve funkcije se svode na sistematsko zamenjivanje oznake tipa unutar cele definicije fukcije. To je rutinski posao, i on se može automatizovati korišćenjem šablonskih ili generičkih funkcija.

Primer generičke funkcije za gornji primer bi bio:

template <class T> T max (T i, T j) { return i>j ? i : j; }

Srećom, u jeziku C++ postoji ugrađeni mehanizam za šablone

Šablonske (generičke) funkcije

Prvi red u kodu sa prethodnog slajda: template <class T> definiše klasu T kao formalni argument koji će biti

zamenjen stvarnim argumentom u trenutku korišćenja šablona. Na primer, ukoliko se pozove max(5,8), T će biti tipa int.

Funkcije ili klase opisane pomoću šablona nazivaju se generičke funkcije ili generičke klase. Na osnovu njih se kasnije generišu konkretne funkcije ili klase.

Kako izgledaju i čemu služe šablonske funkcije i klase

Opšti oblik za šablone generičkih funkcija ili klasa je: template < argument, argument, ... > opis Argumenti mogu da označavaju tipove ili konstante. Opšti oblik

argumenata je: class identifikator_tipa (npr. class T) ili oznaka_tipa identifikator_konstante (npr. int k) Opis može da bude deklaracija (prototip) ili definicija generičke

funkcije (odnosno klase) čiji se šablon opisuje. Šabloni mogu značajno da povećaju fleksibilnost i da smanje

veličinu koda.

Generisanje funkcija

Funkcije na osnovu zadatog šablona se generišu: automatski - kad se naiđe na poziv generičke f-je sa stvarnim

argumentima koji mogu da se uklope u šablon bez konverzije tipova argumenata.

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

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

Primer:

float max (float, float); // eksplicitni zahtev za generisanje

float g = max (i, f);

max<float>(i, f);

Primer 1. template <class Type> Type min (Type a, Type b) { return a < b ? a : b; } int main() { // ok: min(int, int) min(10, 20); // ok: min(double, double) min(10.0, 20.0); return 0; }

Napomena: Naziv parametra šablonske funkcije NE MORA da bude Type. Ispravno će raditi i ovakava funkcija: template <class Glorp > Glorp min (Glorp a, Glorp b) { return a < b ? a : b; }

Tipski i netipski parametri šablona

Tipski parametri se navode kao instanca neke nedeklarisane klase (kao na primer class Glorp)

Kada dođe do pravljenja primeraka šablona, tipski parametri (kao onaj class Glorp) zamenjuju se stvarnim tipovima podataka – bilo ugrađenim (int, double, char*) ili korisnički definisanim (transport, vector <int>*)

Netipski parametar šablona sadrži uobičajenu deklaraciju parametara. Netipski parametar šablona označava potencijalnu vrednost. Ova vrednost predstavlja konstantu u definiciji šablona.

Videti primer na sledećem slajdu.

Tipski i netipski parametri šablona - primer

Ovde je class Glorp tipski, a int size netipski parametar. Parametar size je netipski parametar šablona, odnosno konstantna vrednost koja postavlja veličinu niza na koje se arr odnosi: template <class Glorp, int size> Glorp min(Glorp (&arr) [size])

Kada dođe do pravljenja primeraka šablona funkcije min, vrednost size zameniće se konstantnom vrednošću u vreme kompajliranja.

Šablonska funkcija min – ceo kod

template <class Glorp, int size> Glorp min(const Glorp (&r_array)[size]) { /* Parametrizovana funkcija za pronalaženje

minimalne vrednosti u nizu */

Glorp minival = r_array[0]; for(int i = 1; i < size; i++) { if(r_array[i] < minival) minival = r_array[i]; } return minival;

}

Napomene 1 i 2

Objekat ili tip koji su deklarisani u definiciji šablona funkcije ne mogu nositi isti naziv kao i parametar šablona.

template <class Type> Type min (Type a, Type b) { // Pogrešno! Redefinicija šablonskog parametra. typedef double Type; Type tmp = a < b ? a : b; return tmp; }

Naziv tipskog parametra se može koristiti za određivanje tipa rezultata u šablonu funkcije:

template <class Type, class T2, class T3> Type min (T2 a, T3 b)

Napomene 3 i 4

Naziv parametra šablona se može koristiti samo jednom u istoj listi parametara u šablonu. Dakle, sledeći kod je neispravan:

template <class Type, class Type> Type min (Type a, Type b) Međutim naziv parametra šablona može se ponovo koristiti u

deklaracijama ili definicijama drugih šablona. Dakle, sledeći kod je ispravan: template <class Type> Type min (Type a, Type b) template <class Type> Type max (Type a, Type b)

Šabloni klasa

Šablon klase je “uputstvo” za formiranje klase pri čemu su jedan ili više tipova ili vrednosti parametrizovani.

Pretpostavimo da želimo da definišemo klasu koja podržava mehanizam rada sa redom čekanja (engleski Queue). Red čekanja je FIFO struktura.

Nazovimo našu klasu Queue i neka podržava sledeće operacije: proveravanje da li je red pun

bool is_full()

proveravanje da li je red prazan

bool is_empty()

dodavanje elementa u red

void add(item)

izbacivanje elementa iz reda

item remove()

Šabloni klasa - dalje Definicija klase Queue tada može da izgleda ovako class Queue { public: Queue(); ~ Queue(); Type remove(); void add(const Type &newItem ); bool is_empty(); bool is_full(); } Postavlja se pitanje šta da radimo ako Type nije definisano? E, u tom slučaju učinićemo našu klasu templejtskom – vidite sledeći

slajd

Šablonska klasa Queue

template <class Type>

class Queue

{

public:

Queue();

~ Queue();

Type remove();

void add(const Type &newItem);

bool is_empty();

bool is_full();

}

Generisanje raznih klasa Queue na osnovu šablona

Da biste generisali razne klase Queue koje sadrže cele brojeve, kompleksne brojeve i nizove znakova, treba napisati:

Queue <integer> qi; Queue <string> qs; Queue < Complex <double> > cdp; Ovde je jedino neophodno da postoji prethodno

definisana templejtska klasa complex

Templejtske klase, kao i templejtske funkcijem, mogu da sadrže i tipske i netipske parametre

Sve napomene koje su date kod šablonskih funkcija važe i za templejtske klase

Primer još jedne šablonske klase

Deklaracija klase (Vektor.h) template <class T, int k> class Vektor { T element [k]; public: Vektor(); T& operator[] (int i); };

Definicija članova klase (Vektor.h) template <class T, int k> Vektor<T, k>::Vektor () { } template <class T, int k> T& Vektor<T, k>::operator[] (int i) { if(i < k) return element[i]; return 0; }

Zadatak 7. Napisati templejtsku klasu koja modelira niz sačinjen od proizvoljnog broja

elemenata proizvoljnog tipa. Kao privatne podatke ova klasa treba da ima niz elemenata proizvoljnog tipa i broj elemenata tog niza. Kao javne, klasa treba da ima sledeće funkcije: Default konstruktor koji zauzima prostor za 10 elemenata i postavlja broj

elemenata niza na 10 Konstruktor sa jednim celobrojnim parametrom size, koji zauzima prostor za

size elemenata i broj elemenata niza postavlja na size Konstruktor za kopiranje Destruktor Operator = Operator[] za pristup elementima niza Statički operator [] koji će koristiti statički objekti za pristup elementima Int GetSize() – metodu koja vraća broj elemenata niza Prijateljsku funkciju specifičnog tipa Intrude koja pristupa privatnim

elementima niza (samo kada je niz celobrojan) i štampa ih Prijateljsku operatorsku funkciju << koja štampa sadržaj niza bez obzira na

tip njegovih elemenata.

Nastavak na sledećem slajdu

Zadatak 7 – nastavak

Takođe, kreirati klasu Animal koja od privatnih podataka ima težinu životinje, a od javnih sledeće metode: Default konstruktor koji težinu životinje na 0 Konstruktor sa jednim celobrojnim argumentom koji težinu

životinje postavlja na prosleđenu vrednost Destruktor Funkciju int GetWeight(), koja vraća težinu životinje Funkciju void SetWeight(wt), koja postavlja težinu životinje na

prosleđenu vrednost wt Funkciju Display() koja štampa vrednost težine životinje Operator << koji štampa vrednost težine životinje.

U glavnom programu specijalizovati templejt i testirati sve njegove metode. Za celobrojne podatke Za klasu Animal

Ulaz / izlaz

Objektno orijentisano programiranje – vežbe: časovi 18, 19, 20

Ulaz/Izlaz – Tokovi

VEROVALI ILI NE: Ulaz i izlaz podataka nije deo jezika C++, već postoji odgovarajuća biblioteka klasa za rad sa ulazom/izlazom.

Datoteke u C++-u su samo dugački nizovi bajtova i nazivaju se tokovima. Nema suštinske razlike između toka na disku (datoteke) i toka u operativnoj memoriji.

Rad sa tokovima u C++ realizuje se odgovarajućim klasama. Konkretni tokovi su primerci (objekti) tih klasa. Većina operacija nad tokovima je ista, bez obzira gde su oni smešteni.

Hijerarhija klasa koje se koriste za Ulaz/Izlaz u jeziku C++

Dve najvažnije klase koje se izvode iz klase iostream su:

fstream, za rad sa tokovima na disku (datoteke)

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

Klase za rad sa datotekama

Generalno, za rad sa datotekama postoje tri klase:

ifstream, samo za ulazne datoteke

ofstream, samo za izlazne datoteke

fstream, za kombinovani ulaz i izlaz

Klase za rad sa tokovima

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

istrstream, za uzimanje podataka iz tokova

ostrstream, za smeštanje podataka u tokove

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

Standardni tokovi

Postoje 4 standardna toka (primerka klase ostream i istream) koji se automatski formiraju na početku izvršavanja svakog programa: cin – glavni (standardni) ulaz tipa istream (preciznije objekat klase

istream_withassign, videti sliku sa slajda 3) . Standardno predstavlja tastaturu, ukoliko se drugačije ne specificira (da se izvrši skretanje glavnog ulaza unutar samog programa ili u komandi operativnog sistema za izvršavanje programa).

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

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

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

NAPOMENA

Biblioteka “stdio.h” jezika C i dalje može da se koristi za ulaz/izlaz podataka.

Klase C++ su efikasnije.

Nikako se ne preporučuje da se koriste obe vrste ulazno-izlaznih biblioteka

Ulazni tok

Klase za ulazne tokove

Tri najvažnije klase za ulazne tokove su istream, ifstream, i istrsteam.

Klasa istream je najbolja za rad sa sekvencijalnim tekstualnim ulazom.

Klasa ifstream podržava ulaz iz datoteke.

Konstruisanje objekata ulaznih tokova

Konstruisanje objekata ulaznih tokova

Ukoliko koristite cin objekat, ne treba da se napravi ulazni tok.

Ulazni tok se mora napraviti ukoliko koristite:

tok iz datoteke (File stream)

tok iz niza (String stream)

Konstruktori ulaznih tokova datoteka

Postoji tri načina da se napravi ulazni tok iz datoteke:

korišćenjem konstruktora bez argumenata

navođenjem naziva datoteke i odgovarajućih parametara

navođenjem deskriptora datoteke

1. način

Korišćenjem konstruktora bez argumenata se kreira objekat klase ifstream, a zatim se poziva funkcija open koja otvara navedenu datoteku:

ifstream f; // na steku

f.open ( "test.txt", iosmod);

ili

ifstream* f = new ifstream;// na heap-u

f->open("test.txt", iosmode);

2. način

Navođenje naziva datoteke i odgovarajućih parametara (mode flags) se vrši na sledeći način:

ifstream f("test.txt", iosmode);

3. način

Navođenje deskriptora datoteke se vrši za datoteku koja je već otvorena. Standardni (default) način za postizanje navedenog je:

int fd = _open("test.txt", dosmode);

ifstream f(fd);

Za parametre iosmode i dosmode se koriste sledeće vrednosti:

ios::app upisivanje na kraj datoteke

ios::ate pozicioniranje na kraj datoteke posle otvaranja

ios::in otvara ulaznu datoteku

ios::out otvara izlaznu datoteku

ios::nocreate otvara datoteku ukoliko već postoji

ios::noreplace otvara datoteku ukoliko već ne postoji

ios::trunc otvara datoteku i briše stari sadržaj

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

Konstruktori ulaznih tokova

Konstruktor ulaznog toka niza zahteva adresu prethodno alocirane i inicijalizovane memorije:

char s[] = "123.45";

double amt;

istrstream myString( s );

myString >> amt; // Amt = 123.45

Operacije ulaznog toka

Operator ekstrakcije (>>) je programiran za sve standardne C++ tipove podataka, i predstavlja najlakši način da se preuzmu bajtovi iz objekta ulaznog toka.

char ime[20];

cin >> ime;

Operator >> pamti ulazne podatke samo do prvog unešenog blanko znaka. Za unos “Haroon al Rashid" će se dobiti samo “Haroon".

Operacija get

Operacija get se ponaša kao i operator >>, osim što pamti i blanko znakove. Postoji više prototipova ove funkcije, navešćemo samo najčešće korišćene.

istream& get(char& znak); - uzima sledeći znak iz ulaznog toka i smešta ga u promenljivu znak (npr. cin.get(c)).

istream& get(char* niz, int max); - čita max broj znakova

(ukoliko postoji toliko znakova) i smešta ih u niz (npr. cin.get(ime, 20) ).

istream& get(char* niz, int max, char kraj); - čita sve

znakove do prvog pojavljivanja znaka kraj. Pored toga, može da pročita najviše max broj znakova (npr. cin.get(ime, 20, '\n') ).

Operacija getline je veoma slična operaciji get, jedino što uklanja znak prekida (npr. '\n'), dok get ne uklanja.

Operacija read

Operacija read čita bajtove iz toka u određeni deo memorije. Mora se navesti lokacija gde se upisuju pročitani podaci, kao i broj pročitanih bajtova. Na primer, ukoliko imamo sledeću strukturu:

struct Radnik { char ime[20]; double plata; };

i hoćemo da pročitamo iz (već napravljene) datoteke "plata.dat" jedan slog (strukture Radnik), prvo bi otvorili tok iz te datoteke:

ifstream is( "plata.dat", ios::binary | ios::nocreate );

Operacija read (2)

Ukoliko je datoteka uspešno otvorena, vrši se čitanje podataka iz toka koji se upisuju u strukturu r, i odmah zatim i štampanje strukture r:

if( is ) { Radnik r; is.read( (char *) &r, sizeof(r) ); cout << r.ime << ' ' << r.plata << endl; }

Konačno, otvoreni tok treba zatvoriti: is.close();

Ukoliko se ne navede broj bajtova koji treba pročitati, onda će čitanje prestati kada se dođe do kraja datoteke.

NAPOMENE

Operacije get i getline se koriste za ulazne tokove tekstualnih datoteka, a read i za ulazne tokove binarnih datoteka.

Tokovi datoteka pamte pokazivač na poziciju u datoteci koja će biti pročitana sledeća. Vrednost tog pokazivača se može odrediti pomoću operacije seekg:

ifstream is( "plata.dat", ios::binary | ios::nocreate ); is.seekg (8); // 8 bajtova

Vrednost pokazivača se može dobiti pomoću operacije tellg (npr. is.tellg() ).

Preklapanje operatora ekstrakcije

Preklapanjem (overloading) operatora >> za neku klasu, promeniće se funkcionalnost tog operatora u slučaju upotrebe te klase. Primer, imamo klasu za datum:

class Datum { public: int dan, mesec, godina; friend istream& operator>> ( istream& is, Datum& dt ); };

istream& operator>> ( istream& is, Datum& dt ) { is >> dt.dan >> dt.mesec >> dt.godina; return is; } Datum dt; cin >> dt;

IZLAZNI TOK

Izlazni tokovi

Tri najvažnije klase za izlazne tokove su ostream, ofstream i ostrstream.

Veoma retko se konstruišu objekti klase ostream, već se koriste predefinisani objekti (cout, cerr, clog).

Klasa ofstream podržava rad sa datotekama. Za kreiranje niza u memoriji treba koristiti klasu

ostrstream.

Konstruisanje objekata izlaznih tokova

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

Upotreba operatora umetanja

Operator umetanja (<<) je programiran za sve standardne C++ tipove podataka, i služi za slanje bajtova objektu izlaznog toka.

cout << "Ovo je test"; On takođe radi sa predefinisanim manipulatorima

(elementima koji menjaju formatiranje, npr. endl);

cout << "Ovo je test" << endl; U ovom primeru manipulator endl predstavlja znag

za prelazak u novi red.

Formatiranje izlaza Za određivanje jednake širine izlaza, koristi se manipulator setw ili

operacija width. double values[] = { 1.23, 35.36, 653.7, 4358.24 }; for( int i = 0; i < 4; i++ ) { cout.width(10); cout << values[i] << '\n'; }

U gornjem primeru se pri ispisivanju vrednosti svaki red dopunjava

blanko znakovima do 10 znakova. Ukoliko želimo da se vrši popuna nekim drugim znakom, onda se koristi operacija fill.

for( int i = 0; i < 4; i++ ) { cout.width( 10 ); cout.fill( '*' ); cout << values[i] << endl }

Formatiranje izlaza (2)

Ukoliko se koristi setw sa argumentima, vrednosti se štampaju u poljima iste dužine. U tom slučaju se mora uključiti i IOMANIP.H datoteka.

double values[] = { 1.23, 35.36, 653.7, 4358.24 };

char *names[] = { "Zoot", "Jimmy", "Al", "Stan" };

for( int i = 0; i < 4; i++ )

cout << setw( 6 ) << names[i]<< setw( 10 ) << values[i] << endl;

Ovim bi se dobile dve kolone, u jednoj bi bila ispisana imena, a u drugoj vrednosti.

Operacije izlaznog toka

Kao i kod ulaznog toka, i ovde se koriste iste operacije za otvaranje i zatvaranje toka (open i close).

Za rad sa karakterima se koristi operacija put. Ona smešta zadati znak u izlazni tok. Na primer:

cout.put ('A'); daje isti rezultat kao i cout << 'A';

Funkcija write

Funkcija write se koristi za upis bloka memorije u izlazni tok. Ona ima dva argumenta: prvi, char pokazivač i drugi, broj bajtova za upis. Treba napomenuti da je obavezna konverzija u char* pre adrese strukture ili objekta. Funkcija write se koristi i za binarne datoteke.

Primer za upis strukture Radnik (vidi 5.1.4) u binarnu datoteku:

Radnik r; ofstream os("plata.dat", ios::binary); os.write((char*) &r, sizeof(r)); os.close();

Funkcije seekp i tellp su gotovo identične funkcijama seekg i tellg, jedino što se koriste za izlazne tokove.

Preklapanje operatora umetanja

Preklapanje operatora umetanja (<<) za neku klasu dovodi do drugačijeg format ispisivanja u izlazni tok. Može se koristiti kao sredstvo za formatiranje izlaza. Koristi se zajedno sa manipulatorima.

Primer preklapanja operatora << za klasu Datum dat je na sledećim slajdovima

Klasa Datum class Datum { public: int dan, mesec, godina; Datum (int d, int m, int g){ dan = d; mesec = m; godina = g; } friend ostream& operator<< ( ostream& os, Datum& dt ); }; ostream& operator<< ( ostream& os, Datum& dt ) { os << dt.dan << "." << dt.mesec << "." << dt.godina;

return os; }

main funkcija

void main()

{

Datum dt(4,11,2004);

cout << dt;

}

Izuzeci

Objektno orijentisano programiranje

Izuzeci

Tokom izvršenja programa, računar može da naiđe na dva tipa situacija: na one koje je spreman da obradi i na one koje nije spreman da obradi.

Sledi primer jednostavnog programa za množenje dva broja.

Primer #include <iostream>

int main()

{

double a, b, c;

cout << "Unesite dva broja \n";

cout << "Prvi broj: ";

cin >> a;

cout << "Drugi broj: ";

cin >> b;

// Mnozenje dva broja i prikaz rezultata

c = a * b;

cout << "\n" << a << " * " << b << " = " << c << "\n\n";

return 0;

}

Kako se došlo do izuzetaka?

Sve radi kako treba ukoliko korisnik ne napravi grešku pri unošenju parametara i recimo unese niz karaktera umesto broja. Šta onda?

Pre nego što je uveden sistem obrade izuzetaka, programi definitivno nisu bili bez grešaka. Postoji nekoliko metoda za obradu grešaka koje su se tada koristile, a objasnićemo samo jednu da bi mogli videti prednost sistema obrade izuzetaka.

U starijim programima, svaka važnija funkcija vraća povratnu vrednost koja označava kako je izvršenje prošlo. Kod koji je pozvao funkciju proverava povratnu vrednost i zavisno od toga sprovodi određene akcije. Posledica – programski kod je prepun dodatnim if–naredbama i delovima koda za obradu greške. Takođe, povratna vrednost funkcije ne može da se koristi za vraćanje ničeg drugog sem koda greške.

Koncepti obrade izuzetaka

Globalni koncept obrade izuzetaka je jednostavan.

Ideja je da se postave "zastavice za greške" (error flags) svaki put kada nešto krene naopako.

Takođe postoji sistem koji non-stop nadgleda te zastavice.

Ukoliko je zastavica postavljena, taj sistem poziva kod za obradu greške.

Postavljanje zastavice za grešku se naziva generisanje (eng. raising, throwing) greške.

Koncepti obrade izuzetaka (2)

Kad se generiše greška, ceo sistem reaguje hvatanjem (eng. catching) greške.

Uokviravanje bloka (dela) koda “osetljivog” na greške kodom za obradu izuzetaka se naziva pokušavanje (eng. trying) izvršenja bloka.

Kod za obradu izuzetaka se naziva try-kod.

Koncepti obrade izuzetaka (3)

Jedna od najmoćnijih osobina obrade izuzetaka je to što greška može da se generiše i van granica funkcije.

To znači da ukoliko jedna od najdubljih funkcija na steku (npr. rekurzivni pozivi funkcije) generiše grešku, ta greška može da se propagira do funkcija iznad u hierarhiji, ukoliko postoji try-kod.

To omogućava programerima da postave kod za obradu greške samo na jednom mestu, npr. u glavnoj (main) funkciji.

Sintaksa

Sistem za obradu izuzetaka u C++ jeziku koristi try, catch i throw naredbe.

try

{

// kod osetljiv na greške }

catch (<tip1> &e1)

{

// obrada izuzetka

}

catch (<tip2> &e2)

{

// obrada izuzetka

}

Tok izvršenja programa - 1

Kontrola se predaje try naredbi. Štićeni deo koda u okviru try bloka se izvršava.

Ukoliko ni jedan izuzetak nije generisan tokom izvršenja, catch blok se ne izvršava.

Ukoliko se generiše izuzetak tokom izvršenja štićenog dela ili u okviru bilo koje rutine, potprograma koji je pozvan u tom delu (direktno ili indirektno), kreira se objekat izuzetka i to iz objekta kreiranog od strane throw operanda. Kompajler traži odgovarajuću catch klauzulu koja ume da obradi izuzetak (u pogledu tipa izuzetka). Catch handler-i (klauzule koje odbrađuju izuzetke) se ispituju onim redom kojim se javljaju i u try bloku.

Tok izvršenja programa - 2

Ukoliko se ne nađe ni jedan handler, poziva se predefinisana funkcija terminate.

Ukoliko se nađe odgovarajući catch handler, memorijska (stek) se oslobađa automatski generisanih objekata, kreiranih između početka aktuelnog try bloka i mesta podizanja izuzetka. Destruktori se pozivaju u obrnutom redu u odnosu na konstruisanje (prvo se uništava zadnje napravljeni objekat). Zatim se izvršava catch handler, a potom se nastavlja sa izvršenjem programa, i to od od prve naredbe napisane ispod svih catch handlera.

Primer: #include <iostream>

using namespace std;

int main()

{

char *buf;

try

{

buf = new char[512];

if( buf == 0 )

throw “Memorija nije rezervisana!"; }

catch( char * str )

{

cout << “Prijavljen izuzetak: " << str << '\n'; }

// ...

return 0;

}

Zadatak 1.

Kreirati klasu Vektor koja modelira niz brojeva tipa double sa zadatim opsezima indeksa. Za rešavanje konfliktnih situacija koristiti mehanizam izuzetaka. Privatni atributi klase su:

Donja granica indeksa,

Gornja granica indeksa,

Veličina vektora i Dinamički niz elemenata tipa double. Javni članoivi klase su: Konstruktor bez argumenata,

Konstruktor koji inicijalizuje samo gornju granicu indeksa (podrazumevana donja je 1),

Konstruktor koji inicijalizuje obe granice indeksa,

Zadatak 1.

Konstruktor za kopiranje,

Destruktor,

Operator =,

Operator za pristup elementima niza

Prijateljski operator * za nalaženje skalarnog proizvoda dva vektora,

U funkciji main, kreirati dva objekta klase Vektor, učitati njihove elemente sa standardnog ulaza, izračunati njihov skalarni proizvod i prikazati ga na standardni izlaz.

Zadatak 2

Kreirati šablonsku klasu Vektor čiji su argumenti veličina vektora i tip njegovih elemenata. U privatnom delu klase definisati niz elemenata, a u javnom: funkciju za nalaženje minimalnog elementa vektora, funkciju za učitavanje elemenata vektora iz binarne

datoteke zadatog imena (ukoliko ulazna datoteka ne može biti otvorena prijaviti izuzetak),

funkciju za učitavanje elemenata vektora sa standardnog ulaza i

funkciju za upis elemenata vektora u tekstualnu datoteku zadatog imena.

Zadatak 2.

Kreirati i klasu RacionalaBroj. U privatnom delu

definisati atribute brojilac i imenilac, a javnom

sledeće funkcije: operator < koji, ukoliko je neki od brojeva koji se

porede neodredjen (broj je neodredjen ako je imenilac=0) prijavljuje izuzetak i

prijateljske funkcije:

operator >> za učitavanje racionalnog broja iz tekstualnog toka podataka i

operator << za upis racionalnog broja u tekstualni tok (u formatu brojilac/imenilac).

Zadatak 2.

U funkciji main iz datoteke INTVEK.dat učitati elemente celobrojnog vektora dužine 10, upisaati njegove elemente u tekstualnu datoteku INTVEK.txt, a minimalni element vektora prikazati na standardni izlaz. Ukoliko ulazna datoteka ne bude korektno otvorena ulazne podatke učitati sa standardnog ulaza.

Zatim kreirati vector racionalnih brojeva dužine 15, učitati njegove elemente iz datoteke RBVEK.dat, upisati njegove elemente u datoteku RBVEK.txt, a minimalni element na standardni izlaz. Ukoliko datoteka RBVEK.dat ne može biti otvorena, elemente vektora učitati sa standardnog ulaza. Ukoliko u vektoru ima nedefinisanih elemenata, štampati poruku o tome.

top related