[ fresh.bm @ 21.12.2007. 21:34 ] @
Početnik sam u cpp-u,
trebam savjet u vezi rešenja zadatka;



zadatak je sledeći:

Definisati klasu Bin koja omogućuje manipulaciju prirodnim brojevima u binarnom brojnom sistemu. Svaki objekat ove klase karakteriše niy binarnih cifara. Maksimalan broj cifara u nekom objektu ove klase treba da definiše jedan zajednički privatni atribut dim.
Klasa treba da sadrži sljedeće funkcije članice(unutar definicije klase navesti prototipove funkcije članice, a definicije iznan definicije klase):

1. konstruktor koji podrazumjevano kreira binarni podatak čija je vrijednost 0. Inače, binarni podatak može da se inicijalizuje na osnovu cjelobrojne vrijednosti koju konstruktor može da primi kao argument.
2. operatorska funkcija za kastovanje binarnog podatka u tip int.
3. operatorska funkcija kojom se preklapa operator >, a koja vraća rezultat poređenja dva binarna podatka. Funkcija ne treba da ispisuje, nego da vrati odgovarajući rezultat.
4. operatorska funkcija kojom se preklapa operator ~, a koja vraca kao rezultat binarni podatak koji bi se dobio komplementiranjem datog binarnog broja. treba da vrati odgovarajući rezultat, ne da ga ispise.
5. operatorska funkcija kojom se preklapa operator za prefiksno inkrementovanje binarnog podatka.

Klasa treba da ima i sledeće prijateljske funkcije:
1. operatorska funkcija kojom se preklapa operator <<, a koja ispisuje binarni podatak. Omogućiti kaskadno povezivanje.
2. operatorska funkcija kojom se preklapa operator >>, a koja direktno ucitava binarni podatak kao bilo koji podatak nekog standardnog tipa. Omogućiti kaskadno pozivanje.
3. cifra koja ima mogućnosti da prebroji i kao rezultat vrati broj jedinica u nekom binarnom podatku. Funkcija ne treba da ispisuje, nego da vrati odgovarajući rezultat.

U main-u treba:
1. omogućiti korisniku da postavi maksimalan broj cifara u binarnim podacima.
2. sa standardnog ulaza učita niz(A) binarnih podataka(niz A može da sadrži do 100 elemenata, a treba da se alocira statički). Zatim na standardnom izlazu ispisati taj niz binarnih podataka.
3. sa standardnog ulaza učitavati niz prirodnih brojeva, dok se ne unese 0, pa na osnovu njega formirati niz(B) binarnih podataka koji su ekvivalentni učitanim brojevima(niz B se alocira dinamički). Potom na standardnom izlazu ispisati niz B u opadajućem (nerastućem) redosljedu.
4. Formirati niz C kojeg čine drugi komplement binarnih podataka iz niza B (inkrementovan prvi komplement), pa ispisati taj niz.

primjer programa:

___________________________
Unesite DIM? 5

Koliko ima binarnih podataka u nizu A? 3
1. podatak? 1100
2. podatak? 101
3. podatak? 10001
Niz A: 01100 00101 10001

Unesite 1. prirodan broj? 12
Unesite 2. prirodan broj? 3
Unesite 3. prirodan broj? 8
Unesite 4. prirodan broj? 1
Unesite 5. prirodan broj? 0
Niz B u nerastućem redosljedu:
01100 01000 00011 00001

1. komplement: 10011 10111 11100 11110
2. komplement: 10100 11000 11101 11111

[Ovu poruku je menjao fresh.bm dana 22.12.2007. u 12:09 GMT+1]
[ Sephiroth? @ 22.12.2007. 11:48 ] @
OK, procitao sam zadatak ... Samo ne kontam sta oko cega ti treba pomoc, jer nisi naveo sta ti je jasno,a sta nije! Razumijes li preklapanje operatora? Dinamicku alokaciju? Klase, friend princip? De malo opisi oko cega ti tacno treba pomoci, jer je sve lijepo opisano u zadatku sta treba da se uradi... Nakon toga, tu smo za sve ostalo!

Btw, sto se tice samog zadatka, moje misljenje je da je najbolje da spremas binarne brojeve u varijable tipa string, tako da mozes jednostavno da pristupas pojedinim ciframa, kao i da manipulises obicnom string rijeci. Pretvaranje iz string->int bi bilo takodjer jednostavno na ovaj nacin, ako netko ima bolji prijedlog neka kaze.

Sam izgled klase bi bio nesto ovako:

Code:


class Bin
{
 string binarni_broj;
 int dim;
  .
  .
  .

    public:
 Bin();
  .
  .       // ostali moguci konstruktori, copy konstruktori, destruktori ...
  .
 
 bool operator>(const Bin & neki_broj_za_poredjenje)
  .
  .      // ostali preoptereceni operatori...
  .

 //itd
};

[ fresh.bm @ 22.12.2007. 19:17 ] @
Evo ovako, ja sam pokusao realizovati klasu tako sto sam binarni broj predstavio kao niz karaktera.
Razmisljao sam o resenju preko stringa, ali ne znam raditi sa tom klasom;

Takodje mi je problem dinamicka alokacija memorije;

Preklapanje operatora znam, problem mi je bio preklapanje >> i kaskadno povezivanje.

Ne znam ni napraviti konstruktor (ovaj konkretan);

Ako bi mi mogao poslati citav kod programa pa da vidim kako se to realizuje u kompletu;

[ icobh @ 22.12.2007. 19:34 ] @
Ovo mi je poznato od nekud. Da nisi bio na kolokviju kod Brđe?
[ fresh.bm @ 22.12.2007. 19:38 ] @
Jesam, zeznuo me i na vjezbama pretprosle sedmice sa stringom,
isto sam uradio program VelikiBroj sa nizom!!!
Dao mi je samo bod, kao za trud...

Na pripremi smo radili matricu, od prosle godine.
[ Sephiroth? @ 22.12.2007. 19:52 ] @
Eh, sad, lijepo pise na pocetku da ne treba traziti da netko rijesi zadatak umjesto tebe... Ako treba pomoci, nema problema, ali nemoj pretjerivati... Sto se tice datog konstruktora, trebao bi ici ovako nekako:

Code:


