Presentation is loading. Please wait.

Presentation is loading. Please wait.

Objektno programiranje (C++)

Similar presentations


Presentation on theme: "Objektno programiranje (C++)"— Presentation transcript:

1 Objektno programiranje (C++)

2 Pravila ocjenjivanja Održat će se dva kolokvija. Svaki nosi po 30 bodova. Za prolaz treba imati 25 bodova. Treba predati rješenja dviju domaćih zadaća. Svaka zadaća nosi 20 bodova. Minimum za prolaz je 15 bodova. Popravni kolokvij (maksimum je 45, treba skupiti 20 bodova).

3 Gradivo Ocjene: 45-59 bodova: dovoljan (2) 60-74 boda: dobar (3)
75-85 bodova: vrlo dobar (4) bodova: izvrstan (5)

4 Ciljevi kolegija: Uvod u objektno orijentirano i generičko programiranje kroz programski jezik C++ Detaljna prezentacija C++-a Pripadne standardne biblioteke: STL, Boost Napredne tehnike programiranja, softverski predlošci

5 Gradivo Klase Kontrola kopiranja Operatori Spremnici Iznimke Imenici
Nasljeđivanje Polimorfizam RTTI Predlošci

6 Nastavni materijali Stranica kolegija: Vježbe: Preporučuju se i slideovi kolegija Računarski praktikum 4 (Botinčan, Petričević, Puljić).

7 Literatura Osnovna: Stanley B. Lippman, Josée Lajoie, Barbara E. Moo: C++ Primer, Fourth Edition, Addison Wesley Professional, 2005. Julijan Šribar, Boris Motik: Demistificirani C++, 2. izdanje, Element, Zagreb, 2006. Bruce Eckel: Thinking in C++, 1. i 2. dio (besplatna e- knjiga) :

8 Literatura Napredna: Bjarne Stroustrup: The C++ Programming Language, Addison Wesley, 2000. Scott Meyers: Effective C++, Third Edition, Addison-Wesley, Scott Meyers: More Effective C++, Addison-Wesley, 1996. Herb Sutter: Exceptional C++, Addison Wesley, 2000. Stephen C. Dewhurst: C++ Common Knowledge, Addison Wesley, 2005. Erich Gamma, et. all: Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series).

