Prikazujem rezultate 1 do 2 od 2

Tema: C++ i stukturno programiranje

  1. #1
    Super Moderator Fight fire with fire marijanovic's Avatar
    Datum registracije
    Dec 2005
    Lokacija
    Virovitica
    Postova
    10.751

    C++ i stukturno programiranje

    1. UVOD U C++
    1.1 Strukturno programiranje i C++
    U programiranju danas dominiraju dva osnovna koncepta : koncept strukturnog
    programiranja i koncept objektno-orijentiranog programiranja. Pritom objektno orijentirano
    programiranje predstavlja nadgradnju koncepta strukturnog programiranja, obogacujuci ga
    novim mogucnostima i novim pristupom programiranju.

    Tri osnovna koncepta strukturnog programiranja su :


    stroga hijerarhijska struktura programa (što znaci da je upotreba naredbe GO TO
    nepoželjna, ili barem svedena na minimum),

    odvajanje definicije podataka od njihove obrade (odnosno, postoje odjeljci u programu za
    definicije podataka i odjeljci za njihovu obradu) i


    potprogrami. Logicke cjeline unutar programa izdvajaju se u potprograme s tocno
    odredenom zadacom, tako da se isti programski kod može pozivati iz razlicitih dijelova
    programa.
    1.2 Odnos izmedu C i C++
    Kao što je C strukturno-orijentirani programski jezik, tako je C++ (uz Javu, koja, medutim,
    nasljeduje vecinu koncepata i sintaksu od C++) najtipicniji objektno-orijentirani programski jezik.
    C++ predstavlja proširenje programskog jezika C, u kojeg uvodi koncept objektno-orijentiranog
    programiranja. Jezik C ostao je i dalje ukljucen u jezik C++, kao njegov podskup. Kao takav,
    C++ podržava oba programska koncepta : koncept strukturnog programiranja i koncept
    objektno-orijentiranog programiranja.

    1.2.1 Komentari
    Osim oznaka /* i */ iz C-a koje predstavljaju pocetak, odnosno kraj bloka koji predstavlja
    komentar, C++ uvodi i znak // koji oznacava red izvornog koda programa kao komentar. Na
    primjer :

    {
    // Ovo je komentar!
    }



    1.2.2 Dodjela tipa podataka
    U C-u se dodjela tipa vrši tako da se tip navede u zagradi ispred naziva varijable, kao u
    slijedecem primjeru :

    int i; float f;
    f = (float) i;

    C++ dopušta i drugi nacin, po uzoru na poziv funkcije :

    int i; float f;
    f = float (i);

    1.2.3 Ulaz i izlaz
    Ulaz i izlaz je u C++ riješen pomocu ulaznih i izlaznih tokova. Tokovi su klase definirane
    u standardnim bibliotekama, koje omogucuju komunikaciju programa s vanjskim jedinicama (npr.
    tipkovnicom i ekranom).

    Ispis pomocu cout

    Jedna od najocitijih razlika izmedu C i C++ je zamjena standardne biblioteke stdio
    bibliotekom iostream. Biblioteka iostream zamjenjuje sve mogucnosti biblioteke stdio, odnosno,
    tri programska retka iz slijedeceg primjera daju identican rezultat :

    printf ("Danas je lijep dan.\n"); // C
    cout << "Danas je lijep dan.\n"; // kombinirano rješenje
    cout << "Danas je lijep dan." << endl; // C++

    Znakovi “<<” predstavljaju operator umetanja u tok, jer se njime podatak umece u tok. S
    desne strane se može naci bilo koji tip podatka: char, short, int, long int, char *, float, double,
    long double, void * . Tako vrijede sljedece operacije:

    cout<<9.3<<endl; //float
    cout<<615 <<endl; //int
    cout<<"Ovo je ispis znakovnih nizova"<<endl; //char *

    Za formatiranje izlaza mogu se koristiti manipulatori, koji su definirani unutar biblioteke iomanip.

    Primjer :

    float pi=3.14159;
    cout << setprecision(4) << pi << endl; // ispisuje se 3.141


    Postoji više manipulatora: setw(int), dec, hex, oct, flush, endl...


    Ucitavanje pomocu cin

    Ulazom se upravlja na slican nacin kao s ispisom, s tim da se koristi ulazni tok cin i
    operator izlucivanja iz toka (>>) jer se njime podatak izlucuje s izlaznog toka.

    Primjer :

    scanf ("%f",&pi); // C
    cin >> pi; // C++


    S desne strane se može naci bilo koji tip podatka: char, short, int, long int, char *, float, double,
    long double, void * . Tako vrijede sljedece operacije:

    int i;
    lon l;
    float f;
    double d;

    cin>>i; //int
    cin>>l; //long int
    cin>>f; //float
    cin>>d; //double

    Uspješnim ucitavanjem podatka operator izlucivanja kako rezultat vraca referencu na objekt tipa
    istream. Zbog toga se upis može ulancavati pa možemo napisati:

    cin>>i>>l>>f>>d;

    Datotecni ulaz/izlaz

    Umjesto biblioteke stdio koristi se biblioteka fstream (takoder se mogu koristiti ifstream i
    ofstream). Slijedeci primjer u C-u upisuje dva reda teksta u izlaznu tekstualnu datoteku :

    FILE *dat;

    dat = fopen ("tekst1.txt","wt");

    fprintf (dat,"%s","Prvi red teksta!\n");

    fprintf (dat,"%s","Drugi red teksta!\n");

    fclose(dat);

    Odgovarajuci primjer u C++ :

    fstream dat;
    dat.open ("tekst.txt",ios:ut);
    dat << "Prvi red teksta!" << endl;
    dat << "Drugi red teksta!" << endl;
    dat.close();


    U drugom primjeru definiran je datotecni objekt dat, iz klase fstream. Za otvaranje datoteke
    korišten je funkcijski clan open. Datoteka je otvorena u modu out (izlazna datoteka). Tekst se u
    datoteku upisuje korištenjem operatora umetanja u tok (<<). Da smo željeli citati iz datoteke
    koristili bi mod ios::in.


    Deklaracije varijabli

    Varijable se u C++ deklariraju na isti nacin kao i u C-u, ali mogu biti deklarirane u bilo kojem
    dijelu programskog koda.

    Primjer:

    {
    int a;
    ... programski kod ...
    float b;
    ... programski kod ...
    char c;
    ... programski kod ...
    }


    Konstante

    U C-u se za definiranje konstanti koristi pretprocesorska naredba #define. Na primjer:

    #define UKUPNO 50

    U tu svrhu u C++ se može koristiti i kljucna rijec
    const :

    const UKUPNO = 50;

    Takoder, C++ dopušta da pojedini argumenti funkcija budu konstantni, time se sprecava
    promjena njihove vrijednosti unutar funkcije. Primjer :

    void funkcija (const int a)

    {
    a=10; // Greška; ne može se promijeniti konstanta
    }


    Preopterecenje funkcije

    C++ omogucuje da više funkcija ima isti naziv, pod uvjetom da su im liste parametara razlicite.
    Prema listi parametara kod poziva funkcije odreduje se koja ce funkcija biti pozvana.

    Primjer :

    #include <iostream.h>
    void funkcija(){
    cout << "Funkcija bez parametara!" << endl;
    }
    void funkcija(int a){
    cout << "Cjelobrojni parametar a = " << a << endl;
    }
    void funkcija(float b, float c){
    cout << "Realni parametar b = " << b << endl;
    cout << "Realni parametar c = " << c << endl;
    }



    void main(){
    funkcija();
    funkcija(10);
    funkcija(2.71,3.14);
    }

    Ispisuje se :

    Funkcija bez parametara!
    Cjelobrojni parametar a = 10
    Realni parametar b = 2.71
    Realni parametar c = 3.14

    Podrazumijevani argumenti funkcija

    Prilikom poziva funkcije potrebno je navesti listu argumenata koja odgovara listi argumenata
    u zaglavlju funkcije. C++ dopušta da se neki od argumenata u pozivu funkcije izostave, tako
    da se umjesto njih koriste podrazumijevane vrijednosti.
    Primjer:


    #include <iostream.h>
    void funkcija(int a, int b=5){
    cout << "a = " << a << endl;
    cout << "b = " << b << endl << endl;
    }
    void main(){
    funkcija (3,4);
    funkcija (10);
    }


    Ispisuje se :

    a=3
    b=4
    a = 10
    b=5

    U drugom pozivu funkcije izostavljen je drugi argument, umjesto kojeg se koristi
    podrazumijevani (b=5).

    Alokacija memorije

    C++ zamjenjuje C-ovu funkciju za alokaciju memorije malloc i funkciju za dealokaciju
    memorije free s new i delete. New i delete omogucuju alokaciju korisnicki definiranih tipova
    jednako kao i preddefiniranih.

    C:
    int *pok;
    pok = (int*)malloc(sizeof(int));
    *pok = 10;
    free (pok);


    C++ :

    int *pok;
    pok = new int;
    *pok = 10;
    delete pok;


    Deklaracije referenci

    U C-u se cesto koriste pokazivaci za prosljedivanje argumenata funkcijama. U C++ može se
    koristiti referentni operator (&) u listi argumenata, što cini programski kod cišcim.

    C:
    void zamjena (int *a, int *b){
    int t = *a;
    *a = *b;
    *b = t;
    }
    void main(){
    int x = 3, y = 5;
    zamjena (&x, &y);


    printf ("%i %i\n",x,y);
    }


    C++:

    void zamjena (int &a, int &b){
    int t = a;
    a = b;
    b = t;
    }
    void main(){
    int x = 3, y = 5;
    zamjena (x,y);
    cout << x << y << endl;;
    }


    Takoder, moguce je referenci varijable pridružiti varijablu, kao u slijedecem primjeru :

    int a = 0;
    int &b = a; // varijable a i b zauzimaju isti memorijski prostor
    b = 5;
    cout << a << endl; // ispisuje se 5



    2. OBJEKTI I KLASE
    2.1. Uvod u objektno programiranje
    Objektno orijentirano programiranje: Postupak izrade aplikacija u kojem se svojstva
    apstraktnih ili stvarnih objekata modeliraju programskim klasama i objektima. U stvarnom svijetu
    okruženi smo objektima (automobil, racunalo, pas, drvo, ...). Svaki objekt definiran je stanjem i
    ponašanjem. Npr. za automobil stanje odreduje trenutna brzina, kolicina goriva u spremniku i sl.,
    a ponašanje može biti ubrzavanje, kocenje, skretanje i sl.

    Objekt u programskom okruženju je skup varijabli i pripadnih metoda. Varijable odreduju
    stanje, a metode ponašanje objekta. Varijable objekta nazivaju se i varijable primjerka (instance
    variables) – svaki primjerak (instanca) odredenog objekta sadrži vlastitu kopiju varijabli
    primjerka. Metode mijenjaju stanje objekta, a po potrebi mogu stvarati i nove objekte.

    Objekti medusobno razmjenjuju informacije i traže jedan od drugog usluge. Pritom
    okolina ne mora znati ništa o unutanjem ustrojstvu objekta. Okolina komunicira sa objektom
    preko javnog sucelja (engl. public interface). Nacin na koji se ostvaruje reprezentacija objekta
    jest implementacija objekta (engl. implementation) i ona se najcešce skriva od okoline da bi se
    osigurala konzistentnost objekta. Klasa se dakle sastoji od opisa javnog sucelja i od
    implementacije. Objedinjavanje sucelja i implementacije naziva se enkapsulacija (engl.
    encapsulation).

    Klasa je nacrt objekta, odnosno predložak po kojem je odredeni objekt stvoren. Npr.
    odredeni tip automobila definiran je jednim nacrtom (odnosno skupom nacrta). Na temelju
    jednog nacrta moguce je proizvesti više primjeraka istog tipa automobila (tj. objekata), koji ce se
    medusobno razlikovati po stanju i ponašanju. Podskup stanja i ponašanja (varijabli i metoda)
    može biti zajednicki svim objektima odredene klase. Nazivamo ih varijablama i metodama klase.

    Sintaksa za kreiranje klase u C++ je vrlo slicna sintaksi za kreiranje strukture u C-u:

    Struct datum
    {
    int dan, mjesec, godina;
    };


    Ali pri kreiranju klase osim varijabli definiramo i metode kojima se objekt služi kako bi pristupio
    svojim varijablama i mijenjao ih.

    class datum

    {

    int dan; // Varijable koje se koriste za pohranu datuma

    int mjesec;

    int godina;

    public: // Metode klase

    datum(); //constructor

    datum(int,int,int);

    void postavidatum (int, int, int); //prototip funkcije

    void pokazi(); //function prototype


    Kada definiramo klasu možemo definirati i njene metode. Definiranje metode ima sljedeci format:

    Povratni tip ime klase::ime metode (parametri) {
    Tijelo metode
    }

    Slijedi definicija metoda za naš primjer:

    void datum:ostavidatum (int dd., int mm, int yy)

    {
    dan=dd;
    mjesec=mm;
    godina=yy;

    }

    void datum:okazi (void)
    {
    cout<<”Dan:”<<dan<<”\n”<<”Mjesec:”<<mj esec<<”\n”<<”Godina:”<<godina;
    }

    2.2. Konstruktori
    Konstruktor je posebna metoda, cije ime je jednako imenu klase, koja se automatski
    izvršava u trenutku stvaranja objekta. Najcešce se koristi za inicijalizaciju varijabli objekta. U
    pravilu se definira više konstruktora.

    datum::datum ()

    {
    dan=1;
    mjesec=1;
    godina=2007;

    }

    U gornjem primjeru konstruktor inicijalizira datum na 1.1.2007. godine.

    -Svaka klasa mora imati najmanje jedan konstruktor.
    -Ime konstruktora mora biti jednako imenu klase.
    -Iako nije prikazano u gornjem primjeru konstruktor može primati i parametre.

    -Konstruktor ne vraca nikakvu vrijednost (cak niti void).
    -Objekt se može stvoriti samo jednom kao što se i varijabla samo jednom može definirati.



    2.3. Stvaranje objekta
    Nakon što smo definirali klasu možemo stvoriti pojedinacne objekte tako da navedemo ime klase
    kojoj objekt pripada i ime objekta:

    datum danas;

    -Kreirali smo objekt klase datum koji se zove danas
    -Buduci da je objekt danas instanca klase datum pri njegovoj se deklaraciji automatski
    poziva konstruktor koji postavlja vrijednost datuma na 1.1.2007.

    -Objekt danas izgleda ovako:

    danas
    dan=1
    mjesec=1
    godina=2007
    void postavidatum(int,int,int)
    void pokazi()

    -Kao i kod struktura možemo pristupati pojedinacnim varijablama objekta koristeci
    sljedecu notaciju "."

    danas.dan=15;

    -No rijetko cemo direktno pristupati varijablama objekta jer tako ne koristimo prednost
    objektno orijentiranog programiranja. Umjesto toga cemo pozvati metodu objekta.
    Sintaksa poziva metode je:

    Ime objekta.ime metode (parametri);
    danas.postavidatum(15,10,2007);


    -Objekt danas sada izgleda ovako:

    danas
    dan=15
    mjesec=10
    godina=2007
    void postavidatum(int,int,int)
    void pokazi()

    -Za ispis trenutnog datuma:

    danas.pokazi();


    2.4. Preopterecenje konstruktora
    U C++ moguce je preopterecenje konstruktora. Da bismo to pokazali dodat cemo još jedan
    konstruktor postojecoj klasi.

    //Stari konstruktor koji postavlja datum na predefiniranu vrijednost

    datum(); //ovo je samo prototip konstruktora

    //Novi konstruktor koji stvara objekt datum sa definiranim vrijednostima za dan, mjesec i //godinu

    datum(int dd, int mm, int yy); //ovo je samo prototip konstruktora

    datum::datum(int dd, int mm, int yy)

    {

    dan=dd;

    mjesec=mm;

    godina=yy; //ovo je definicija konstruktora
    }

    Primjer kako pozvati oba konstruktora:

    datum petak; //ovo ce pozvati konstruktor sa predefiniranim vrijednostima
    datum danas(15,11,2007); //ovo ce pozvati novi konstruktor

    2.5. Destruktor
    Kao što se objekt može stvoriti tako se mora moci i uništiti. C++ omogucuje eksplicitno korištenje
    destruktora, tj. metode koja se poziva neposredno prije uništenja objekta.
    Sintaksa za destruktor je vrlo jednostavna:


    ~ime klase();
    U našem primjeru:

    ~datum();

    Destruktori se koriste za cišcenje memorije nakon što su objekti uništeni.


    2.6. Dodjela prava pristupa
    Apstrakcija je mogucnost uklapanja tudeg programa u svoj vlastiti program bez
    razumijevanja kako je program implementiran. Moramo znati što program radi, ali ne moramo
    znati kako on radi. Ljudi koji rade velike programe moraju brinuti samo kako radi njihov kod i
    kako koristit kod koji su napisali drugi ljudi, a ne moraju razumjeti citav program.

    Ovijanje (engl. encapsulation) je mehanizam kontrole pristupa varijablama i metodama
    klasa i objekata. Ona odvaja implementaciju od korisnika. Svaki pristup unutarnjoj reprezentaciji
    je kroz metode klase koje predstavljaju sucelje prema korisniku. Dva su razloga zbog kojih
    korisnik ne bi trebao imati pristup unutarnjoj prezentaciji objekta:

    -Ovijanje sprjecava korisnika da mijenja podatke na ilegalan nacin
    -Ovijanje omogucava programeru da mijenja stanje objekta u bilo koje vrijeme

    C++ implementira enkapsulaciju korištenjem kljucnih rijeci public, private i protected.
    Prava pristupa odreduju koji ce clanovi razreda ili klase biti dostupni izvan razreda, koji iz
    naslijedenih razreda, a koji samo unutar razreda. Svaki clan klase može imati jedan od tri
    moguca nacina pristupa što ce biti pokazano na primjeru:

    Primjer:

    class Pristup {

    public:

    int a, b;
    void Funkcija1(int brojac);


    private:

    int c;

    protected:

    int d;
    int Funkcija2();
    };



    Javni pristup se dodjeljuje kljucnom rijeci public. Clanovima sa javnim pristupom može
    se pristupiti i izvan klase. Varijable a i b te Funkcija1() se mogu pozvati tako da se
    definira objekt klase Pristup i da im se pomocu operatora . pristupi.

    Privatni pristup se dodjeljuje kljucnom rijeci private. Clanovi sa privatnim pristupom nisu
    dostupni vanjskom programu i klasama koji nasljeduju promatranu klasu. Tako se
    varijabli c može pristupiti samo preko funkcijskih clanova klase pristup.

    Zašticeni pristup se dodjeljuje kljucnom rijeci protected. Clanovi sa zašticenim pristupom
    nisu dostupni vanjskom programu nego samo funkcijskim clanovima klase i klasama koji
    nasljeduju promatranu klasu.

    Ako se eksplicitno ne navede pravo pristupa nekom clanu klase podrazumijeva se privatno
    pravo pristupa. Za ilustraciju korištenja prava pristupa imamo stvorit cemo objekt klase
    Pristup i pokazati kojim se clanovima može pristupiti iz kojeg dijela programa:
    Pristup x;
    void Pristup::Funkcija1(int brojac) {
    //sada smo unutar funkcijskog clana klase pa možemo
    //pristupiti svim clanovima klase
    a=brojac;
    c=a+5;
    Funkcija2();
    }
    int main() {
    x.a=x.b=6; //U redu jer a i b imaju javni pristup
    x.Funkcija1(1); // U redu jer Funkcija1() ima javni pristup
    cout<<x.c<<endl //Nije u redu jer c ima privatni pristup i ne može
    //mu se pristupiti izvana
    x.Funkcija2(); //Nije u redu jer Funkcija2() ima zašticeni pristup
    };

    2.7. Javno sucelje
    Clanovi razreda sa javnim pristupom formiraju javno sucelje objekta. Programer analizira
    objekte i nacin njihovog korištenja te tako odreduje koji clanovi pripadaju javnom sucelju. On
    zatim obznanjuje suradnicima što tocno trebaju pružiti objektu i što od njega mogu dobiti
    nazad. Sam sadržaj objekta je crna kutija za korisnike. Implementaciju klase korisnik piše na
    osnovi javnog sucelja cime sucelje postaje neovisno o implementaciji. Ako se kasnije ustvrdi
    da je neka implementacija bolja objekt se jednostavno preradi, dok ostatak koda (npr. kod
    drugih programera) ne treba dirati jer on vidi samo javno sucelje objekta koje se ne mijenja.
    Primjer:
    class Vektor {
    private:
    float ax, ay;
    public:
    void PostaviXY(float x, float y) {
    ax=x;
    ay=y;
    }
    float VratiX() { return ax; }
    float VratiY() { return ay; }
    void MnoziSkalarom(float skalar) {
    ax*=skalar;
    ay*=skalar;
    }
    };



    Implementacija vektora je izvedena u Descartesovom koordinatnom sustavu. Javno
    sucelje ne govori ništa o koordinatama nego samo omogucava da se utvrdi projekcija svakog
    vektora na os x i os y neovisno u kojem je koordinatnom sustavu vektor prikazan. Mi u
    implementaciji možemo promijeniti koordinatni sustav u polarni ako se pokaže da program
    bolje radi, ali javno sucelje ostaje isto.

    Primjer:

    #include <iostream.h>
    class Point {
    int x;
    int y;
    public:
    Point();
    Point(int, int);
    ~Point();
    };
    Point:oint(){ //Definiranje konstruktora
    x=0;
    y=0;
    cout<<"Poziv konstruktora bez parametara"<<endl;
    cout<<"X="<<x<<"\tY="<<y<<endl;
    }
    Point:oint(int InitX,int InitY){ //Preoptereceni konstruktor
    x=InitX;
    y=InitY;
    cout<<"Poziv preopterecenog konstruktora "<<endl;
    cout<<"X="<<x<<"\tY="<<y<<endl;
    }
    Point::~Point(){ //Definiranje destruktora
    cout<<"Poziv destruktora"<<endl;
    }
    void main(){
    Point P1; //Stvaranje objekta P1 prvim konstruktorom
    Point P2(10,10); //Stvaranje P2 preopterecenim konstruktorom
    }


    a) Napraviti metodu koja ce postavljati koordinate tocke na zadanu vrijednost i koja ce
    ispisati koordinate tocke.
    b) Dodati metodu koja ce racunati udaljenost izmedu dvije tocke i ispisati udaljenost na
    ekran. Funkciju preopteretiti da racuna udaljenost zadane tocke od ishodišta
    koordinatnog sustava.


    #include <iostream.h>

    #include <math.h>

    class Point {

    int x;

    int y;

    double d;

    public:

    Point();

    Point(int, int);

    ~Point();

    void SetXY(int,int); //Deklaracija metode SetXY

    void Distance(Point,Point);

    void Distance(Point);

    };

    Point:oint(){ //Definiranje konstruktora

    SetXY(0,0);

    cout<<"Poziv konstruktora bez parametara"<<endl;

    }

    Point:oint(int InitX,int InitY){ //Preoptereceni konstruktor

    SetXY(InitX,InitY); //Poziv metode SetXY

    cout<<"Poziv preopterecenog konstruktora "<<endl;

    }

    void Point::SetXY(int Xset,int Yset) { //Definiranje metode SetXY

    x=Xset;

    y=Yset;

    cout<<"X="<<x<<"\tY="<<y<<endl;

    }

    void Point:istance(Point P1, Point P2) { //Definiranje metode Distance

    d=sqrt((P1.x-P2.x)* (P1.x-P2.x) + (P1.y-P2.y)* (P1.y-P2.y));

    cout<<"Udaljenost dvaju tocaka je d="<<d<<endl;

    }

    void Point:istance(Point P1) {

    d=sqrt((P1.x-0)* (P1.x-0) + (P1.y-0)* (P1.y-0));

    cout<<"Udaljenost od ishodista je d="<<d<<endl;

    }

    Point::~Point() { //Definiranje destruktora

    cout<<"Poziv destruktora"<<endl;

    }

    void main() {

    Point P1; //Stvaranje objekta P1 prvim konstruktorom

    Point P2(10,10); //Stvaranje P2 preopterecenim konstruktorom

    Point P3(15,1; //Stvaranje P3 preopterecenim konstruktorom

    P1.SetXY(5,5);

    P1.Distance(P1,P2);

    P3.Distance(P3);

    }


    2.8. Nasljedivanje
    Nasljedivanje je mehanizam pomocu kojega je na temelju postojecih klasa moguce
    definirati nove i proširene klase. Ideja nasljedivanja je da se odrede slicne klase i da se pri
    nasljedivanju promijene samo neka specificna svojstva, dok se ostala svojstva nasljeduju u
    nepromijenjenom obliku.
    Svojstva izvedene klase:
    -Ona nasljeduje varijable i metode svoj nadredene klase tj. klase koju nasljeduje
    -Ima svoje nove varijable i metode koje ispunjavaju njene specificne zahtjeve
    -Izvedena klasa nasljeduje sva svojstva i mogucnosti nadredene klase
    -Svaki konstruktor u izvedene klase trebao bi pozvati konstruktor nadredene klase
    -Npr. klasa Tocka može biti nadredena klasa klasi Krug (krug se može opisati
    pomocu tocke i pripadnog radijusa)
    Primjer:

    #include <iostream.h>
    #include <math.h>
    class Point {
    int x;
    int y;
    double d;
    public:
    Point();
    Point(int, int);
    ~Point();
    void SetXY(int,int);
    void Distance(Point,Point);
    void Distance(Point);
    };
    Point:oint(){
    SetXY(0,0);
    cout<<"Poziv konstruktora bez parametara klase Point"<<endl;
    }
    Point:oint(int InitX,int InitY){
    SetXY(InitX,InitY);
    cout<<"Poziv preopterecenog konstruktora klase Point "<<endl;
    }
    void Point::SetXY(int Xset,int Yset) {
    x=Xset;
    y=Yset;
    cout<<"X="<<x<<"\tY="<<y<<endl;
    }
    void Point:istance(Point P1, Point P2) {
    d=sqrt((P1.x-P2.x)* (P1.x-P2.x) + (P1.y-P2.y)* (P1.y-P2.y));
    cout<<"Udaljenost dvaju tocaka je d="<<d<<endl;
    }



    void Point:istance(Point P1) {
    d=sqrt((P1.x-0)* (P1.x-0) + (P1.y-0)* (P1.y-0));
    cout<<"Udaljenost od ishodista je d="<<d<<endl;
    }
    Point::~Point() {
    cout<<"Poziv destruktora klase Point"<<endl;
    }


    class Circleublic Point {
    int radius;
    public:
    Circle();
    Circle(int,int,int);
    ~Circle();
    };
    Circle::Circle()oint() {
    radius=10;
    cout<<"Radijus r="<<radius<<endl;
    cout<<"Poziv konstruktora bez parametara klase Circle "<<endl;
    }
    Circle::Circle(int InitX,int InitY, int InitR)oint(InitX,InitY) {
    radius=InitR;
    cout<<"Radijus r="<<radius<<endl;
    cout<<"Poziv preopterecenog konstruktora klase Circle "<<endl;
    }
    Circle::~Circle() {
    cout<<"Poziv destruktora klase Circle "<<endl;
    }


    void main() {
    Circle C1; //Stvaranje objekta C1 prvim konstruktorom
    Circle C2(6,6,5); //Stvaranje objekta C2 preopterecenim konstruktorom
    }


    Ideja nasljedivanja je da svaki objekt podredene klase sadrži varijable i metode
    nadredene klase. Ako su varijable u nadredenoj klasi nalaze pod privatnim pristupom
    (private) onda im se ne može pristupiti iz podredene klase. Da bi im se moglo pristupiti
    varijable bi se morale nalaziti pod kljucnom rijeci protected, što bi znacilo da im metode iz
    podredene klase mogu pristupiti. Ali ako želimo zadržati fleksibilnot i integritet prezentacije
    podataka u osnovnoj klasi moramo ih držati kao protected. Stoga ako je moguce podredene
    klase bi trebale koristiti metode sa javnim pristupom (public) u nadredenoj klasi kako bi
    pristupile zašticenim podacima. Npr. klasa Circle pomocu metode SetXY koja u klasi Point
    ima javni pristup postavlja koordinate središta kruga (x,y) koje u klasi Point imaju privatni
    pristup.



    2.9. Preopterecenje metoda nadredene klase
    Ponekad je potrebno promijeniti metodu koja je naslijedena od nadredene klase jer
    želimo dobiti neka nova svojstva. To se cini preopterecenjem metode koja se nalazi u
    nadredenoj klasi. Potrebno je napisati sasvim novi kod za navedenu metodu. Koja ce od
    dvaju metoda biti pozvana prevodilac odlucuje na osnovu objekta koji poziva metodu. Ako
    objekt koji poziva metodu pripada nadredenoj klasi onda ce biti pozvana metoda iz
    nadredene klase, a ako objekt pripada podredenoj klasi onda ce biti pozvana metoda iz
    podklase.

    Primjer:

    #include <iostream.h>
    class A {
    public:
    void print() {
    cout<<”Metoda koja pripada nadredjenoj klasi”<<endl;}
    };
    class B: public A {
    public:
    void print() {
    cout <<”Metoda koja pripada podredjenoj klasi ”<<endl;}
    };
    void main() {
    A Obj1;
    B Obj2;
    Obj1.print(); //Ovo poziva metodu iz nadredene klase
    Obj2.print(); //Ovo poziva metodu iz podredene klase
    Obj2.A:rint(); //Ovo poziva metodu iz nadredene klase pomocu operatora ::
    }



    Primjer:

    #include <math.h>
    #include <iostream.h>
    #include<string.h>
    class Datum{
    public:
    int dan;
    int mjesec;
    int godina;
    void Ispis();
    Datum(){dan=1; mjesec=1;godina=2007;}
    Datum(int d, int m , int g){dan=d; mjesec=m; godina=g;}
    };
    void Datum::Ispis(){
    cout<<"\n"<<endl;
    cout<<"datum "<<dan << mjesec << godina<<endl;
    }
    class Studentublic Datum{
    public:
    Datum D1;
    Datum D2;
    char ime[32];
    char prezime[32];
    int idn;
    Student();
    Student(int ID);
    Student(char[], char[], int, int, int, int,int,int,int);
    void Ispis();
    };
    Student::Student(){
    strcpy(ime, "Marko");
    strcpy(prezime, "Markic");
    idn=1000;
    }
    Student::Student(int ID){
    idn=ID;
    }
    Student::Student(char im[32], char pr[32],int IDN, int d, int m, int g, int dd,int mm, int gg): D1
    (d, m,g), D2(dd,mm,gg) {
    strcpy(ime, im);
    strcpy(prezime, pr);
    idn=IDN;
    }
    void Student::Ispis(){
    cout<<"\n"<<endl;
    cout<<"Ime "<<ime<<endl;
    cout<<"Prezime "<<prezime<<endl;
    cout<<"Maticni broj "<<idn<<endl;
    cout<<"datum rodenja "<<D1.dan<<"."<<D1.mjesec<<"."<<D1.godina<<end l;



    cout<<"datum upisa "<<D2.dan<<"."<<D2.mjesec<<"."<<D2.godina<<end l;
    }


    class Ispit: public Student{
    public:
    char imeisp[32];
    int ocjena;
    Datum D3;
    Ispit();
    Ispit(char[], int, int, int, int, int);
    void Ispis();
    void Polozeno();
    };
    Ispit::Ispit():Student(){
    strcpy(imeisp, "Oop");
    ocjena=5;
    }
    Ispit::Ispit(char isp[32], int ocj, int ID, int da, int mj, int go):Student(ID), D3(da, mj, go){
    strcpy(imeisp, isp);
    ocjena=ocj;
    }
    void Ispit:olozeno(){
    if (ocjena>1){
    cout<<"\n"<<endl;
    cout<<"Polozeni ispit je: "<< imeisp<<endl;
    }
    }
    void Ispit::Ispis(){
    cout<<"\n"<<endl;
    cout<<"Ime ispita "<<imeisp<<endl;
    cout<<"Ocjena "<<ocjena<<endl;
    cout<<"Maticni broj "<<idn<<endl;
    cout<<"datum polaganja "<<D3.dan<<"."<<D3.mjesec<<"."<<D3.godina<<end l;
    }
    main (){
    Student S1("Josip", "Josipovic", 1015, 5, 4, 1987, 12, 7, 2004);
    Student S2("Marko", "Markic", 1111, 6,6,1986, 1,1,2003);
    S1.Ispis();
    Ispit I1("Oop",0, 1015, 30,6,2007);
    Ispit I2("Teorija mreza", 2, 1015, 1,9,2007);
    Ispit I3("Baze podataka", 4, 1111, 12,5,2007);
    I2.Polozeno();
    I3.Polozeno();
    I1.Ispis();
    I2.Ispis();
    I3.Ispis();
    return 0;
    }



    2.10. Polimorfizam i virtualne funkcije
    Nasljedivanjem klasa možemo konstruirati hijerarhijsku strukturu koja ce nam poslužiti
    za lakše oblikovanje programskog koda. Zajednicka svojstva grupe klasa mogu biti sadržana
    u jednoj ili više nadredenih klasa iz koje su izvedene ostale klase. Imamo li više izvedenih
    klasa, možemo pojednostaviti rukovanje s njima koristeci pokazivac ili referencu na osnovnu
    klasu. Pokazivac tipa osnove klase može, osim na objekte osnovne klase, pokazivati i na bilo
    koji objekt izvedenih klasa, kojima nije jednak po tipu. Ovisno o vrsti objekta na koji pokazuje,
    prevodilac može razlicito interpretirati upotrebu takvog pokazivaca (polimorfizam).
    OsnovnaK *p;
    IzvedenaK1 a;
    IzvedenaK2 b;
    p=&a;
    p=&b;
    p=new IzvedenaK3;
    Izvedena klasa moci ce koristiti public i protected metode osnovne klase, ali se te metode
    takoder mogu i nadopuniti ili cak nanovo definirati u izvedenoj klasi. Ispred deklaracije
    metode osnovne klase koja ce biti redefinirana u nekoj izvedenoj klasi potrebno je staviti
    kljucnu rijec virtual. Tada ce prevodilac pozvati istoimenu metodu izvedene klase, cak i kada
    je pokazivac, koji pokazuje na objekt izvedene klase, iz osnovne klase.
    class Osnovni {
    public:
    virtual void virt_fun(void)
    { ... }
    void fun(void)
    { ... }
    };
    class Izvedeni {
    public:
    void virt_fun(void)
    { ... }
    void fun(void)
    { ... }
    };
    ...
    Osnovni *p = new Izvedeni;
    p->fun(); // izvrsit ce se funkcija Osnovni::fun()
    p->virt_fun(); // izvrsit ce se funkcija Izvedeni::virt_fun()
    ...
    p->Osnovni::virt_fun() // ako bas zelimo pozvati funkciju iz osnovnog razreda,
    // mozemo joj pristupiti operatorom dosega ::
    U tijelu redefinirane metode u izvedenoj klasi možemo takoder pozvati i istoimenu metodu
    osnovne klase (npr. ako je dio posla koji obavljamo identican) sa:
    void Izvedeni::virt_fun()
    { ...
    Osnovni::virt_fun();
    }



    Primjer:

    #include<iostream.h>
    class A {
    public:
    void funkcija() {
    cout<<”Poziva se A::funkcija()”<<endl;
    }
    };
    class B : public A {
    public:
    virtual void funkcija() {
    cout<<”Poziva se B::funkcija()”<<endl;
    }
    }:
    class C : public B {
    public:
    virtual void funkcija() {
    cout<<”Poziva se C::funkcija()”<<endl;
    }
    };



    2.11. Predlošci funkcija i klasa
    Predlošci omogucuju pisanje opcenitog koda koji se može primijeniti za razlicite tipove
    podataka. Tako se omogucuje lakše i brže pisanje složenijih programa. Na primjer ako
    napišemo algoritam za sortiranje (poput bubble sort ili quick sort) kod je isti za sve tipove
    podataka, ali do sada programeri nisu bili u mogucnosti napisati funkciju koja ce vrijediti za sve
    tipove podataka. Problem je u tome što C++ zahtijeva tocno odredivanje tipa podataka s kojima
    radi. Programer zbog toga mora mijenjati deklaraciju funkcije i kopirati kod. Ali se tako se
    povecava kod programa i ako se pojavi greška izmjena se mora napraviti u svim funkcijama.
    Rješenje problema je u korištenju predložaka koji omogucuju da se isti algoritam koristi za
    razlicite tipove podataka.

    Postoje dvije vrste predložaka:

    • Predlošci funkcija
    • Predlošci klasa
    2.11. 1. Predlošci funkcija
    Definicija predloška funkcije zapocinje kljucnom rijeci template. Svaki predložak ima listu
    formalnih parametara koja se nalazi izmedu znakova < lista parametara > . Svaki formalni
    parametar se sastoji od kljucne rijeci class iza koje se navede identifikator.

    Funkcija manji() koja vraca manji od dva broja može se pisati:

    template <class NekiTip>
    NekiTip manji (NekiTip a, NekiTip b) {

    return a < b ? a : b;
    }

    Prilikom prevodenja predložak ce rezultirati konkretnom funkcijom za svaki tip
    parametara. Pisanje tijela funkcije je isto kao i kod regularnih funkcija samo što ne navodimo
    konkretan tip nego tip koji se nalazi u listi parametara. Da bi se moglo izvršiti kompajliranje
    programa u kojem se koristi genericka funkcija, kompajleru mora biti na raspolaganju potpuna
    definicija funkcije, a ne samo deklaracija funkcije kao što je slucaj kod upotrebe regularnih
    funkcija.


    Primjer:

    template <class Tip>
    Tip Kvadrat (Tip x) {
    Tip result = x*x;
    return result;
    }
    int main ()
    {
    int i=5, k;
    float a=10.9, b;
    k=Kvadrat<int>(i);
    b=Kvadrat<float>(a);
    cout << k << endl;
    cout << b << endl;
    return 0;
    }


    Ispis je:
    25

    118.81
    U gornjem primjeru ne mora se specificirati tip podatka jer je jasno da su varijable a i b tipa float,
    ali je preporuka da se eksplicitno specificira tip parametara.

    Mogu se definirati i funkcije s više tipova.

    Primjerice:
    template <class T, class U>
    T GetMin (T a, U b) {

    return (a<b?a:b);
    }
    definira funkciju s dva argumenta kojima su tipovi T i U. Moguce je izvršiti poziv funkcije na

    slijedeci nacin:
    int i,j;
    long l;
    i = GetMin<int,long> (j,l);

    ili još jednostavnije


    i = GetMin (j,l);
    Iako su j i l razlicitog tipa, kompajler sam vrši pretvorbe tipova.


    Osim kljucne rijeci class možemo koristiti i kljucnu rijec
    typename kojom eksplicitnije navodimo
    da se ne radi o klasi vec
    o tipu podatka. Ali ona ima i dodatnu funkciju, njome možemo unutar
    predloška odrediti da nešto predstavlja tip, a ne nešto drugo.

    Na primjer:
    template <typename T> //T oznacava tip
    void funkcija() {
    T :: A * pok1; //Ove dvije deklaracije imaju
    typename T :: A * pok2; //razlicito znacenje
    }

    Pretpostavimo da naš predložak u sebi mora imati definiran tip A. Prva deklaracija ne daje
    željenu vrijednost. Ona pristupa identifikatoru A unutar tipa T i množi ga sa pokazivacem pok1
    što je pogrešno. Druga deklaracija pomocu typename oznacava da se radi o tipi T::A i prilikom
    instantacije se specificira stvarni tip.

    Preopterecenje predložaka funkcije

    Predložak funkcije može biti preopterecen proizvoljan broj puta pod uvjetom da se potpisi
    funkcija razlikuju po tipu ili broju argumenata. Na primjer može se definirati funkcija zbroji:

    //Zbrajanje dvaju elemenata
    template <class Tip>
    Tip zbroji (Tip a, Tip b);


    //Zbrajanje triju elemenata
    template <class Tip>
    Tip zbroji (Tip a, Tip b, Tip c);


    //Zbrajanje dvaju nizova
    template <class Tip>
    Tip zbroji (Tip *niza, Tip *nizb, Tip *rez, int brElem);


    Nije moguce preopteretiti funkcije tako da se razlikuju samo u povratnom tipu:



    //Pogreška: funkcija se razlikuje samo u povratnom tipu

    template <class Tip>

    int zbroji (Tip a, Tip b);

    2.11.2. Predlošci klasa
    Slicno kao kod predložaka funkcija postoje i predlošci klasa koji se koriste za definiranje
    opcih klasa koje se kasnije konkretiziraju stvarnim klasama. Primjer takvih klasa su spremnici ili
    kontejnerske klase. To su klase koje manipuliraju objektima neke druge klase te se cesto koriste
    za pohranjivanje nizova objekata. Na primjer ako želimo napraviti klasu koja ce manipulirati
    listama. Listu možemo predstaviti kao objekt. Pravila za održavanje liste ne ovise o stvarnim
    objektima. Postoje standardne operacije nad listom: dodavanje elementa na pocetak, dodavanje
    elementa, brisanje, pretraživanje itd. Operacije se konceptualno obavljaju isto neovisno o tipu,
    stoga je korisno definirati opci predložak klase koji definira opcenite algoritme. Ako ne koristimo
    predloške tada moramo imati klasu za svaku listu npr. CjelobrojnaLista, RealnaLista itd.

    Za realizaciju liste trebamo dvije klase:


    Klasa Lista koja ce definirati opca svojstva liste, memorirati prvi i posljednji clan liste i
    definirati javno sucelje. Klasa Lista sastoji mora imati sljedece operacije:
    o
    void UgurajClan(element, izaKojeg) dodaje element iza clana na kojeg pokazuje
    izaKojeg
    o
    void GoniClan(element) izbcuje clan na koji element pokazuje
    o
    bool Prazna() vraca true ako je lista prazna

    Klasa ElementListe koja ce definirati objekt koji se smješta u listu. Objekt ce sadržavati
    pokazivace na prethodni i iduci clan, te vrijednost objekta.
    Deklaracija predloška klase zapocinje tako da se ispred kljucne rijeci class umetne kljucna rijec
    template iza koje se unutar znakova <> umetnu argumenti predloška.

    template <class Tip>

    class Lista;

    Ako imamo više parametara:

    template <class Tip1, class Tip2, class Tip3 >

    class NekaKlasa;

    Klase definirane kao predlošci pozivaju se na isti nacin kao obicne klase s time da se iza imena
    klase unutar <> moraju navesti parametri. Lista cijelih brojeva se ozncava kao:

    Lista<int>


    A lista kompleksnih brojeva:
    Lista<Kompleksni>


    Primjer: definicija klase ElementListe:

    template<class Tip>
    class ElementListe {
    private:
    Tip vrijednost;
    ElementListe *prethodni, *sljedeci;
    public:
    ElemetListe *Prethodni() { return pretodni; }
    ElemetListe *Sljedeci () { return sljedeci; }
    void PostaviPrethodni (ElemetListe *pret) { pretodni=pret; }
    void PostaviSljedeci (ElemetListe *sljed) { sljedeci=sljed; }
    ElemetListe (const Tip &elem);
    ElemetListe ();
    Tip &DajVrijednost();
    };


    Identifikator Tip oznacava tip klase koji ce se navesti prilikom poziva klase. Pomocu tog
    identifikatora možemo raditi sve što možemo i sa drugim tipovima: stvarati objekte, proslijedivati
    ih, brisati itd. Unutar klase identifikator ElemetListe se koristi bez parametara jer su oni
    navedeni u template deklaraciji na samome pocetku. No izvan deklaracije klase pored
    identifikatora klase se uvijek moraju navesti parametri.

    Instanciranje predložaka klase

    Predložak klase se instancira tako da se iza imena klase unutar <> navedu svi parametri
    predloška:
    ElementListe<int> eli(5);
    ElementListe<Kompleksni> elk(Kompleksni());
    ElementListe<char *> elz(“Listam”);


    U gornjem primjeru samo ime ElementListe oznacava sve moguce elemente liste i ono nije
    dovoljno za definiranje objekta. Potpuna definicija je ElementListe<int> koja opisuje element
    liste cijelih brojeva. Gornjim primjerom dobili smo tri neovisne klase. Prevoditelj ce tri puta
    zamijeniti formalni tip stvarnim tipom. Nakon toga se provjerava sintaticka ispravnost koda. Za
    neki tip kod može raditi a za drugi ne. To se može dogoditi ako neke operacije nisu podržane za
    odredei tip (npr. za neke tipove ne postoji operator usporedbe).


    Primjer:

    template <class T>

    class array {

    T *m_pbuff;

    int m_size;

    public:

    array(int N = 10): m_size(N) {m_pbuff=new T[N];}

    ~array() {delete [ ]Sm_pbuff;}

    int size() {return m_size;}

    void set(int x, T value);

    T get(int x);

    T & operator [ ]S(int i)

    {return m_pbuff[i];}

    };

    template <class T> void array<T>::set(int x, T value)

    { m_pbuff[x]=value; }

    template <class T> T array<T>::get(int x)
    { return m_pbuff[x]; }


    //Get() i set() funkcije smo mogli definirati inline unutar definicije klase. One su definirane
    //izvan definicije klase kako bi se pokazalo da se tada uvijek ispred definicije funkcije
    //mora napisati genericki prefiks: template <class T> .

    int main () {
    array<int> myints(5);
    array<float> myfloats(5);
    myints.set (0, 100);
    myfloats.set(3, 3.1416);
    cout << "myints ima: "
    << myints.size() <<" elemenata"<< '\n';
    cout << myints[0]S<< '\n';
    cout << myfloats[3]S<< '\n';
    return 0;



    }

    Rezultat je:

    myints ima: 5 elemenata

    100

    3.1416

    Primjer:
    Klasa pair služi kao kontenjer dva elementa koji mogu biti razlicitih tipova. Formirat cemo niz
    takovih parova pomocu genericke klase array. U taj niz cemo upisati i iz njega ispisati niz parova
    kojima prvi element predstavlja redni broj (tip int), a drugi element je kvadratni korijen prvoga (tip

    float).

    template <class T1, class T2>

    class pair

    {

    T1 value1;

    T2 value2;

    public:

    pair (T1 first=0, T2 second=0) {

    value1=first; value2=second;

    }

    T1 GetFirst () {

    return value1;

    }

    void SetFirst (T1 val) {

    value1 = val;

    }

    T2 GetSecond () {

    return value2;

    }

    void SetSecond (T2 val) {

    value2 = val;

    }

    };

    int main ()


    {

    // niz od 5 parova int,float

    array <pair<int,float> > arr(5);

    for(int i=0; i<arr.size(); i++)

    {

    arr[i].SetFirst(i); // prvi sadrži redni broj

    arr[i].SetSecond(sqrt(i)); // kvadratni korijen prvog

    }

    for(int i=0; i<arr.size(); i++)

    cout << arr[i].GetFirst() <<':'

    <<arr[i].GetSecond()<< endl;

    cout << endl;

    return 0;

    }
    Rezultat je:

    0:0
    1:1
    2:1.41421
    3:1.73205
    4:2
    Važno je upozoriti na oblik deklaracije niza parova:

    array <pair<int,float> > arr(5);

    1. Vidimo da se konkretna realizacije generickog tipa može izvršiti i pomocu drugih generickih
    tipova.
    2. U deklaraciji se pojavljuju dva znaka > >. Izmedu njih obvezno treba napisati razmak, jer ako
    bi napisali
    array <pair<int,float>> arr(5); // greška : >>

    kompajler bi dojavio grešku zbog toga jer se znak >> tretira kao operator. Da bi se izbjegle
    ovakve greške, preporucuje se koristiti typedef deklaracije oblika:

    typedef pair<int,float> if_pair;
    array <if_pair> arr(5);


    Ugniježdeni predlošci

    Moguce je definirati predložak neke klase unutar druge klase.

    template<class T1>
    class X {
    public:


    template <class T2>

    class Y {
    void Funkcija();
    T1 *pok;


    };
    };

    Clan Funkcija() je parametriziran s dva tipa T1 i T2 i njena definicija izvan klase izgleda ovako:
    template<T1> template<T2>
    void X<T1>::Y<T2>::Funkcija() {
    //Tijelo funkcije
    }


    Instanca tj. objekt klase Y ovisi o parametrima klase X i o prametrima klase Y:


    X<char>::Y<Kompleksni> objekt;



    Predlošci i nasljedivanje

    Postoje tri nacina na koji se mogu nasljedivati predlošci:


    Prvi nacin je kada predložak služi kao osnovni nacin za izvodenje konkretne klase koja
    nije predložak. Osnovni razred mora biti parametriziran konkretnim tipom podatka. Na
    primjer klasa SkupRijeci nasljeduje klasu Lista:
    class SkupRijeci: Lista<char *> {
    //Tijelo klase
    };


    • Drugi nacin je da se predložak klase izvodi iz neke obicne klase. Npr klasa
    Listanasljeduje klasu Kontejner koja sadrži opca svojstva objekta koji skladišti druge
    objekte:

    class Kontejner {

    public:
    virtual int BrojElemenata()=0;
    virtual void Prazni();

    };

    template <class Tip>

    class Lista : public Kontejner {

    //Tijelo klase

    };

    Sve varijante klase Lista imat ce podobjekt klase Kontejner koji nije parametriziran predloškom.


    Treci nacin je u kojem se predložak izvodi iz predloška. Osnovni razred tj. Predložak
    mora imati listu parametara. Na primjer klasa Lista može poslužiti za izvodenje klase
    Stog:
    template <class Tip>
    class Stog : Lista<char> {
    //Tijelo klase
    };


    Ovaj slucaj je identican prvom slucaju s time da je osnovna klasa sada predložak.



    Specijalizacija predloška

    Ponekad se s jednim predloškom ne mogu obuhvatiti svi slucajevi realizacije s razlicitim
    tipovima. U tom se slucaju, za tipove kojima realizacija odstupa od predloška, može definirati
    poseban slucaj realizacije koji nazivamo specijalizacija predloška.

    Za klasu koju definiramo predloškom:

    template <class opci_tip> identifikator {...}

    specijalizacija za konkretni tip se zapisuje u obliku:

    template <> class identifikator <konkretni_tip> {...}

    Specijalizacija se smatra dijelom predloška, pa njena deklaracija zapocinje sa template <>.U
    njoj se moraju definirati svi clanovi predloška, ali iskljucivo s konkretnim tipovima za koje se
    vrši specijalizacija predloška. Podrazumijeva se da moraju biti definirani konstruktor i destruktor,
    ukoliko su definirani u opcem predlošku.

    To cemo demonstrirati banalnim primjerom. U klasi pair definirana je funkcija modul() koja daje
    ostatak dijeljenja prvog s drugim elementom kada su oba elementa tipa int, a kada su elementi
    drugih tipova funkcija modul() vraca vrijednost 0.

    template <class T1, class T2> //opci predložak
    class pair {
    T1 value1;
    T2 value2;
    public:
    ....
    int modul () {return 0;}
    };
    template <> // specijalizacija predloška
    class pair <int,int> { // za tip int,int
    int value1, value2;
    public:
    ....
    int modul () {return value1 % value2;}
    };



    Predlošci s konstantnim parametrima

    Parametri predloška mogu biti i integralne konstante(int,char,long, unsigned). Primjerice,

    template <class T, int N> class array {...}

    je predložak za definiranje klase array pomocu generickog tipa T i integralne konstante N.
    Vrijednost integralne konstante se mora specificirati pri deklaraciji objekta. Primjerice, s

    array <float,5> myfloats;

    deklarira se myfloats kao niz realnih brojeva kojem se dodatna svojstva zadaju s konstantom
    N=5. U slijedecem primjeru koristit cemo ovaj konstantni parametar za postavljanje velicine niza.

    template <class T, int N>

    class array

    {

    T m_buff[N]; // niz od N elemenata

    int m_size;

    public:

    array() : m_size(N) {}

    int size() {return m_size;}

    T & operator [ ]S(int i) {return m_buff[i];}

    };

    int main () {

    array <int,5> myints;

    array <float,5> myfloats;

    myints[0]= 100;

    myfloats[3]= 3.1416;

    cout << "myints ima: "<< myints.size() <<" elemenata"<< '\n';

    cout << myints[0]S<< '\n';

    cout << myfloats[3]S<< '\n';

    return 0;

    }
    Dobije se ispis:

    100

    3.1416


    Predodredeni i funkcijski parametri predloška

    U predlošcima se mogu koristiti predodredeni parametri. Primjerice, predloškom

    template <class T=int, int N=10>
    class array {...}


    se omogucuju deklaracije oblika:


    array<> a_int_10; // niz od 10 int
    array<float> a_float_10; // niz od 10 float
    array<char,100> a_char_100; // niz od 100 char


    Napomena: predodredeni parametri se ne smiju koristiti u funkcijskim predlošcima.


    Parametri predloška mogu biti funkcije:


    template <int Tfunc (int)>
    class
    {...


    y = Tfunc(x);
    }

    Definicija i upotreba klase tvector<class T>

    Genericka klasa tvector predstavlja podskup standardne klase vector. Namjena joj je
    manipuliranje s homogenim kolekcijama elemenata proizvoljnog tipa. Izvršit cemo specifikaciju i
    implementaciju klase tvector.

    Objekti tvector klase imaju slijedece karakteristike:


    capacity() -kapacitet vektora je broj celija koje se automatski alociraju za spremanje
    elemenata vektora,

    size() -velicina vektora je broj elemenata koje stvarno sadrži vektor.

    operator [ ]S-Pojedinom elementu vektora se pristupa pomocu indeksnog operatora, ili se
    koriste dvije funkcije:


    push_back(el) dodaje element na indeksu iza posljednje upisanog elementa, a

    pop_back(el) briše posljednji element iz vektora.

    Pri pozivu push_back() funkcije vodi se racuna o kapacitetu vektora i vrši se automatsko
    alociranje potrebne memorije (memorija se udvostrucuje ako potrebna velicina vektora
    premašuje trenutni kapacitet). Namjena ove dvije funkcije je da se vektor može koristiti
    kao dinamicka kolekcija elemenata (kontejner).

    • resize(n) -ako je potreban veci kapacitet vektora, on se može dobiti pozivom funkcije
    resize(n).
    Specifikacija ADT tvector

    //************************************************** *
    // template <class T> tvector {....}
    // Genericki parametar tipa T mora zadovoljiti:
    // (1) T ima predodredeni konstructor
    // (2) za tip T definiran je operator =
    //
    // tvector( )
    // POST: Kapacitet vektora == 0
    //
    // tvector( int size )
    // PRED: size >= 0
    // POST: kapacitet/velicina vektora == size
    //
    // tvector( int size, const T & Value )
    // PRED:: size >= 0
    // POST: Kapacitet/velicina == size, svi elementi se iniciraju na Value
    //
    // tvector( const tvector & vec )
    // Opis: kopirni konstruktor
    // POST: vector je kopija od vec
    //
    // ~tvector( )
    // Opis: destruktor
    //
    // const tvector & operator = ( const tvector & rhs )
    // POST: dodjela vrijednosti od rhs;
    // ako se razlikuju velicine ova dva vektora
    // vrši se promjena velicine na rhs.size()
    //
    // pristupnici
    //
    // int length( ) const
    // int capacity( ) const
    // POST: vraca broj celija alociranih za vektor



    // int size( ) const
    // POST: vraca stvarni broj elemenata u vektoru
    // koristi se za push_back i pop_back funkcije
    //
    // indeksirani pristup i mutatori
    //
    // void push_back(const T& t);
    // POST: umece element t na poziciji size()
    // ako je size>= capacity dvostruko
    // povecava kapacitet
    //
    // void pop_back();
    // POST: odstranjuje element s pozicije size()
    // i smanuje size za 1
    //
    // T & operator [ ]S( int k );
    // const T & operator [ ]S( int k );
    // Opis: Dobava k-tog elementa, uz kontrolu indeksa
    // PRED: 0 <= k < capacity()
    // POST: vraca k-ti element
    //
    // void resize( int newSize );
    // Opis: promjena velicine vektora u newSize
    // PRED: newSize >= 0,
    // kapacitet je capacity, a velicina je size()
    18
    // POST: 1. size() == newSize;
    // Ako je newSize > size() tada i
    // capacity=newSize
    // inace se kapacitet ne mijenja
    // 2. za 0 <= k <= min(size(), newSize),
    // vector[k]je kopija originala
    // ostali elementi se iniciraju
    // pomocu predodredenog T konstruktora
    // Nota: ako je newSize < size(), gube se neki elementi
    //
    // void clear();
    // POST: size = 0, capacitet nepromijenjen
    //
    // void reserve(int size);
    // Opis: rezervira memoriju
    // PRED: size >0
    // POST: resize(size) ako je size > capacity()


    Primjer deklaracije:

    tvector<int> v1; // vektor s 0-elementa
    tvector<int> v2(4); // vektor s 4-elementa
    tvector<int> v3(4, 22); // vektor s 4-elementa -svi vrijednosti 22.


    Primjer: Napišite program u kojem korisnik unosi proizvoljan niz brojeva. Kao kontejner brojeva
    koristite tvector objekt. Nakon završenog unosa izracunajte sumu i srednju vrijednost unesenog
    niza.


    #include <iostream>
    #include "tvector.h"
    using namespace std;


    int main()
    {
    tvector<double> vec;
    double val;


    // unos proizvoljnog niza brojeva u vektor vec
    while (cin >> val)
    vec.push_back(val);

    // unos završava kada se otkuca neko slovo!!
    //zatim racunamo sumu i srednju vrijednost


    double sum = 0;
    for (int i=0; i<vec.size(); i++)
    sum += vec[i];


    double avg =sum /vec.size();
    cout <<"Suma od "<<vec.size()
    <<" elemenata: "<< sum
    <<". Srednja vrijednost:"<< avg << endl;
    return 0;
    }


    Uocite: pri unosu podataka vektor koristimo kao kontejner – u njega odlažemo elemente
    pomocu funkcije push_back. Kasnije vektor koristimo kao obican niz (elementima pristupamo
    pomocu indeksa). Korištenje vektora je pogodnije od rada s obicnim nizovima je ne moramo
    voditi racuna o alokaciji memorije.



    2.12. Preopterecenje operatora
    Preoptereceni operator definiramo isto kao i funkciju pa se on može koristiti kao funkcija
    pomocu kljucne rijeci operator i oznake operatora cije se djelovanje definira tom funkcijom.

    class Counter

    {

    private:
    int m_count;
    public:

    //konstruktori i destruktor
    //...
    // pristupnici i mutatori
    int get_count() {return m_count;};


    // definiranje operatora ++, =, ==, < , <<


    Counter& operator = (const Counter &);
    Counter& operator++(); //prefiks inkrement
    Counter& operator++(int unused); //postfiks inkrement
    friend bool operator == (const Counter &c1, const Counter& c2);
    friend bool operator < (const Counter &c1, const Counter& c2);
    friend ostream& operator << (ostream &s, const Counter &c1);


    };


    Ovakvom specifikacijom klase Counter omogucuju se izrazi oblika:


    Counter a;
    ++a;
    Counter b = a++;
    if(a==b) cout << "vrijednost oba brojaca je" << a;


    2.12.1. Oblici operatorskih funkcija
    Vecina operatorskih funkcija može biti deklarirana kao:

    • nestaticka clanska funkcija ili kao
    • neclanska globalna funkcija.
    Primjerice, operator == može biti deklariran na više nacina:

    1. deklaracije operatora == kao clanske nestaticke funkcije
    class Counter
    {
    private:
    int m_count;
    public:



    //...
    int get_count() {return m_count;};


    Counter & operator =(const Counter &);
    bool operator ==(const Counter & d);


    };


    2. deklaracije operatora == kao globalne funkcije
    bool operator == (const Counter& c1, const Counter& c2);

    3. deklaracije operatora == kao prijateljske funkcije
    class Counter
    {
    private:
    int m_count;
    public:
    //...
    int get_count() {return m_count;};
    Counter & operator =(const Counter &);


    friend bool operator == (const Counter& c1, const Counter& c2);

    };

    Izuzetak od ovog pravila su operatori

    [],(),=,i ->

    koji mogu biti deklarirani samo kao nestaticke clanske funkcije.
    Isto vrijedi i za složene operatora tipa

    +=, *= itd.


    Preopruceni oblici operatorskih funkcija.
    Operator je simbolicki oznacen s @, a objekti i klase s a,bi c:
    a je objekt klase A, b je objekt klase B, a c je objekt klase C.


    Izraz Operator (@) Clanska funkcija
    Globalna
    funkcija
    @a + -* & ! ~ ++ -A::
    operator@() operator@(A)
    a@ ++ -A::
    operator@(int) operator@(A, int)
    a@b
    + -* / % ^ & | < > == != <= >= << >>
    && || ,
    A:perator@(B) operator@(A, B)
    a@b = += -= *= /= %= ^= &= |= <<= >>= [ ]SA:perator@(B) -
    a(b,c..) () A:perator()(B, C..) -
    a->b -> A:perator->() -

    2.12.2. Sucelje operatora
    Sucelje operatora se opisuje tipom argumenata i tipom rezultata operatorske funkcije.
    Preporucljivo je koristiti sucelje opisano u sljedecoj tablici.

    Tip operatorske funkcije
    Vraca
    Prijenos argumenata
    const
    funkcija
    Aritmeticki (+, -, *, /) vrijednost const referenca da
    Pridjela vrjednosti (=, +=, -=) referencu (*this) const referenca ne
    Relacijski (<, ==) vrijednost (bool) const referenca da
    Unarni prefiks (++, --) referencu (*this) -ne
    Unarni postfiks (++, --) vrijednost vrijednost (int dummy) ne
    Unarni -i + vrijednost -da
    Indeksni [ ]Sreferencu vrijednost (int) ne
    Funkcijski () po volji po volji ne

    Implementacija sucelja je u nadležnosti programera.
    Pri izradi sucelja i implementacije treba voditi racuna:


    da se poštuju pravila asocijativnosti i prioriteta djelovanja operatora kakovi vrijede za
    proste skalarne tipove,
    t2+t1/t2 se uvijek interpretira kao t2+(t1/t2)


    da primjena operatora zadovoljava kontekst izraza u kojim oni koriste (glupo bi bilo dati
    znacaj operatoru koji se razlikuje od znacaja kojeg ima u C jeziku, iako je to dopušteno).

    Primjer: operator ==.

    To je binarni i simetricni operator. Njime se usporeduje dva istovrsna operanda, i dobija rezultat
    tipa bool. Simetricnost je u tome da operandi mogu zamijeniti mjesto. Ovo svojstvo implicira da
    se treba deklarirati friend operatorsku funkcija, jer ako se operator deklarira pomocu clanske
    funkcije tada se dobija asimetricnost u izvršenju izraza.

    class Counter

    {

    private:

    int m_count;

    public:

    //...

    // 1 – asimetricna deklaracija ne zadovoljava primjenu

    // bool operator ==(const Counter &);

    // 2 – simetricna deklaracija zadovoljava primjenu operatora
    friend bool operator == (const Counter& c1, const Counter& c2) const;

    };

    //// implementacija

    bool operator == (const Counter &c1, const Counter& c2)

    {

    return c1.m_count == c2.m_count;

    }


    Primjer: operator pridjele vrijednosti =.

    Njime se mijenja lijevi operand a desni ostaje nedirnut. Sucelje mora izrazitu tu cinjenicu.
    Povoljna je deklaracija clanskom funkcijom jer imamo asimetricno djelovanje operatora.
    Argument funkcije može biti const referenca. Funkcija mora vratiti referencu kako bi se mogli
    koristiti izrazi oblika:

    a = b= c;

    To je ocito ako napišeno operatorski oblik:
    a.operator=(b.operator=(c));

    vidimo da b.operator=(c) mora vratiti referencu jer je to argument funkcije a.operator=(...).
    Implementacija se realizira tako da funkcija operator= vraca dereferencirani this pokazivac, tj.

    Counter& Counter:perator=(const Counter& c)
    {
    m_count = c. m_count;
    m_mod = c. m_count;
    return *this;
    }


    Da zakljucimo,

    sucelje operatora se mora realizirati tako da se osigura slicnost s djelovanjem tog operatora na
    standardne tipove. Implementcija je u nadležnosti programera.

    2.12.3. Restrikcije
    Mogu se preopteretiti slijedeci operatori:

    new delete new[ ]Sdelete[ ]S
    +-*/%^&| ~!=<>
    += -= *= /= %= ^= &= |= << >> >>= <<= ==
    != <= >= && || ++ --, ->* -> () []S


    s tim da se operatori: +, -, * i & mogu koristiti u unarnom i binarnom obliku.



    Ne smije se mijenjati znacaj slijedecih operatora:

    • Tocka operator .
    • operator indirekcija pokazivaca na clana klase .*
    • operator dosega ::
    • operator sizeof
    zbog toga jer oni djeluju na ime a ne na objekt.

    Ne smije se mijenjati znacaj ternarnog uvjetnog operatora ?:.

    Ne smije se mijenjati znacaj novih type cast operatora: static_cast<>, dynamic_cast<>,
    reinterpret_cast<>, i const_cast<> .

    Ne smije se mijenjati znacaj # i ## predprocesorkih simbola.

    Za operatorske funkcije vrijede pravila naslijedivanja kao i za ostale clanske funkcije. Izuzetak je
    jedino operator pridjele vrijednosti, koji je uvijek definiran za svaku klasu (ako definiranje
    operatora pridjele vrijednosti ne izvrši programer, tada to implicitno obavlja sam kompajler).

    Ne smije se izmišljati nove operatore. Primjerice, nedozvoljeno je deklarirati znak @ kao
    operator:

    void operator @ (int); // nije dozvoljeno

    Broj argumenata mora odgovarati broju argumenata koji se koriste u standardnim izrazima.

    Za razliku od obicnih funkcija, u operatorskim deklaracijama se ne smiju koristiti predodredeni
    parametri. Izuzetak je jedino operator ().


    2.12.4. Postfiks i Prefiks Operatori
    Operatori --i ++ mogu se koristiti kao prefiks i postfiks operatori. Da bi se mogla razlikovati
    definicija postfiks i prefiks djelovanja ovih operatora, pravilo je da se postfiks operatori
    deklariraju s jednim argumentom tipa int, iako se taj argument ne koristi, a prefiks operatori
    se deklariraju bez argumenata. Primjerice, za klasu Counter se inkrement operator definira na
    slijedeci nacin:

    class Counter {
    int m_count;
    public:

    Counter & operator++(); //prefiks

    Counter operator++(int unused); //postfiks
    ...
    };

    // primjena


    Counter c1, c2;
    //prefiks: prvo inkrementira c2, i pridjeljuje ga c1
    c1 = ++c2;


    //postfiks; prvo pridjeli c2 u c1 pa inkrementiraj c2
    c1 = c2++;


    //implementacija


    const Counter& Counter:perator++() // prefiks
    {
    ++m_count;; // inkrementiraj brojac
    return *this; // vrati njegovu referencu
    }
    const Counter Counter:perator++(int unused) // postfiks
    {
    Counter temp(*this); // zapamti stanje u temp
    ++ m_count;; // inkrementiraj brojac
    return temp; // vrati temp objekt
    }



    Uocite da se u postfiks verziji vraca referenca, a u prefiks verziji se vraca vrijednost. Mogli smo
    obje verzije napisati tako da se vraca void. U tom slucaju inkrementiranje brojaca bi se moglo
    izvršiti samo u prostim naredbama tipa:

    ++c; ili c++;

    Kada koristiti povrat referenca, a kada povrat vrijednosti objekta? U principu treba koristiti povrat
    reference (*this) uvijek kada je to moguce. U postfiks verziji operatora ++ to nije moguce jer *this
    predstavlja referencu inkrementirane vrijednosti, a funkcija mora vratiti prethodnu vrijednost
    objekta.

    2.12.5. Korištenje operatora kao normalnih funkcija
    Counter c1, c2;
    bool equal;
    c1.operator++(0); // ekvivalentno: c1++;
    c1.operator++(); // ekvivalentno: ++c1;
    equal = operator==(c1, c2); // ekvivalentno: equal = c1==c2;


    2.12.6. Višestruko preopterecenje operatora
    Normalne funkcije se mogu višestruko preopteretiti na nacin da se definira više verzija s
    razlicitim parametrima. Isto vrijedi i za operatore, preopterecenje može biti višestruko.


    Tri verzije operatora == za klasu Counter


    bool operator == (const Counter &c1, const Counter& c2);
    bool operator == (const Counter &c, int i);
    bool operator == (int i, const Counter& c);


    U tom slucaju moguce je usporedivati vrijednost brojaca s cjelobrojnom varijablom.


    Na samom pocetku ovog poglavlja definirana je friend funkcija:


    ostream & operator << (ostream & s, const Counter &c1)

    {

    return s << c1.get_count(); // ostream objekt vraca referencu

    }

    koja omogucuje da se operatorom << usmjerava vrijednost brojaca na izlazni tok. Primjerice,

    Counter c;

    cout << c;


    ispisuje vrijednost brojaca na standardnom izlazu. Funkcija vraca referencu na ostream objekt.
    To je nužno za korištenje izraze oblika
    Counter a, b, c;
    cout << a << b << c;

    Operatori << i >> su vjerojatno najoptereceniji operatori, jer se definiraju u vecini klasa koje
    komuniciraju s ulazno/izlaznim tokovima.

    2.12.7. Preopterecenje operatora pobrojanih tipova
    Pobrojani tip obuhvaca skup konstanti. Cilj nam je da se pomocu operatora ++ i --može
    iterativno mijenjati vrijednosti iz definiranog skupa konstanti.
    Primjer:


    enum Days{
    Monday, Tuesday, Wednesday, Thursday,
    Friday, Saturday, Sunday
    };
    Days& operator++(Days& d, int) // postfix ++
    {


    if (d == Sunday)
    return d = Monday;
    int temp = d; //pretvori u int
    return d = (Days) (++temp);


    }
    int main()
    {
    Days day = Monday;
    for (; //prikaz days kao cjelobrojnih vrijednosti
    {
    cout<< day <<endl;
    day++;
    if (day == Sunday)
    break;
    }
    return 0;
    }



    Ako želimo da se simbolicke konstante ispisuju svojim imenom, a ne numerickom vrijednošcu,
    tada se može preopteretiti << operator;

    ostream& operator<<(ostream& os, Days d)
    {
    switch(d){
    case Monday: return os<<"Monday";
    case Tuesday: return os<<"Tuesday";
    case Wednesday: return os<<"Wednesday";
    case Thursday: return os<<"Thursady";
    case Friday: return os<<"Friday";
    case Saturday: return os<<"Saturday";
    case Sunday: return os<<"Sunday";
    default: return os<<"Unknown";
    }
    }


    2.12.8 Klasa intArray i preopterecenje indeksnog operatora [ ]S
    Kada klasa služi za apstrakciju niza elemenata poželjno je definirati indeksni operator [ ]Spomocu
    kojeg se pristupa elementima niza. Poželjno je uvijek definirati dvije verzije operatora [ ]: const
    verziju i ne-const verziju. Primjerice, definirat cemo klasu iarray koja nam može poslužiti za rad s

    cjelobrojnim dinamickim nizovima.
    const int def_iarray_size = 5;
    class iarray // niz cjelobrovnih elemenata
    {
    int *m_arr; // pokazivac
    na memoriju koja sadrži niz
    int m_size; // broj elemenata niza
    void init(const int *array, int size); // pomocna privatna funkcija
    public:
    iarray(int def_size = def_iarray_size) {
    init((const int *) 0, def_size);
    }
    iarray(const iarray& rhs) // kopirni konstruktor
    {
    init(rhs.m_arr, rhs.m_size);
    }
    iarray(const int *array, int size) // konstruktor pomocu postojeceg
    { init(array, size); // int * niza
    }
    ~iarray(void) { delete [ ]Sm_arr; } // destruktor


    // pridjela vrijednosti

    iarray& operator=(const iarray&);
    int size() const {return m_size;}

    // indeksni operator – const i non-const verzija

    int& operator[ ](int index) { return m_arr[index]; }
    const int& operator[ ](int index) const { return m_arr[index]; }
    };
    void iarray::init(const int *array, int size)
    {


    // alociraj memoriju velicine m_size cijelih brojeva

    m_size = size;
    m_arr = new int [size];

    //inicijaliziraj niz
    if (array != 0)
    for (int index=0; index<size; index++)
    {
    Moj YT kanal | Komp i periferija | Mob: OnePlus 6
    I'm running out of boxes to think outside of.

  2. #2
    Super Moderator Fight fire with fire marijanovic's Avatar
    Datum registracije
    Dec 2005
    Lokacija
    Virovitica
    Postova
    10.751
    m_arr[index]S= array[index];
    }

    }

    iarray& iarray:perator=(const iarray& rhs)

    {
    if (this != &rhs)
    {

    // ako se objekti razlikuju
    // dealociraj postojecu memoriju


    delete [ ]Sm_arr;

    // zatim alociraj i kopiraj rhs to lhs

    init(rhs.m_arr, rhs.m_size);
    }
    return *this; // vrati referencu
    }


    Primjena:
    iarray a;
    a[0]S= 1;
    for(int i = 1; i < a.size(); i++)
    a[i]S= 7;

    2.12.9 Operatori pretvorbe tipova
    Opci oblik deklaracije operatora pretvorbe tipova je:

    class Aclass {
    ....
    public:
    operator tip () {...}; // operator pretvorbe u tip
    }


    Deklaracija operatora pretvorbe tipova se razlikuje od uobicajenih deklaracija u dvije stvari.

    1. ne deklarira se vrijednost funkcije, vec
    deklaracija zapocima s rijecju operator,
    2. funkcija pretvorbe parametara se definira bez parametara.
    Primjerice, može nam biti korisno da se u nekim izrazima objekti tipa Counter tretiraju kao
    cjelobrojne varijable koje sadrže vrijednost brojaca. To smo do sada realizirali na nacin da smo
    koristili clansku funkciju get_count().

    int square(int i) {return i*i;}
    int main() {
    Counter c;
    c++;
    int i = square(c.get_count());
    ....


    Ako bi u klasi Counter definirali operator pretvorbe u tip int,
    class Counter
    {
    int m_count;
    public;


    ...

    operator int () {return m_count;}

    };
    tada bi bilo moguce prethodni insert programa zapisati u obliku:

    int square(int i) {return i*i;}

    int main() {

    Counter c;

    c++;

    int i = square(c); //c se tretira kao int

    ....

    jer kompajler pri pozivu funkcije square raspolaže s operatorom pretvorbe objekta klase Counter
    u vrijednost tipa int.

    Ova tehnika programiranja je dosta kritizirana u akademskim krugovima, jer se njome zaobilazi
    stroga kontrola tipova.

    2.12.10. Funkcijski objekti
    Funkcijski objekt se implementira kao klasa u kojoj je definiran operator poziva funkcije ().

    class Fclass {

    ....

    public:

    tip operator () (parametri);

    }

    Objekti ove klase mogu se upotrebljavati kao funkcije:

    Fclass Fobj;
    Fobj(argumenti ..) // funkcijski objekt – vrši poziv funkcije


    Regularne funkcije mogu imati proizvoljan broj argumenata, stoga je operator () iznimka medu
    operatorskim funkcijama, jer se može definirati s proizvoljnim brojem argumenata ukljucujucii
    predodredene parametre.


    U slijedecem primjeru definirana klasa increment. Pomocu nje je definiran funkcijski objekt incr.
    Koristimo ga kao obicnu funkciju (vidimo da incr(n) predstavlja poziv funkcije definirane u
    klasom increment).

    class increment

    {

    // generic increment function

    public :

    int operator() (int x) const { return ++x;}

    };

    void f(int n, const increment& incr)
    { cout << incr(n) << endl;
    }
    int main()
    {
    int i = 0;
    increment incr;
    f(i, incr); // ispisuje 1
    return 0;


    }

    Koncept preopterecenja operatora je temeljni element implementacije apstraktnih tipova
    podataka.


    Preoptereceni operatori uzrokuju iste efekte kao clanske i globalne funkcije: mogu se
    naslijediti i mogu se višestruko preopteretiti.

    Preoptereceni operator može imati fiksni broj parametara, s tim da se ne smije deklarirati
    predodredene parametre (izuzetak su funkcijski objekti).

    Pravila asocijativnosti i prioriteta se preuzimaju iz pravila koji vrijede za izraze s
    standardnim tipovima.

    Preporucuje se da kontekst primjene preopterecenih operatora odgovara kontekstu
    primjene standardnih operatora.

    Specijalni tip preopterecenih operatora su operatori pretvorbe tipova. Za njih vrijede
    posebna pravila; ne vracaju vrijednost i ne koriste argumente.

    2.13. Prihvat i generiranje iznimki -Exception handling
    2.13.1. try-catch-throw
    Iznimke su situacije u kojima program ne može nastaviti normalan rad. Potrebno je prekinuti
    izvodenje programa te izvodenje prenijeti na neku rutinu koja ce obraditi pogrešku. Pogreške
    mogu nastati ako se dijeli s nulom, ponestane memorije i tako dalje. Rješenje problema je
    mehanizam za rukovanje iznimkama. Ideja je da se program podijeli na dio u kojem može
    nastati iznimka i na dio u kojem se on nastavlja kada nastane iznimka. Kljucna rijec
    try
    oznacava blok programa u kojem može nastati iznimka, a kljucna rijec
    catch oznacava dio
    programa koji ce se izvršiti kada nastane neka iznimka (Za catch-blok se cesto koristi naziv
    exception-handler). Uz kljucnu rijec
    catch u zagradama se navodi parametar koji oznacava
    tip iznimke. Neke iznimke generira izvršni sustav C++ kompajlera, a neke može generirati
    korisnik koristeci kljucnu rijec
    throw.

    Pojednostavljeno se ovaj mehanizam može zapisati u obliku:

    class Iznimka { } // može biti "prazna klasa"
    try { // program koji pokušavamo izvršiti
    if (neka_greška) throw Iznimka();

    // iznimku može generirati i izvršni sustav
    }

    catch (Iznimka){
    // dio programa koji se izvršava kada korisnik
    // generira iznimku tipa Iznimka
    }

    catch (std:exception& e){
    // dio programa koji se izvršava kada nastane
    // iznimka koju generira standardne biblioteka
    }

    catch (...){
    // dio programa koji se izvršava za sve ostale
    // iznimke
    }


    Primjer: U prvom primjeru pokazat cemo kako se generiraju uznimke koje u catch-blok šalju
    vrijednost prostih tipova.

    try {
    int n;
    char *mystring = new(nothrow) char [10];
    if (mystring == NULL)

    throw "Greska alokacije memorije!";
    cout << "Unesi cijeli broj: ";
    cin >> n;
    if (n>9)


    throw n;
    else
    mystring[n]='z';
    }

    catch (int i) {
    cout << "Iznimka: ";
    cout <<"indeks "<<i
    <<" izvan dozvoljenog intervala!"
    << endl;
    }

    catch (char * str) {
    cout << "Iznimka: " << str << endl;
    }

    Rezultat:
    Unesi cijeli broj: 99
    Iznimka: indeks 99 izvan dozvoljenog intervala!


    Svaki catch blok hvata samo iznimke odredenog tipa. Prvi catch blok hvata iznimku tipa int, a
    drugi blok iznimku tip string.



    Primjer: Pokazat cemo kako se koriste korisnicki definirane klase kao parametri catch-bloka.
    Klasu Range_error koristit cemo za generiranje iznimke kada se prekoraci neki dozvoljeni
    cjelobrojni interval:

    class Range_error {

    public:
    Range_error(int i) :m_i(i) { }
    int m_i;

    };

    Pretpostavimo da smo definirali funkciju int2char() koja vraca znakovnu vrijednost ako je
    cjelobrojni argument unutar intervala [0,255], u suprotnom generira se iznimka Range_error.

    char int2char(int i) {
    if(i < 0 || i>255) throw Range_error(i);
    return (char) i;
    }

    Pri korištenju ove funkcije moguca su dva nacina prihvata iznimke. Prvi nacin je da catch ne
    koristi argument:


    try {
    char c = int2char (600);
    }
    catch(Range_error) {
    cout << "int2char: nedozvoljen argument";
    }


    Drugi je nacin da se catch specificira s formalnim parametrom x, pa tada može primati
    argumente:


    try {
    char c = int2char (600);
    }
    catch(Range_error x) {
    cout << " int2char: nedozvoljen argument "


    << x.m_i << endl;
    }


    2.13.2. Neprihvacene iznimke
    Ako nastane iznimka za koju nije osiguran prihvat ( i ako ne postoji catch(...) ) tada se poziva
    specijalna funkcija terminate(), kojom se prekida trenutni proces (program). Zapravo,
    terminate je pokazivac
    na funkciju kojem je predodredena vrijednost na adresu standardne C
    funkciju abort( ), kojom se prekida program. To znaci da nece biti pozvani destruktori
    globalnih i statickih objekata, pa se ovakav tip neprihvacenih iznimaka smatra
    programerskom pogreškom. Moguce je da korisnik sam definira funkciju za prihvat ovakovih
    iznimki. To se vrši pomocu funkcije set_terminate(), koja vraca pokazivac
    na funkciju
    terminate(), a prima kao argument void funkciju koja nema argumenata i koja ce zamijeniti
    funkciju terminate().

    Ugniježdeni try-catch

    Try-catch-blokovi mogu biti ugniježdeni. Primjerice:

    try {

    try {

    // neki kod
    }
    catch (int n) {

    throw; // preusmjeri na vanjski catch-blok
    }
    }

    catch (...) {
    cout << "Iznimka";
    }

    U slucaju ugniježdenih blokova moguce je usmjeriti iznimku iz unutarnjeg catch bloka u
    vanjski catch block. U ovom primjeru smo u tu svrhu u unutarnjem bloku koristili throw bez
    argumenta. Preporuka je da se ne koriste ugniježdeni try-catch blokovi.

    Grupiranje iznimki

    Cesto se iznimke mogu logicno grupirati. Primjerice, za rad s matetematickim operacijama
    mogli bi definirati sljedece:

    class MathErr() {}; // greska u mat. operacijama
    class ZeroDevide: public MathErr {}; // dijeljenje s nulom
    class Overflow: public MathErr {}: // prekoracen maksimum
    void f()
    {

    try {
    //matematicke operacije
    }

    catch(ZeroDevide) {

    // dijeljenje s nulom
    }
    catch(MathErr){

    // prihvaca sve ostale mat. greske
    //osim dijeljenja s nulom
    }
    }


    U ovom slucaju se koristi naslijedivanje za definiranje objekta identifikacije iznimke. Zbog
    toga, u ovom primjeru se dijeljenje s nulom prihvacau catch(ZeroDevide), a ostale
    matematicke iznimke u catch(MathErr) ukljucujuci i Overflow (jer se Overflow izvodi iz
    MathErr).

    Deklaracija funkcija s iznimkama

    Kada deklariramo neku funkciju

    int f ();

    nije poznato da li se u njoj koristi ili ne koristi mehanizam generiranja iznimki.

    int f() throw();

    znaci da se u funkciji f() ne generiraju iznimke, a

    int f() throw(MarhErr);

    znaci da se u funkciji f() generira iznimka MathErr.

    Standardne iznimke

    Standardne iznimke generiraju se iz koda koji stvara sam C++ kompajler ili iz koda
    standardne biblioteke. Deklarirane su pomocu temeljne klase exception, koja je deklarirana u
    zaglavlju <exception>:

    class exception {

    public:
    exception() throw();
    exception(const exception&) throw();
    exception& operator = (const exception&) throw();
    virtual ~exception() throw();
    virtual const char * what() const throw();

    private:
    //
    }
    catch(bad_alloc)
    {
    // greska alokacije
    }
    catch (std::exception & e) // ostale izvršne greške
    {
    cout << "Exception: " << e.what();
    }




    Dozvoljeno kopiratri, ETF Osijek.
    http://beta.etfos.hr/nastava/predmet...&predmet=PR301
    Moj YT kanal | Komp i periferija | Mob: OnePlus 6
    I'm running out of boxes to think outside of.

Slične teme

  1. PHP programiranje
    By deathadder in forum Pomagaj brate!
    Odgovora: 0
    Posljednji post: 07-04-2011, 20:44
  2. Počinjem programiranje!!!
    By Cookie monsta in forum Programiranje i izrada igara
    Odgovora: 12
    Posljednji post: 21-09-2009, 22:05
  3. Zadaci za programiranje??!!!
    By wavefunction in forum Programiranje i izrada igara
    Odgovora: 33
    Posljednji post: 25-06-2009, 14:26
  4. programiranje
    By iceman94 in forum Software
    Odgovora: 2
    Posljednji post: 27-11-2008, 18:17
  5. Web programiranje
    By Berax0r in forum Programiranje i izrada igara
    Odgovora: 24
    Posljednji post: 04-05-2008, 12:18

Pravila postanja

  • Ne možeš stvarati nove teme
  • Ne možeš odgovarati na postove
  • Ne možeš slati privitke
  • Ne možeš mijenjati svoje postove
  •