Bin::Bin(int int_broj, int max_cifara)
 {
    dim = max_cifara;
  
  for (int i=0; i<dim; i++)                                      // popuni string sa ciframa 0, po defaultu
    binarni_broj += "0";
  
  if ( int_broj > (pow(2,dim)-1))                            // provjera da li dati broj moze stati u binarni broj velicine dim
   //throw(error())                                              // 2^dim je max broj koji se moze spremiti, ako je dim 3 znaci
   cout << "ERROR\n";                                         // 2^3 je 8, tj. ne moze se spremiti broj veci od 8
  else
   {
    int index = dim - 1;                                        // index koji nam sluzi za pomicanje unazad po binarni_broj
    int rez = int_broj;                                          
    
    while(rez != 0)                                              // standardni algoritam pretvaranja int u binarni broj
     {  
      if ((rez % 2) == 1)
       binarni_broj[index--] = '1';
      else
       binarni_broj[index--] = '0';
      rez = rez / 2;
     }
    
    while (index != -1)                                         // popunjavanje ostatka binarni_broj sa nulama
     {
      binarni_broj[index--] = '0';
     }
   }
 }



Sto se tice same manipulacije stringovima, veoma je jednostavno. Puno je jednostavnije raditi sa stringovima nego sa nizovima znakova. Preporucam ti da za vjezbu pokusas sam napraviti string klasu.

Evo primjera:

Code:


#include <iostream>
using namespace std;

int main(void)
 {
  string S;
  
  cin >> S;
  
  cout << S << "\n";
  
  cout << "Slovo na poz 3: " << S[3] << "\n";
  
  S += "rijec";
  
  cout << S << "\n"; 
 
  system("pause");
  return EXIT_SUCCESS;             
 }

[ fresh.bm @ 23.12.2007. 09:52 ] @
Hvala, jos samo da mi pokazes dinamicku alokaciju.

Kako da sortiram niz B?
Gdje da smjestim niz prirodnih brojeva na osnovu kojeg cu formirati niz B, da li se taj niz formira dinamicki?
Ne vidim kako bi se drugacije i mogao napraviti, ne poznajem unaprijed broj clanova tog niza.
[ Sephiroth? @ 23.12.2007. 13:41 ] @
Dinamicka alokacija je relativno jednostavna za pocetak. Imas dvije funkcije (operatora) koje sluze za manipulaciju memorijom. To su new i delete (ili jos iz starog C-a nesto slicno: malloc() i free()). Kada se kaze “dinamicka” memorija obicno se misli na heap, tj. dio memorije koji se koristi za dinamicko zauzimanje i oslobadjanje, u nekom najprostijem konceptu naravno… Probaj google malo dublje, ili u knjizi nekoj…

Uglavnom, dinamicka alokacija ne radi bez pointera. New i delete rade samo sa pointerima. Kolicina memorije koja se zauzima zavisi od velicine tip varijable koju zelis zauzeti ( sizeof() ), broja clanova nekog niza ako se radi o nizu, velicini strukture/klase/datoteke …

Prosto:

Code:


int *a;

a = new int(4);

cout << *a << endl;



Kasnije, za brisanje sadrzaja pointera (ali ne i samog pointera !) koristi se delete:

Code:


delete a;



Ako neku dinamicki alociranu memoriju ne “izbrises”, stvara se nesto sto se zove curenje memorije. Morati ces malo sire o tome naci na netu, tema je dosta siroka…

Uglavnom, jos samo par stvari… Da bi zauzeo neki niz brojeva potrebno ti je:

Code:


int *a = new int[10];

// kasnije i delete [] a;
// da bi izbrisao niz!

// Nakon ovoga clanovima pristupas kao i normalnom nizu:

a[0] = 1;
a[1] = 99;

cout << a[1] << endl; //itd…



Kako ovo iskoristiti u zadatku, to je drugo pitanje... Vidjet cu ako danas budem imao vremena pa cu ti napisati par koncepta ...
[ Sephiroth? @ 23.12.2007. 14:55 ] @
E sto se tice zadatka samog... Uradio sam jos sinoc Bin klasu, vjezbe radi :) Prilozicu cijeli program u attachmentu sa klasom, evo ovdje samo Bin dio:

Code:


class Bin
{
 int dim;
 string binarni_broj;
 
    public:
 Bin();
 Bin(int max_cifara);             
 Bin(int max_cifara, int int_broj);
 Bin(const Bin&);
 ~Bin() {}
 
 string getBinarniBroj()           const { return binarni_broj; }
 void   setBinarniBroj(string set)       { binarni_broj = set; }
 void   setBinarniBroj(int set)          ;
 int    getDim()                   const { return dim; }
 
 bool operator > (const Bin &);
 Bin operator ~ ();
 const Bin& operator ++();
 operator int();

 friend int count(const Bin& toCount);
 friend ostream& operator << (ostream& theStream, Bin& binary);
 friend istream& operator >> (istream& theStream, Bin& binary);
};



IMPLEMENTACIJE :

Code:


//==================== konstruktori ==================//


Bin::Bin()
 {
  dim = 4;                        // default vrijednost za dim, proizvoljno
  
  for (int i=0; i<dim; i++)  
    binarni_broj += "0";         
 }



Bin::Bin(int int_u_binarni)
 {
  dim = 4;
  
  for (int i=0; i<dim; i++)
    binarni_broj += "0";
  
  this->setBinarniBroj( int_u_binarni );
 }



Bin::Bin(int max_cifara, int int_broj)
 {
    dim = max_cifara;
  
  for (int i=0; i<dim; i++)
    binarni_broj += "0";
  
  this->setBinarniBroj( int_broj );
 } 



Bin::Bin(const Bin &rhs)
 {
  dim = rhs.getDim();
  binarni_broj = rhs.getBinarniBroj();
 }



Code:


//=================== get() i set() ===================//


void Bin::setBinarniBroj(int set)
 {
  if ( set > (pow(2,dim)-1))
   //throw(error())
   cout << "ERROR\n";
  else
   {
    int index = dim - 1;
    int rez = set;
    
    while(rez != 0)
     {
      if ((rez % 2) == 1)
       binarni_broj[index--] = '1';
      else
       binarni_broj[index--] = '0';
      rez = rez / 2;
     }
    
    while (index != -1)
     {
      binarni_broj[index--] = '0';
     }
   }
 }



Code:


//==================   operatori    ===================//