9 Povijest Programski jezik C++ nastao je razvojem jezika C i zadržava visoku kompatibilnost s njime. Kreator jezika je Bjarne Stroustrup ( Razvoj je započeo g.; ime C++ skovano je godine, a prva komercijalna implementacija pojavila se 1985. Između i jezik je doživio veće inovacije: zaštićeni članovi, parametrizirane klase, višestruko nasljeđivanje itd. Jezik je godine definiran standardnom koji nosi ime ISO/IEC 14882:2003. Planira se novi standard C++0x.

10 Objektno orijentirano programiranje
U centru interesa softverskog inženjerstva su složene aplikacije. Problemi: Održivost -- mogućnost lakog održavanja koda. Proširivost -- mogućnost lakog dodavanja nove funkcionalnosti.

11 Tehnike programiranja
Proceduralna: Razlaganje programa na manje cjeline -- implementacija pomoću funkcija procedura. Nemogućnost definiranja apstrakcije koncepata iz aplikacijske domene. Ne nudi podršku za postizanje održivosti i proširivosti. "Svemoguća" main funkcija.

12 Tehnike programiranja
Objektno orijentirana: Klase predstavljaju koncepte iz aplikacijske domene. Program se sastoji od objekta (instance klasa) i njihove komunikacije. Objekti imaju punu odgovornost za svoje ponašanje.

13 Osnovni elementi OO tehnike:
Apstrakcija -- Koncepti se reprezentiraju neposredno u programu, a izvedbeni detalji su skriveni iza sučelja (eng. interface) koje reprezentira koncept. Enkapsulacija -- Sposobnost osiguravanja da se apstrakcija koristi prema svojim specifikacijama. Enkapsulacijom se sprečava narušavanje apstrakcije, odnosno prodiranje implementacijskih odluka izvan granica apstrakcije. Polimorfizam -- Skrivanje različitih implementacija iza istog sučelja. Osigurava široku primjenjivost koda i reducira zavisnost o implementaciji. Nasljeđivanje -- Konstrukcija novih apstrakcija polazeći od već postojećih.

14 Generičko programiranje
Pojam generičkog programiranja odnosi se na generalizaciju softverskih komponenti kako bi se lako mogle koristiti u različitim situacijama. Osnovni elementi generičkog programiranja u C++-u su parametrizirane klase i funkcije.

15 Reference Referenca na neki objekt je novo ime za taj objekt. Osnovna uloga reference je omogućiti prijenos parametara po referenci, dakle prijenos u kojem nema kopiranja parametra, već funkcija dobiva referencu na objekt. Referenca spada u tzv. složeni tip (engl. compound type), tip koji se gradi pomoću drugih tipova. Tu spadaju još pokazivači (na neki tip) i polja (nekog tipa). Budući da referenca uvijek mora referirati na neki objekt, slijedi pravilo: Prilikom deklaracije referenca mora biti inicijalizirana. int n=100; int &rn = n; float ℞ // Greška pri kompilaciji. Neinicijalizirana referenca

16 Reference Na jedan objekt možemo imati više referenci, a kako je svaka referenca samo novo ime za objekt, svaka promjena objekta putem jedne reference vidljiva je i kroz sve druge. int n = 100; int &rn1 = n; int &rn2 = n; std::cout << “n= "<< n << std::endl; //100 std::cout << “rn1= "<< rn1 <<“, rn2=“<<rn2<< std::endl; //100,100 rn1++; std::cout << “n= "<< n << std::endl; //101 std::cout << “rn1= "<< rn1 <<“, rn2=“<<rn2<< std::endl; //101, 101

17 Reference Referenca može biti konstantna i tada može referirati na konstantan objekt. Obična referenca ne može referirati na konstantu: char &ra = 'a'; // greška pri kompilaciji. // Nekonstantna referenca, konstantan objekt. const char &rb = 'b'; // o.k. Konstantna referenca nam garantira da kroz nju ne možemo promijeniti objekt na koji referira. Referenca je dakle isto što i pokazivač koji se automatski dereferencira. Za razliku od pokazivača, referenca koja referira na jedan objekt nikad ne može referirati na neki drugi objekt.

18 Reference Korištenjem referenci donekle ograničavamo prirodan način korištenja implicitnih konverzija. Na primjer, sljedeći kod javlja grešku pri kompilaciji. double x = 2.71; int &rx = x; S druge strane, sljedeći javlja samo upozorenje o konverziji double u int (jer je naravno moguć gubitak podataka): const int &rx = x;

19 Reference Kada prevoditelj treba referencu na jedan tip inicijalizirati objektom nekog drugog, kompatibilnog, tipa, on kreira privremeni objekt istog tipa kao i referenca, vrši konverziju i inicijalizira referencu tim privremenim objektom. Kod koji prevoditelj generira izgleda, dakle, ovako: double x = 2.71; int tmp = x; const int &rx = tmp; Nekonstantna referenca nekog tipa može biti inicijalizirana jedino objektom egzaktno tog istog tipa.

20 Reference Referencu možemo definirati i na pokazivač. int n = 100;
int *pn = &n; int *&rpn = pn; // Referenca na pokazivač std::cout << "*rpn = "<< *rpn << std::endl; Nije dozvoljeno deklarirati pokazivač na referencu, a isto tako ni referencu na referencu. Polje referenci ne postoji. double &x[8];  // Polje referenci nije dozvoljeno – neispravno. Razlog je taj što svaka referenca mora biti inicijalizirana nekim objektom, što pri definiciji polja nije moguće.

21 Reference void f(int *p) { std::cout<<*p<<std::endl; p++;
} void g(int * & p) int a=3; int *ptr=&a; std::cout<<ptr<<std::endl; f(ptr); g(ptr);

22 Reference U C++ u koristimo reference kad ne želimo prijenos parametara po vrijednosti. Ako je formalni argument funkcije deklariran kao referenca nekog tipa, onda prilikom poziva funkcije neće doći do kopiranja stvarnog argumenta u formalni, već će formalni argument (koji je referenca) biti inicijaliziran stvarnim. Formalni argument tako postaje alias za stvarni argument i kroz njega možemo dohvatiti stvarni argument. Na taj se način postiže prijenos parametara po referenci. void swap(int &x, int &y) {      int tmp = x;      x = y;      y = tmp; } Prijenos po referenci vršimo kad želimo mijenjati stvarni argument ili kad je argument suviše velik za kopiranje.

23 Reference U drugom slučaju, kad samo želimo izbjeći kopiranje stvarnog argumenta, formalni argument treba deklarirati kao konstantnu referencu. S druge strane, kako je moguće konstantnu referencu inicijalizirati nekonstantnim objektom (const je samo obećanje da referenca neće služiti za mijenjanje objekta), funkcija koja deklarira konstantnu referencu može uvijek uzeti nekonstantan argument (koji, naravno, ne može u svom tijelu promijeniti).

24 Reference void print(std::string& text) {
std::cout << text << std::endl; }     std::string a("...");     print(a);       // o.k.      print("...");   // greška Bolje je dakle pisati: void print(const std::string& text) {     

25 Reference Ako je argument funkcije deklariran kao nekonstantna referenca, onda pri pozivu funkcije nije dozvoljena konverzija tipova za taj argument. Formalni i stvarni argument moraju biti istog tipa. Kad je referenca konstantna, onda je, kao što smo vidjeli, konverzija dozvoljena. Treba imati pri tome na umu da će prevoditelj generirati privremeni objekt i da će formalni argument referirati na njega.

26 Reference Kako se polja ne mogu kopirati, ne možemo napisati funkciju koja ima parametar tipa polje. Kad koristimo polje u izrazima, ono se automatski konvertira u pokazivač na prvi član polja. Sljedeće deklaracije su ekvivalentne. Interpretiraju se kao funkcija koja kao parametar prima pokazivač tipa int*. void print( int * ){ …..} void print( int[ ] ){ …. } void print( int[100] ){ …… }

27 Reference Funkcija koja uzima polje kao argument dobiva pokazivač na prvi element polja. Takva funkcija može uzeti polje bilo koje dimenzije. S druge strane, moguće je deklarirati funkciju koja uzima referencu na polje: int count(int (&arr)[4]) {      int x = 0;      for(int i=0; i< 4; ++i) x += arr[i];      return x; } Funkcija count će uzimati samo polja dimenzije 4, jer nema implicitnih konverzija između polja različite veličine. Referenca na polje je dakle manje fleksibilna od pokazivača na polje, pa se stoga rijetko koristi kao parametar funkcije.

28 Reference Ako funkcija vraća referencu, tada ne dolazi do kopiranja vrijednosti, već samo do kopiranja reference. Za velike objekte to može biti velika ušteda. Referenca kao povratna vrijednost ima još i tu prednost da predstavlja vrijednost koja se može naći na lijevoj strani znaka jednakosti (lvalue). char& get(std::string& s, unsigned i) {       return s[i]; }     std::string s1("abc");      get(s1,2)='z';

29 Reference Moramo paziti da nikad ne vratimo referencu na lokalnu varijablu jer će ona nakon vraćanja reference na nju biti uništena. Isto naravno vrijedi i za pokazivač na lokalnu varijablu.   // Neispravan kod std::string&  ccn(const std::string& s1, const std::string& s2){     std::string tmp = s1+s2;      return tmp; // greška } Funkcija ne smije nikada vratiti referencu ili pokazivač na lokalnu varijablu.

30 Pokazivači na funkcije
Kao i ostali pokazivači, i pokazivač na funkciju pokazuje na točno određeni tip funkcije. Pritom se uzima u obzir i povratni tip i lista parametara. Primjer: Pokazivač na funkciju koja prima const string referencu i int, a vraća int, deklariramo na sljedeći način: int (*fptr)(const string &, int ); Napomena: zagrade oko *fptr su nužne jer je int *fptr(const string &, int ); deklaracija funkcije koja ima jednaku listu parametara i vraća pokazivač na int.

31 Pokazivači na funkcije
Zbog kompliciranosti sintakse, preporučljivo je uvesti novo ime za pokazivač na funkciju: typedef int (*fptrType)(const string &, int ); Ukoliko koristimo ime funkcije za poziv funkcije, ona se i tretira kao funkcija. U ostalim slučajevima se automatski tretira kao pokazivač na funkciju pripadnog tipa.

32 Pokazivači na funkcije
Neka je deklarirana odgovarajuća funkcija: int f(const string &, int ); Pokazivač na funkciju može se inicijalizirati samo s funkcijom ili pokazivačem na funkciju jednakog tipa ili s konstantnim izrazom jednakim nula. Ne postoji konverzija između pokazivača na funkcije različitih tipova (moraju se slagati i lista parametara i povratni tip). fptrType ptr1=f; ptr1=&f; fptrType ptr2=0; ptr2=ptr1;

33 Pokazivači na funkcije
Možemo uvesti novo ime i za sam tip funkcije. typedef int (*fptrType)(const string &, int ); typedef int fType(const string &, int ); int f(const string &, int ); fptrType fptr1=f; fType *fptr2=fptr1;

34 Pokazivači na funkcije
Pokazivač na funkciju koji nije inicijaliziran ili ima vrijednost nula ne može se koristiti za poziv funkcije. Pri pozivu funkcije možemo koristiti i pokazivač na funkciju i dereferencirani pokazivač. int f(const string &s, int a){ cout << s; return ++a; } fptrType fptr=&f; int a=(*fptr)("Hello",1); int b=fptr("Hello",1);

35 Pokazivači na funkcije
Pokazivač na funkciju može biti i parametar neke funkcije. Pritom su ekvivalentne sljedeće deklaracije: typedef int (*fptrType)(const string &, int ); void Test( int , int(const string &, int ) ); void Test( int , int (*) (const string &, int ) ); void Test( int , fptrType );

36 Pokazivači na funkcije
Funkcija može i vratiti pokazivač na funkciju. Pritom povratna vrijednost mora biti baš pokazivač, ne može biti sama funkcija. Preporučljivo je koristiti typedef za tip pokazivača. Ekvivalentno je: int (*f( int ))(const string &, int ); fptrType f( int ); Nije dozvoljeno deklarirati funkciju tipa fType ff( int );

37 Pokazivači na funkcije
¸Moguće je definirati i polje pokazivača na funkcije. void f1( int i) { cout<<"Funkciji f1 proslijedili ste broj "<<i; } void f2( int i ) { cout<<"Funkciji f2 proslijedili ste broj "<<i; } void f3( int i ){ cout<<"Funkciji f3 proslijedili ste broj "<<i; } void (*fp[3])( int )={ f1,f2,f3 }; fp[1](13);

38 Pokazivači na funkcije
Zadatak: Definirane su funkcije bool ascending ( int a, int b ) { return a<b ; } bool descending ( int a, int b ) { return a>b ; } Napišite definiciju funkcije deklariranu s void Sort ( int [ ] , const int , bool(*) (int, int)); koja sortira polje int-ova u rastućem ili padajućem poretku. Napišite main koji testira funkciju.

39 Konverzije Izrazi mogu biti sastavljeni od operanada različitih tipova ukoliko se svi mogu konvertirati u odgovarajući zajednički tip. Prevoditelj će prije izvršenja operacije izvršiti konverziju operanada u taj zajednički tip. Budući da se radi o konverzijama koje se rade bez eksplicitnog zahtjeva od programera govorimo o implicitnim konverzijama.

40 Konverzije Implicitne konverzije se dešavaju u ovim situacijama:
Kod aritmetičkih, relacijskih i logičkih izraza. Kod testiranja u if, while, for i do while naredbama dolazi do konverzije u tip bool. Kod izraza pridruživanja (=) dolazi do konverzije u tip varijable na lijevoj strani. Ako pri tome dolazi do gubitka preciznosti, konverzija je svejedno legalna, jedino što će prevoditelj dati upozorenje. Kod poziva funkcije, ako stvarni i formalni argumenti nisu istog tipa.

41 Konverzije Najčešće su aritmetičke konverzije. Osnovno pravilo je da dolazi do konverzije u najširi tip u izrazu s ciljem da se sačuva preciznost rezultata. Integralna promocija: svi se integralni tipovi manji od int (char, signed char, unsigned char, short, unsigned short) pretvaraju u int, ako je to moguće, a ako ne, onda u unsigned int. Kada se tip bool pretvara u int, onda se true pretvara u 1, a false u nulu.

42 Konverzije Ostale konverzije:
U većini izraza polje se konvertira u pokazivač na prvi član polja. To se ne dešava kod primjene adresnog operatora i sizeof operatora te kad se poljem inicijalizira referenca na polje. U izrazima testiranja pokazivač se pretvara u bool tip: null-pokazivač se konvertira u false, svaki drugi u true. Svaki pokazivač se može konvertirati u void *. Nula (0) se može konvertirati u pokazivački tip. Enumeracija se konvertira u integralni tip koji je strojno zavisan. Nekonstantan objekt se može konvertirati u konstantan. Pokazivač na konstantan tip može se inicijalizirati adresom nekonstantnog objekta. Napomena: klase definiraju svoje vlastite konverzije.

43 Konverzije Od prevoditelja možemo eksplicitno zahtijevati da napravi konverziju tipova, ukoliko je takva konverzija dozvoljena. U C-u bismo to učinili izrazom (T) izraz; gdje je T tip u koji konvertiramo izraz. Jednako je dozvoljen izraz oblika T(izraz). Primjer: izbjegavanje cjelobrojnog dijeljenja.     int x = 3;      int y =4;      double z=3.24;      z = x/y; // cjelobrojno dijeljenje      z = double(x)/y; // realno dijeljenje ili      z = (double) x/y; // realno dijeljenje

44 Konverzije Ista je sintaksa dozvoljena i C++-u, no ovdje se umjesto jednoga uvodi četiri specijalizirana operatora konverzije: static_cast<T>(izraz); const_cast<T>(izraz); dynamic_cast<T>(izraz); reinterpret_cast<T>(izraz); Prednost specijaliziranih operatora je bolja dijagnostika grešaka, a ekspresivnija sintaksa omogućava lakše uočavanje eksplicitnih konverzija u kodu.

45 Konverzije static_cast<T> služi za sve one konverzije koje prevoditelj radi implicitno. Većinu tih konverzija on može učiniti i u suprotnom smjeru. Na primjer, svaki se pokazivač implicitno može konvertirati u pokazivač na void, ali obratna konverzija se ne dešava automatski. Moguće ju je tražiti eksplicitno:     void *pv = &x;      int *pi;        pi = pv;                     // Greška pri kompilaciji      pi = static_cast<int *>(pv); // o.k. Ovaj je kod ispravan samo ako je varijabla x kojom smo incijalizirali pokazivač pi tipa int. Prevoditelj, općenito, nije u stanju detektirati stvarni tip varijable čija je adresa uzeta. Stoga se greške u uporabi eksplicitnih konverzija pokazuju za vrijeme izvršavanja programa.

46 Konverzije Pomoću static_cast<T> operatora ne možemo pretvoriti konstantan objekt u nekonstantan. U tu svhu služi const_cast<T>. const_cast<T>(izraz) uklanja konstantnost izraza. Pretpostavimo, na primjer, da imamo funkciju koja uzima nekonstantnu referencu na tip, ali ne mijenja stvarni argument. Tu funkciju ne možemo pozvati s konstantnim stvarnim argumentom. void f(int& i) {     // ... } // ....      const int x = 3;      f(x);                     // Greška pri kompilaciji f(const_cast<int &>(x)); // o.k const_cast<T> se može primijeniti samo na pokazivačima i referencama.

47 Konverzije dynamic_cast<T>(izraz) se koristi za konverziju pokazivača ili reference na baznu klasu u pokazivač ili referencu na izvedenu klasu. reinterpret_cast<T>(izraz) vrši konverzije zavisne o implementaciji kao što je konverzija pokazivača u int. Radi se o reinterpretaciji niza bitova koja ovisi o sustavu na kojem se vrši i stoga nije prenosiva s računala na računalo. Rijetko se koristi, a ima svoje mjesto u sistemskom programiranju. Eksplicitne konverzije su opasne jer dovode do grešaka za vrijeme izvršavanja koje nije moguće otkriti za vrijeme kompilacije. One često indiciraju grešku u dizajnu programa i najčašće se mogu izbjeći. Stoga ih treba koristiti u najmanjoj mogućoj mjeri.

48 Preopterećenje funkcija
Dvije funkcije u istom dosegu su preopterećene ako imaju isto ime, ali različitu listu parametara. U jeziku C funkcija je na jedinstven način identificirana svojim imenom pa stoga različite funkcije moraju imati različita imena. Primjer: double sin(double) i float sinf(float). U C++ jeziku funkciju identificira njena signatura (potpis) koja se sastoji od imena funkcije te liste njezinih parametara. Nije moguće preopteretiti dvije funkcije samo na osnovu različitog povratnog tipa, odnosno ne mogu postojati dvije funkcije istog imena, broja i tipa parametara, koje bi se razlikovale samo po tipu povratne vrijednosti.

49 Preopterećenje funkcija
U nekim se situacijama parametri, koji se čine različitim, ne tretiraju kao različiti i prevoditelj javlja grešku redefiniranja funkcije. To su sljedeće situacije:

50 Preopterećenje funkcija
Typedef ne uvodi novi tip već samo predstavlja novo ime za stari tip i stoga ne može biti osnova za razlikovanje: void f(double y) { cout << "f(double)"<<endl; } typedef double R; void f(R y) { cout << "f(R)"<<endl; } // Greška, redefinicija

51 Preopterećenje funkcija
Defaultni argument ne smanjuje broj argumenata funkcije pa ne može biti osnova za razlikovanje: void f(int x, int y) { cout << "f(int,int)"<<endl; } void f(int x, int y=0) { cout << "f(int,int=0)"<<endl; } // Greška, redefinicija

52 Preopterećenje funkcija
Kada je const irelevantan, ne može služiti za razlikovanje tipova. U ovom slučaju const je irelevantan jer funkcija dobiva kopiju argumenta i nikako ne može promijeniti original. void f(int x) { cout << "f(int)"<<endl; } void f(const int x) { cout << "f(const int)"<<endl; } // Greška, redefinicija Konstantni i nekonstantni argument mogu služiti za razlikovanje samo kod referenci i pokazivača.

53 Preopterećenje funkcija
Prvi korak: nalaženje kandidata. Prevoditelj nalazi sve funkcije danog imena koje su vidljive na mjestu poziva. Drugi korak: selektiranje među kandidatima onih funkcija koje imaju korektan broj parametara i svi parametri se mogu konvertirati u odgovarajući tip. Time dolazimo do skupa dobrih kandidata (engl. viable functions), tj. onih koji mogu biti pozvani sa zadanim parametrima. Prilikom uspoređivanja broja argumenata uzimaju se u obzir i eventualni defaultni argumenti. Ako je skup dobrih kandidata prazan imamo grešku pri kompilaciji.

54 Preopterećenje funkcija
Treći korak: određivanje najboljeg kandidata. Princip nalaženja je sljedeći: kandidat kod kojeg imamo egzaktno podudaranje argumenta je bolji od kandidata kod kojeg moramo vršiti konverziju argumenta. Kako ima više vrsta konverzija one se rangiraju prema kvaliteti u svrhu definiranja uređaja među kandidatima. Za funkcije s više argumenata najbolji kandidat je onaj koji nije lošiji od drugih niti po jednom argumentu, a u jednom je bolji od svih. Ako na kraju postupka ima više najboljih kandidata, poziv je dvosmislen (engl. ambiguous) i prevoditelj javlja grešku.

55 Preopterećenje funkcija
Rang lista konverzija prema kvaliteti je sljedeća: Egzaktno podudaranje Integralna promocija Standardna konverzija Konverzija definirana u klasi

56 Preopterećenje funkcija
Primjer: void f(int x, int y) { cout << "f(int,int)"<<endl; } void f(double x, double y) { cout << "f(double, double)"<<endl; f(1.2, 2);

57 Preopterećenje funkcija
Primjer: void f(int x, int y) { cout << "f(int,int)"<<endl; } void f(double x, double y) { cout << "f(double, double)"<<endl; f(1.2, 2); Poziv je dvosmislen. Obje funkcije su dobri kandidati.

58 Preopterećenje funkcija
Primjer: void f() { cout << "f()"<<endl; } void f(int x) { cout << "f(int)"<<endl; } void f(int x, int y) { cout << "f(int,int)"<<endl; } void f(double x, double y=0.0) {cout << "f(double, double=0)"<<endl;} f(1.2);

59 Preopterećenje funkcija
Primjer: void f() { cout << "f()"<<endl; } void f(int x) { cout << "f(int)"<<endl; } void f(int x, int y) { cout << "f(int,int)"<<endl; } void f(double x, double y=0.0) {cout << "f(double, double=0)"<<endl;} f(1.2); Bit će pozvana funkcija f(double, double) jer ne traži niti jednu konverziju.

60 Preopterećenje funkcija
Često se funkcija preopterećuje na osnovu toga što jedna verzija uzima konstantnu referencu, a druga nekonstantnu. To je legalno jer prevoditelj uvijek može odrediti koju funkciju pozvati. void f(int & x) { cout << "f(int &)"<<endl; } void f(const int & x) { cout << "f(const int &)"<<endl;

61 Preopterećenje funkcija
Ako funkcija dobiva referencu na const objekt, onda je poziv korektan jedino ako funkcija definira argument kao konstantnu referencu; verzija funkcije koja deklarira argument kao nekonstantnu referencu ne garantira da neće promijeniti objekt. Ako pak imamo referencu na nekonstantan objekt, onda su obje funkcije dobri kandidati i obje mogu biti pozvane. Ali, funkcija s nekonstantnim argumentom ne traži konverziju argumenta, dok kod funkcije s konstantnim parametrom treba prvo konvertirati referencu na nekonstantan objekt u referencu na konstantan objekt. Prema tome, verzija s nekonstantnom referencom je bolja te se poziva.

62 Preopterećenje funkcija
Posve je analogna situacija s pokazivačima na konstantan i nekonstantan objekt. void f(int * x) { cout << "f(int *)"<<endl; } void f(const int * x) { cout << "f(const int *)"<<endl;

63 Preopterećenje funkcija
Napomena: Kada funkcija uzima referencu na neki tip, a dobije referencu na neki drugi tip koji je s njim vezan, onda prevoditelj radi konverziju tako što kreira privremeni objekt i predaje funkciji konstantnu referencu na taj objekt. U tom se dakle slučaju poziva verzija funkcije s konstantnom referencom i ako takva nije definirana dolazi do greške pri kompilaciji. void f(int &a) {std::cout<<"f(int &)";} void f(const int &a){std::cout<<"f(const int &)";} …. double d=0.0 f(d);

64 Preopterećenje funkcija
Ne možemo preopteretiti funkciju obzirom na to uzimaju li pokazivač ili konstantan pokazivač (ne pokazivač na const), jer funkcija ionako dobiva kopiju pokazivača. void f(int * x) { cout << "f(int *)"<<endl; } void f(int * const x) { cout << "f(int * const)"<<endl; } // Greška, redefinicija

65 Preopterećenje funkcija
class Klasa{ int i; public: Klasa (int a):i(a){ }; }; //void f(int i) { std::cout<<“ f( int ) ”;} void f(double i) { std::cout<<“ f( double ) ";} void f(Klasa i) { std::cout<<“ f( Klasa )";} int main() { Klasa k(2); f(2); f(1.2); f(k); return 0; }

66 Preopterećenje funkcija
class Klasa{ int i; public: Klasa (int a):i(a){ }; }; //void f(int i) { std::cout<<“ f( int ) ”;} //void f(double i) { std::cout<<“ f( double ) ";} void f(Klasa i) { std::cout<<“ f( Klasa )";} int main() { Klasa k(2); f(2); f(1.2); f(k); return 0; }

67 Preopterećenje funkcija
class Klasa{ int i; public: Klasa (int a):i(a){ }; }; void f(int i) { std::cout<<“ f( int ) ”;} void f(double i) { std::cout<<“ f( double ) ";} //void f(Klasa i) { std::cout<<“ f( Klasa )";} int main() { Klasa k(2); f(2); f(1.2); f(k); return 0; }

68 Preopterećenje funkcija
void g( int i ) { cout<<"int"; } void g( long i ) { cout<<"long"; } void g( float i ) { cout<<"float"; } void g( double i ){ cout<<"double"; } int main() { short a=1; g(a); return 0; }

69 Preopterećenje funkcija
void g( int i ) { cout<<"int"; } void g( long i ) { cout<<"long"; } void g( float i ) { cout<<"float"; } void g( double i ){ cout<<"double"; } int main() { short a=1; g(a); return 0; } Poziva se g(int ) jer integralna promocija ima prednost.

70 Preopterećenje funkcija
void g( long i ) { cout<<"long"; } void g( float i ) { cout<<"float"; } void g( double i ){ cout<<"double"; } int main() { short a=1; g(a); return 0; }

71 Preopterećenje funkcija
void g( long i ) { cout<<"long"; } void g( float i ) { cout<<"float"; } void g( double i ){ cout<<"double"; } int main() { short a=1; g(a); return 0; } Poziv funkcije je dvosmislen.


Download ppt "Objektno programiranje (C++)"

Similar presentations


Ads by Google