bool Bin::operator > (const Bin &rhs)
 {
  string rhs_binarni_broj = rhs.getBinarniBroj();
  
  for (int i=0; i<dim; i++)
   {
    if ( (binarni_broj[i] == '1') && (rhs_binarni_broj[i] == '0') )
     return true;
    if ( (binarni_broj[i] == '0') && (rhs_binarni_broj[i] == '1') )
     return false;
   }
  return false;
 }



Bin Bin::operator ~ ()
 {
  Bin temp(*this);
  string komplement = temp.getBinarniBroj();
  
  for (int i=0; i<dim; i++)
   {
    if (komplement[i] == '1')
     komplement[i] = '0';
    else
     komplement[i] = '1';
   }
  
  temp.setBinarniBroj( komplement );
  return temp;
 }



const Bin& Bin::operator ++ ()
 {
  for (int i=dim-1; i >= 0; i--)
   {
    if (binarni_broj[i] == '1')
     binarni_broj[i] = '0';
    else
     {
      binarni_broj[i] = '1';
      return *this;
     }
   }
  return *this;
 }



Bin::operator int () 
 {
  int int_rez = 0;
  int eksponent = 0;
  
  for (int i=dim-1; i >= 0; i--)
   int_rez += ( (binarni_broj[i] - '0') * ( (int) pow(2, eksponent++) ));
  return int_rez;
 }



Code:


// ================== friend funkcije ! ======================//

int count(const Bin &toCount)
 {
  int num = 0;
  string temp = toCount.getBinarniBroj();
  
  for (int i=0; i < toCount.getDim(); i++)
   if ( temp[i] == '1') 
    ++num;
   
  return num;
 }


ostream& operator << (ostream& theStream, Bin& binary)
 {
  theStream << binary.getBinarniBroj();
  return theStream;
 }


istream& operator >> (istream& theStream, Bin& binary)
 {
  string temp;
  theStream >> temp;
  binary.setBinarniBroj( temp );
  return theStream;
 }

[ Sephiroth? @ 23.12.2007. 15:00 ] @
Omogucene su sve funkcije trazene u programu, ukljucujuci kaskadnog povezivanja cin i cout, castovanja itd... Nisam implementirao main() dio, ali misim da je jako jednostavan sa ovom klasom da se uradi. Posto sebe smatram pocetnikom, a posto je ovo prvi put da sam implementirao neke koncepte (friend, operatori << i >>), molio bih da date komentare na apsolutno sve dijelove koda, sta bi poboljsali, promijenili, izbrisali, dodali ...

Veliki nedostaci koje sam ja primjetio :

- prema postavci zadatka, svaka Bin instanca ima svoj dim, koji je nepromjenjiv u programu... Bas zbog ovoga nisam se trudio implementirati dijelove kao rukovanje sa 2 Bin objekta razlicitih dim vrijednosti. Ako imamo 0101 i 101010, i stavimo 0101 > 101010 ? nece se dobiti odgovarajuci rezultat. Zbog podrazumijevanog dim da je svagdje isti, nema potrebe za ovim.
- u programu nema nikakve exception obrade, to razumijem, znam istu implementirati, nisam to uradio da ne komplikujem
- ostalih stvari se iskreno ne mogu sjetiti, ali primjetio sam jos toga...

Sad me zanima vase misljenje, oko SVEGA. Da li sam jos negdje trebao staviti const, smaknuti mozda? Svi prijedlozi/sugestije/kritike, slobodno ih iznesite!

Pozdrav!
[ fresh.bm @ 23.12.2007. 15:30 ] @
Puno ti hvala na ovome,

sto se tice dim promjenjive u pravu si da ne treba implementirati obrade objekata sa razlicitim dim-ovima;

Ako opet sta zapne, javit cu se.
Pozdrav
[ Sephiroth? @ 23.12.2007. 17:12 ] @
Samo jos nesto, pitao si sta sa sortiranjem niza B... E ovako, sto se tice samog sortiranja, tu imas velik izbor... Sortiranje je jako olaksano implementaciom operatora >. Moguci pristupi (naravno kada bi radio neki slozeniji projekat) zavise striktno od resursa koji koriste i njihove brzine. Razliciti algoritmi su ti na raspolaganju:

- bubblesort brzina O(n^2) , najsporiji, najgori ali i najjednostavniji algoritam sortiranja...
- ostali O(N^2) algoritmi sortiranja, ali ipak malo brzi zavisno od slucaja do slucaja: insertion sort, selection sort ...
- neki mozda od najbrzih, quicksort, shellsort itd...

Ako te ova tema oko sortiranja malo vise zanima, pogledaj forum "art of programming".
Najjednostavnija je implementacija bubblesort-a, samo malo izmjeni kod da radi sa binarnim brojevima, sve metode su ti date u Bin klasi, kao argumenat proslijedi niz Bin brojeva...

Code:


// nije testirano!

bubblesort()                   
 {
    bool found = true;
    int temp;
    while(found)
     {
      found = false;
      int iter = nElem-1;
       for (int i=0; i<iter; i++)
        {
         if (a[i] > a[i+1])
          {
           // switch!
           temp = a[i];
           a[i] = a[i+1];
           a[i+1] = temp;
           found = true;
          } 
        }
       iter--; 
     } 
 }

[ k.bojan @ 23.12.2007. 19:39 ] @
Citat:

class Bin
{
int dim;
string binarni_broj;

public:...


sto se tice zadatka od Sephiroth?-a ja imam jednu primjedbu...u zadatku pise da postoji jedan zajednicki atribut "dim"
kao sto se vidi u primjeru izvrsavanja programa dim se unosi na samom pocetku, sto znaci da treba da bude staticka promjenljiva.jednom se unosi i stalna je do kraja izvrsavanja programa,sem ako se ne navede drugacije(znaci trebalo bi ici "static int dim").

A sto se tice poredjenja nije problem jer je dim kod svih objekata isti,a i da je razlicit to se lako rijesi konverzijom izmedju tipova.Npr
operator int()const;
kada ovo preklopis on vrsi konverziju iz bin u int , a konstruktor vrsi konverziju iz int u bin, tako da ako imas dva objekta sa razlicitim dim-ovima(sto nije slucaj u zadatku) onda npr ako imas 10011100011 i 10110 odradis kastovanje oba objekta u int i samo poredis dva int-a zar ne ;)?

ja licno ne bih smjestao niz 0 i 1 u string vec preko pokazivaca...
evo dole interfejs klase kako bi ja uradio...
Jos nisam preklopio operator dodele i konstruktor kopije, a svi znamo gdje ima pointera ima i konstruktor kopije, operator dodjele i destruktor ;)
Pozdrav

P.S. Ako sam negdje pogrjesio bicu zahvalan ako mi neko ukaze na gresku




Code:

class Bin
{
    friend ostream &operator << (ostream &,const Bin);
    friend istream &operator >> (istream &, Bin &);
    friend int cifra(const Bin);
private:
    int *binArray;
    static int dim;
public:
    Bin();
    Bin(int);
    ~Bin();
    operator int()const;
    bool operator > (const Bin &) const;
    Bin operator~()const;
    Bin operator++();
    static void SetDim(int);
};



[Ovu poruku je menjao k.bojan dana 23.12.2007. u 21:01 GMT+1]
[ Sephiroth? @ 23.12.2007. 20:35 ] @
Da, slazem se sa static int dim... Ali kao sto sam i rekao, velik problem je u postavci zadatka...

Ovako:

a) sto se tice dim vrijednosti, mislim da bi najbolje rjesenje bilo da svaki Bin ima svoj dim, ali, da svaki binarni_broj odredjuje dim. Znaci ako je Bin a = 3, (binarno 11) dovoljan je dim = 2 da se spremi broj. Pri inkrementaciji, i svim ostalim opcijama trebalo bi da se manipulise sa ovim dim atributom klase tako da odgovara broju spremljenom u klasu. Drugi pristup bi bio da je dim jedinstven, npr 32, kao u arhitekturi 32-bit sistema... Slazem se da u zadatku dim treba biti static
btw, kada bi svaki dim jedinstveno odgovarao svakom binarnom broju, kod uporedbe bi bilo jednostavno provjeriti samo ciji je dim veci, jer je i taj broj veci... Ako su dim vrijednosti jednake, onda se moze primjeniti algoritam napravljen vec iznad.


b) sto se tice stringova ili pokazivaca... Ne bih znao sa sigurnoscu reci. Koliko znam stringovi u pozadini rukuju sa dinamicki alociranim nizovima znakova. Koristenjem string klase se otklanja dosta posla oko problema curenje memorije, jednostavnosti implementacije itd... Cinjenica je da pristup i sa pokazivacima ne bi bio narocito tezak za implementirati, ali koliki je dobitak time ako string-ovi znacajno pojednostavljuju stvar?

Uostalom da li je isplativije spremiti 20 Bin objekata koji se sastoje od ( int binarni_broj[5] ) ili 20 Bin objekata sa ( char binarni_broj[5] ). Sizeof(int) je 4 byte, dok je za char 1 byte, a stringovi rade na char nizovima.


Zasada samo par stvari oko tvoje deklaracije klase:

1) Upravu si da
Code:

friend ostream &operator << (ostream &,const Bin);

prima const Bin, ali zasto praviti dodatnu kopiju umjesto prosljedjivanja reference

Code:

friend ostream& operator << (ostream& theStream,const Bin& binary);


2) Zar
Code:
Bin operator++();

ne bi trebao vracati referencu na samog sebe, jer se on sam inkrementira?
Code:
const Bin& operator ++();


Pozdrav !
[ k.bojan @ 23.12.2007. 21:02 ] @
Citat:
ne bi trebao vracati referencu na samog sebe, jer se on sam inkrementira?

Slazem se...moja greska...trebao bi vracati referencu na samog sebe...osim ako bi bilo nesto kao
Code:

const Bin operator ++()
{
      Bin tmp;
      ...
      return tmp;
}
[/quote]
ali slazem se da je bolje 
Code:
const Bin& operator ++();

jer onda nema podrebe za dodatnim objektom a samim tim i usteda memorije i povecanje brzine programa...

sto se tice
Code:
friend ostream &operator << (ostream &,const Bin);

potpuno se slazem da nema potreba za pravljenjem kopije objekta i u pravu si da je mnogo efikasnije
Code:
friend ostream& operator << (ostream& theStream,const Bin& binary);


Citat:
Uostalom da li je isplativije spremiti 20 Bin objekata koji se sastoje od ( int binarni_broj[5] ) ili 20 Bin objekata sa ( char binarni_broj[5] ). Sizeof(int) je 4 byte, dok je za char 1 byte, a stringovi rade na char nizovima.


iskreno ovo mi nije palo na pamet :(
ovo je mnogoooooo bolje nego prego int *
iz razloga koji si naveo...manje zauzimanje memorije :)))

slazem se da bi najbolje bilo da svaki Bin ima svoj dim ako nista drugo zbog poredjenja i manje memorije...ali zadatak zahtjeva staticki dim(sto je nepotrebno)
kada za svaki ucitani string uradis strlen(ime_stringa) i dobijes dim :)))

Pozdrav
Hvala na korekcijama ;)
u svemu se potpuno slazem
[ Sephiroth? @ 23.12.2007. 21:21 ] @
Jedino moram malo prouciti ovo sa friend, to mi je potpuno novo, znas ono kada nesto zaobilazis za ucenje a ne znas ni sam zasto... ;) Razumijem potpuno za sta se koristi (u principu), ali ne razumijem zasto ovi operatori moraju biti friend. Program savrseno lijepo radi i bez postavljanja da su ove funkcije friend (komentiraj njihove prototipe u deklaraciji klase). Moram malo procitati oko toga i pronaci par primjera, nadam se da ce mi onda stvari biti jasnije... Pozdrav!
[ k.bojan @ 23.12.2007. 21:44 ] @
U C++ ne postoje naredbe niti operatori za ulaz i izlaz podataka, već se ulaz i izlaz realizuju
odgovarajućim klasama.


Svakoj otvorenoj datoteci, kao i standardnom ulazu/izlazu pridružuje se po primjerak jedne od
tih klasa. Pristup nekoj datoteci ostvaruje se pristupanjem tim objektima putem njihovih
funkcija članica ili prijateljskih funkcija.


Sve potrebne deklaracije vezane za klase za ulaz i izlaz podataka nalaze se u <iostream.h>
Klasa za ulaz podataka naziva se istream. Klasa za izlaz podataka naziva se ostream.
Objekat klase istream za pristup standardnom ulazu (tastaturi) zove se cin.
Objekat klase ostream za pristup standardnom izlazu (monitor) zove se cout.


Ova dva objekta (cin, cout) automatski se stvaraju na početku izvršavanja svakog programa.
Operatori za čitanje i pisanje podataka (uz primjenu ulazno-izlaznih konverzija) definisani su za
sve standardne tipove podataka preklapanjem operatora >> i <<.


Prototipovi odgovarajućih operatorskih funkcija su:
istream & operator>> (istream &dat, Tip &data);
data je odredište za podatke i ne moze biti const
(za sada cin)
ostream & operator<< (ostream &dat, const Tip &data);
data je izvor podataka
(za sada cout)


Obje funkcije vraćaju upućivač na datoteku dat (za sada cin i cout). Ovi upućivači su ujedno i
prvi operandi operatorskih funkcija. Ovo omogućava kaskadno (lančano) pozivanje operatora
>> i <<, odnosno prenošenje više podataka jednim izrazom. Ovo je moguće jer je njihova
asocijativnost slijeva udesno.


Programer može preklapanjem operatora >> i << da obezbijedi operatore za U/I konverzije
svojih klasa. Ove funkcije moraju da budu prijateljske funkcije korisničkim klasama. Ne mogu
da budu funkcije članice, jer prvi objekat nije korisnički definisan!

Evo jedan jednostavni primjer

Code:

#include <iostream.h>
class Complex
{
friend ostream & operator<< (ostream &dat, const Complex &z)
{ return dat << "(" << z.real << "," << z.imag << ")" ; }
public:
Complex (double re=0, double im=0) : real(re), imag(im) {}
private:
double real, imag;
};
main()
{
Complex z(1,1);
cout << "z = " << z << endl;
}


Nadam se da sam bar malo pomogao
Pozdrav
[ Sephiroth? @ 23.12.2007. 22:02 ] @
Mehanizam rukovanja sa I/O operacijama razumijem...

Citat:
k.bojan: ... Ove funkcije moraju da budu prijateljske funkcije korisničkim klasama. Ne mogu
da budu funkcije članice, jer prvi objekat nije korisnički definisan! ...


Ovo me jedino buni... Kako mislis? Ne kontam te... Kakva je primjena friend za funkciju count() u gornjem zadatku onda?

[ k.bojan @ 23.12.2007. 22:24 ] @
Citat:
Sephiroth?...Kakva je primjena friend za funkciju count() u gornjem zadatku onda?

Nikakva...ova funkcija je mogla da bude implementirana i kao funkcija clanica...ali u zadatku pise da ta funcija treba da bude implementirana kao prijateljska,pa sam je zato tako i implementirao.

Citat:
Ove funkcije moraju da budu prijateljske funkcije korisničkim klasama. Ne mogu
da budu funkcije članice, jer prvi objekat nije korisnički definisan! ...


Ovo me jedino buni... Kako mislis? Ne kontam te...


E pazi objekat ostream je objekat neke klase koju nije korisnik definisao vec je definisana u C++ library.
Pretpostavimo da u toj biblioteci imamo apstraktnu klasu IO koja definise ulazni izlazne tokove...e sada nju nasledjuju dve klase Input i Output.
E sada iostream je objekat klase Input koja je nasljedila IO i samim tim vec definisana u library...
Znas i sam da je IO realizovan preko klase u C++u kao i sve druge operacije...kao sto je operator + preklopljen za koprisnicki definisane tipove kao sto su int,char,float,double
Znaci ti ne mozes da pises nesto kao:
Code:

istream &operator(Tip &data){...}

jer
kada napises npr
Code:

bool operator==( const Bin &a ) const;

ovdje se podrazumjeva da je objekat s kojim ces vrsiti poredjenje(poredjenje tvog objekta i Bin-u ovom slucaju su oba Bin) da li je jednak,je objekat tvoje klase,tj korisnicki definisane
tj.
prvom objektu ces pristupati kao
binArray[index] direktno
a drugom
a.binArray[index] indirektno
i onda ces da ih poredis
a ti kod preklapanje operatora << treba da uzmes podatak Bin i da ga ispises na cout(koji nije korisnicki definisan), tj ti ne mozes private podacima clanovima da pristupas direktno,vec samo indirektno kao sto sam ti naveo u predhodnom primjeru(a to je definisano standardom).

Nadam se da si uspio da skontas...ako nisi reci sta ti nije jasno pa cu pokusati da ti objasnim (onoliko koliko mogu jer i sam sam nov u ovome :))

P.S.
Moguce je da sam negdje i pogresio
U svakom slucaju provjeri...a i ja cu...ako nesto saznam ostavicu post ovdje
Pozdrav

[Ovu poruku je menjao k.bojan dana 23.12.2007. u 23:51 GMT+1]
[ icobh @ 23.12.2007. 22:53 ] @
Prijateljske f-je se koriste onda kada je poželjno da se zaobiđu neki prioriteti odnosno da se nešto stavi u isti položaj.

Npr. operator "+" se može preklopiti kao f-ja članica klase ali se onaj drugi operand stavlja, da tako kažem, u potlačen položaj:
Code:
A.operator+(B);


Onda se pribjegava impelementaciji sa prijateljskom funkcijom gdje su operandi u istom položaju:
Code:
operator+(A,B);
[ Sephiroth? @ 23.12.2007. 23:20 ] @
Meni je samo problem da skontam zasto onda ta funkcija/operator (whatever) mora biti friend, kada i ne mora :) Mislim, ne sumnjam da razlog postoji, samo ga ja jos ne vidim. Ako za sva 3 friend clana :

Code:

class Bin
{
 .
 .
 .
 friend int count(const Bin& toCount);
 friend ostream& operator << (ostream& theStream, Bin& binary);
 friend istream& operator >> (istream& theStream, Bin& binary);
};

// implementacije gornjih clanova


postavimo komentare:
Code:

class Bin
{
 .
 .
 .
 //friend int count(const Bin& toCount);
 //friend ostream& operator << (ostream& theStream, Bin& binary);
 //friend istream& operator >> (istream& theStream, Bin& binary);
};

// implementacije gornjih clanova


program radi! Ja sam mislio da sa friend omogucavamo funkcijama da pristupe odredjenim podacima klase, tj. kao da su i pripadnice te klase, ali kada pokusam iz bilo koje ove funkcije pristupiti npr. binarni_broj, izbaci mi error. Ne mogu pristupiti nicemu, niti varijablama niti metodama... Sad se pitam za sta uopste sluzi ovo friend (u ovome zadatku). Ne vidim neophodnost primjene friend kada sve radi i bez toga. Mozda je stvar ovdje u nekakvom manipulacjiom memorijom? Da se ove funkcije uklope nekako u ove objekte? Iskreno, ne znam... Znam da cu pogledati malo, jer bezveze samo postavljam pitanja a nisam niti pogledao neke osnovne primjere tako da se ne trudite objasniti, iako jako zahvaljujem na pomoci. Ako nadjes Bojane nesto korisno, postali ovdje, i ja cu uraditi isto kad se naspavam :)) Pozdrav!

EDIT: Naravno, pomocu drugog argumenta (Bin &) operatorima proslijedjujemo sve potrebne informacije da bi obavili posao kako treba, zasto ce nam onda friend?
[ fresh.bm @ 24.12.2007. 21:29 ] @
Imam pitanje u vezi rješenja, imali potrebe za četiri konsturktora ili je se to moglo rješiti preko jednog?

A što se tiče funkcije

Code:
void   setBinarniBroj(string set)       { binarni_broj = set; }


primjetio sam ako je uneseni broj manji od dim, npr.

ako je dim=5, a unesemo 0011 ili 001,
prilikom ispisivanja dobit ćemo:
0011 i 001
a ne
00011 i 00001,
kako je prikazano u zadatku;

Znam da se na prvi način štedi memorija, ali radi učenja kako da dodam te nule na početak?
[ Sephiroth? @ 24.12.2007. 21:57 ] @
Eh, ovako... Sto se tice konstruktora, odgovor je i da i ne. Od 4 konstruktora prvi je prazan konstruktor (podrazumjevano kreira bin 0), drugi se koristi za cast iz int u Bin, kao i za kreiranje samo nekog Bin broja bez dim vrijednosti (default dim = 4). Treci, najvazniji, je tipicni konstruktor kreacije Bin objekata. Cetvrti, copy konstruktor, se ovdje uopste ne treba implementirati, jer bi compiler sam napravio odgovarajuci shallow-copy copy konstruktor jer se u objektu ne koriste pointeri. E sada, da li su sva 4 potrebna? 4. nije. Prvi se moze rijesiti tako da stavimo u 3. default vrijednosti argumenata (u slucaju da se pozove prazan konstruktor) itd... Ipak, zasto onda postoje 4? Zato da se ("eventualna") kasnija nadopunjavanja koda lakse izvrse. Treba znati gdje je sta, da se kasnije lako moze nadograditi kod. Sa pristupom default argumenata samo cemo kasnije zakomplicirati sve.
Sto se tice ostatka, kao sto sam vec napisao negdje prije, program pati od ozbiljnog nedostatka sa ovim dim-ovima. Dati metod setBinarniBroj(string) se samo oslanja na to. Procitaj prethodne postove. Zasto bi on trebao mjenjati dim ako je dim nepromjenjiv u zadatku? Ako bi ti trebalo da neki uneseni broj kao 101 zbog dim 5 spremis u 00101, onda jednostavno unutar setBinarniBroj(string) moras dodatno provjeriti da li je broj koji unosis <= dim objekta. Broj spremis u binarni_broj, ostatak popunis nulama. Nesto slicno imas u metodi setBinarniBroj(int set)... Javi ako zapne, pozdrav!
[ k.bojan @ 25.12.2007. 13:47 ] @
Za Sephiroth?-a:

Evo mali isjecak iz jedne knjige o friend funkcijama:

Razmotrimo definicije preopterecenih operatora jedanosti za klasu String koji su definisani u oblasti vazenja prostora imena. Operator jednakosti za dva objekta klase string je:
Code:

bool operator==(const String &str1, const String str2)
{
   if( str1.size() != str2.size() )
       return false;
   return strcmp( str1.c_str(), str2.c_str() ) ? false : true;
}


Uporedite ovu definiciju sa definicijom istog operatora kada je on definisan kao funkcija clanica:
Code:

bool String::operator==(const String &rhs ) const
{
   if( _size != rhs.size() )
       return false;
   return strcmp( _string, shs._string ) ? false : true;
}

Da li vidite razliku? Uocite da smo u ovoj definiciji morali da promenimo nacin na koji se koriste privatni podaci clanovi klase String. Posto je novi operator jednakosti globalna funkcija, a ne funkcija clanica klase, on ne moze da direktno koristi privatne podatke clanove klase String. Zbog toga, za dobijanje velicine objekta klase String i njemu odgovarajuce niske znakova u stilu jezika C, on koristi pristupne funkcije clanice size() i c_str();
Drugo moguce resenje je da se ovi globalni operatori jednakosti deklarisu kao prijatelji klase String. Ako neka klasa deklarise funkciju ili operator kao prijatelje, ona toj funkciji ili operatoru daje pravo pristupa do svojih nejavnih clanova.
Deklaracija prijatelja pocinje kljucnom reci friend. Ona moze da se nalazi samo unutar definicije klase. Posto prijatelji nisu clanovi klase koja proglasava prijateljstvo, na njih ne utice to da li su unutar tela klase deklarisani u javnoj, privatnoj ili zasticenoj sekciji. U sledecem primeru izabrali smo da sve deklaracije prijatelja grupisemo odmah posle zaglavlja klase:
Code:

class String{
   friend bool operator==(const String &, const String &);
   friend bool operator==(const char *, const char *);
   friend bool operator==(const String &, const char *);
public:
   //...ostatak klase String
};

Ove tri deklaracije prijatelja u klasi String deklarisu tri preopterecena operatora poredjenja koja su deklarisana u globalnoj oblasti vazenja kao prijatelji klase String.
Sada posto su operatori jednakosti deklarisani kao prijatelji, njihova definicija moze da direktno koristi privatne clanove klse String:
Code:

// operatori koji su prijatelji: mogu da direktno koriste privatne clanove klse String
bool operator==(const String &str1, const String &str2)
{
   if( str1._size() != str2._size() )
       return false;
   return strcmp( str1._string(), str2._string() ) ? false : true;
}

inline bool operator==(const String &str1, const char *s)
{
   return strcmp( str._string, s ) ? false : true;
}

Neko moze sa pravom da tvrdi da, u ovom slucaju, direktan pristup clanovima _size i _string nije neophodan, posto funkcije size() i c_str(), kao umetnute, daju jednaku efikasnost, a u isto vrijeme obezbedjuju i enkapsulaciju podataka clanova. Koriscenje pristupnih funkcija umjesto direktnog pristupa clanovima ne znaci uvjek i manje efikasnu imeplementaciju. Zbog ovih pristupnih funkcija nema potrebe da deklariseno operatore jednakosti kao funkcije prijaljtelje klase String.
Kako se onda odlucujemo da li neki operator koji nije clan klase treba da bude proglasen prijteljem ili in treba da koristi pristupne funkcije clanice? U opstem slucaju dizajner klase treba da tezi da minimizuje broj globalnih funkcija i operatora koji imaju pristup do interne reprezentacije klase. Ako postoje funkcije za pristup clanovima koje su jednako efikasne, onda je bolje da se one koriste i time izoluju globalni operatori od promjena u reprezentaciji klase, bas kao sto se to radi sa drugim funkcijama prostora imena. Ako za neke privatne clanove klase ne postoje odgovarajuce pristupne funkcije u lasi, i ako globalni operatori za obavljanje svojih funkcija moraju da koriste te privatne clanove, onda koriscenjem mehanizma prijatelja postaje neophodno.
Najcesca primjena mehanizma prijatelja je da se preopterecenom operatoru koji nije clan klase dozvoli pristup do privatnih clanova te klase ciji je on prijatelj. Razlog za ovo, pored potrebe da se omoguci simetrija koriscenja levog i desnog operanda, jeste sto bi preopterecen operator i inace morao da bude funkcija sa punim pristupom do privatnih clanova klase.
Mada je dominantna primjena mehanizma prijatelja ona za preopterecene operatore, postoje slucajevi kada funkcija prostora imena, funkcija clanica neke druge prethodno definisane klase ili citava klasa moraju da se deklarisu kao prijatelji. Kada se jedna klasa proglasava prijateljem druge, funkcije clanice klasa prijatelja dobijaju pristup do nejavnih clanova klase koja proglasava prijateljstvo. Sada cemo deteljnije razmotriti deklaracije prijatelja za funkcije koje nisu operatori.
Klasa mora da deklarise kao prijatelja svaku funkciju iz skupa preopterecenih funkcija koje zeli da ucini prijateljima. Na primjer:
Code:

extern ostream& storeOn( ostream &, Screen & );
extern BitMap& storeOn( BitMap &, Screen & );
//...

class Screen {
   extern ostream& storeOn( ostream &, Screen & );
   extern BitMap& storeOn( BitMap &, Screen & );
   //...
};


Ako funkcija manipulise objektima dvije razlicite klase i potreban joj je pristup do nejavnih clanova obe klase, ta funkcija moze bilo da se deklarise obe klase, bilo da se ucini funkcijom clanicom jedne a prijateljem druge...
Primjer:
Ako odlucimo da funkcija mora da bude prijatelj obe klase, ove deklaracije prijateljstva izgledaju kao na primjer:
Code:

class Window;//samo deklaracija
class Screen {
   friend bool is_equal ( Screen &, Window & );
   /...
};

class Window {
   friend bool is_equal ( Screen &, Window & );
   /...
};

Ako odlucimo da funkcija mora da bude clanica jedne klase a prijatelj druge klase, deklaracija funkcije clanice i deklaracija prijatelja su na primjer:
Code:

class Window;
class Screen {
   public:
   // copy je funkcija clanica klase Screen
   Screen& copy ( Window & );
   //...
};

class Window {
   // Screen::copy je prijatelj klase Window
   friend Screen& Screen::copy( Window & );
   // ...
};

Screen& Screen::copy( Window & ) { /* ... */ }

Funkcija clanica neke klase ne moze da se deklarise kao prijatelj druge klase pre definicije ove prve klase. ovo se ne moze uvjek postici. Na primjer, sta ako klasa Screen mora kao prijatelje da deklarise funkcije clanice klase Window, a klasa Window isto mora kao prijatelje da deklarise funkcije clanice klase Screen? U ovom slucaju, citava klasa Window moze da se deklarise kao prijatelj klase Screen. Na primjer:
Code:

class Window;
class Screen {
   friend class Window;
   /...
};

Svaka funkcija clanica klase Window sada ima pristup svim ne javnim clanovima klase Screen;




Nadam se da sam pomogao, i da ces uspjeti da shvatis svrhu friend. Ako nesto jos treba javi...
Pozdrav
[ Sephiroth? @ 25.12.2007. 21:45 ] @
Kako je ovo meni smjesno sada :)) Hvala PUNO na ovome, ali kad nekome nesto ne ide u glavu... Shvatio sam, kljucni dio:

Code:

// razlika izmedju

str1._string

// i

str.getString()


joj... Ja sam izgleda bio shvatio poentu friend, ali sam napravio ogromnu pogresnu pretpostavku! Ja cijelo vrijeme kontam da se funkcije napravljene sa friend ukljucuju ravnopravno medju ostale public metode klase. Mislio sam da uopste ne trebamo proslijedjivati objekat friend funkciji, jer naravno (po mojoj bivsoj logici) funkcija ima pristup njemu zato sto je U NJEMU, ali pa naravno nije... U cemu bi inace i bio smisao, pa friend samo omogucuje da vidimo privatne dijelove proslijedjenog objekta! Uh! Nemam pojma zasto sam ovako to pogresno zakljucio, :(. Hvala ti na ovome, trebalo mi je samo da vidim par primjera, iako sam mislio to veceras preci temeljno, ustedio si mi truda, ovako samo nastavljam dalje. Moram naravno pogledati jos oko toga svega... Malo offtopic, ali zanimljivo je da je Java potpuno izbacila friend princip zbog narusavanja koncepta "sakrivanja informacija". Iskreno, i ne vidim velik znacaj ovog friend koncepta, ali naravno on negdje postoji (pretpostavljam radi performansi), pa je i zato i ukljucen u C++... Ko nece da ga koristi i ne mora! Pozdrav!

P.S promijenicu kod iz zadatka, sada kontam da su friend metode pogresno napravljene! Prikacicu sutra...
[ k.bojan @ 25.12.2007. 22:40 ] @
Drago mi je da sam pomogao...
A sto se tice jave...tako nesto nam je profesor na predavanjima pricao...ja licno javu ne volim...ne znam zasto...mozda zasto sto su svi sa kojima sam pricao o njoj rekli nesto tipa "ma java je losa" puna bagova...a i uvjerio sam se da je spora...Tako da sto se tice programiranja C,C++,C# meni za sada tri omiljena jezika...
Pozdrav
[ Branimir Maksimovic @ 25.12.2007. 22:45 ] @
Pogledaj klasu bitset iz headera <bitset>. Cini mi se da tamo ima sve sto ti treba.
Sa malo gimnastike mozes izvesti tu tvoju klasu maltene bez ikakvog kodiranja cini mi se
(nema samo 2. komplement).
Zadatak nije bas najjasnije definisan sa obzirom da konverzija u int podrazumeva
da ti dim ne predje odredjenu vrednost (31).
Sta ako je binarni broj veci od toga? Ili ovo podrazumeva da dim bude max 31?
U tom slucaju klasu mozes predstaviti bas sa jednim int-om.

Pozzzzzzzzzzzz!
[ Sephiroth? @ 25.12.2007. 23:10 ] @
@ bojan:

Nije Java losa, ali naravno nije niti za poredjene sa C++-om. Java je po meni samo Internetski jezik, iako se za mnogo toga jos moze koristiti, ali tu svrhu najbolje oponasa, dok se C++ moze koristiti za sve, zato jeste i najobuhvatniji i sa najvise opcija, iako je naravno uvijek brze koristiti jezik namjenjen za neko podrucje... C# ~= Java.

Citat:
"ma java je losa" puna bagova
to je ipak malo i vise nego pretjerano
[ fresh.bm @ 25.12.2007. 23:12 ] @
Opet ja, probao sam nesto napraviti i to izgleda ovako:

Code:
 int cBin::set_number(string input) 
{
     
     int len = input.length();
     
     if (len > dim) 
     {
             //Error;
             return 0;
     }
     else 
     {
      string temp;
      
      for (int i=0; i<(dim-len); i++)
          temp += '0';
      
      number = (temp+input);
      return 1;
     }


// dim i number su atributi klase cBin, dim je int, a number je string;
// dok je set_number member funkcija klase cBin;

Pitanje, da li se ovo može rešiti na jednostavniji način?
pod jednostavnijim mislim elegantnijim načinom.

// ovo je vezano za ono pitanje kada unesem 011 da mi ispiše 00011, u zavisnosti od dim;
[ fresh.bm @ 25.12.2007. 23:12 ] @
Opet ja, probao sam nesto napraviti i to izgleda ovako:

Code:
 int cBin::set_number(string input) 
{
     
     int len = input.length();
     
     if (len > dim) 
     {
             //Error;
             return 0;
     }
     else 
     {
      string temp;
      
      for (int i=0; i<(dim-len); i++)
          temp += '0';
      
      number = (temp+input);
      return 1;
     }


// dim i number su atributi klase cBin, dim je int, a number je string;
// dok je set_number member funkcija klase cBin;

Pitanje, da li se ovo može rešiti na jednostavniji način?
pod jednostavnijim mislim elegantnijim načinom.

// ovo je vezano za ono pitanje kada unesem 011 da mi ispiše 00011, u zavisnosti od dim;
[ Sephiroth? @ 26.12.2007. 20:47 ] @
Evo ovako… Ovo je veoma malena metoda, stoga neces puno dobiti na ukupnim performansama cijelog programa da je ubrzas i za 200%. Da radis 10 puta veci program, i da pozivas ovu metodu konstatntno, onda bi to vec bilo nesto. Elegantno rjesenje ne znaci nuzno i najefikasnije. Prost primjer toga ti je rekurzija, iako veoma elegantna, u 95% slucajeva je inferiorna svim ostalim tehnikama kada se radi o istom problemu.
Ucenja radi, evo malo da rasclanimo:

Code:


int cBin::set_number(string input)

/* funkcija ti ne bi trebala za uspjeh ili neuspjeh zadatka vracati jedinice ili nule. To ti je zastarjela tehnika,
iako korisna naravno, ali citljivije ti je ta kao povratnu vrijednost stavis bool, te koristis true i false.
Btw, ovo ti smanjuje velicinu povratne vrijednosti sa 4 byta na 1 byte, ako cemo bas sve gledati.
Ime funkcije ti je lose odabrano, po nekakvoj nepisanoj konvenciji, imena metoda se pisu sa pocetnim malim
slovom, te sa velikim slovom za svaku slijedecu rijec, npr. nekaMetodaKojaNistaNeRadi(), tj setNumber().
set_number bi bolje vazio za neku prostu varijablu kojoj bi trebao takav naziv. */

 {
     //Za slijedece linije koda postoji “elegantnije” rjesenje 
     int len = input.length();
     
     if (len > dim) 
     {
             //Error;
             return 0;      // return true;
     }
     else 
     {
      string temp; 
      
      for (int i=0; i<(dim-len); i++)
          temp += '0';
      
      number = (temp+input);
      return 1;
     }
 } 



Evo nesto drugacije rjesenje:

Code:


bool cBin::set_number(string input) 
 {
  if (input.length() > dim) 
   {
    //Error;
    return false;
   }
  else 
   {
    number = "";
          
    for (int i=0; i < (dim - input.length()); ++i)
     number += '0';
    number.append(input);
    return true;
   }
 }



Objasnjenje: postavis number da je prazan, dodas koliko nula treba, na ostatak nadovezes input pomocu append. Ovo rjesenje nije nista drugacije u konceptu od tvoga, ali sada nemas vise 2 pomocne varijable. append() je najbrze moguce rjesenje za dodavanje stringa, koristim ga samo zato sto sam izbacio jednu dodatnu temp varijablu koja ti je trebala.

Ovaj moj odgovor je samo odgovor na tvoje pitanje, da li postoji drugacije rjesenje. Tvojoj metodi apsolutno nista ne fali. Drugo rjesenje uvijek postoji, i ne mora nuzno biti bolje. Budi siguran u svoj kod, kada ga napises, uvijek se zapitaj cime ga mozes unaprijediti, tako najbolje ucis...
[ fresh.bm @ 27.12.2007. 16:49 ] @
Ok, puno hvala!!
Pozdrav.
[ Sephiroth? @ 28.12.2007. 18:54 ] @
Evo samo code koji sam obecao, kompletna klasa Bin, naravno jos uvijek pati od dim nedostatka (pogledaj gornje postove), ali sada su friend funkcije onakve kakve bi i trebale biti...
[ icobh @ 28.12.2007. 19:34 ] @
Ja 78 bodova na ovom kolokviju. Dosta brate, kakve sam sve gluposti pisao. Još će Brđo da kaže: "Kolega, da je ovo moj kôd, ja bih odmah skočio sa gradskog mosta!"