Neka proširenja C jezika
Referenca
Referenca predstavlja drugo ime neke varijable. Može se shvatiti kao
pokazivač koji se koristi sa jednostavnijom sintaksom. Najčešće se koristi
kao parametar funkcije ili vrijednost koju vraća funkcija.
a) referenca kao parametar funkcije - u slučaju kad se
referenca upotrebljava kao parametar funkcije, prevodilac neizravno prenosi
adresu varijable.
int funkcija(int &referenca)
{ ... }
...
rezultat = funkcija(varijabla);
Ova metoda se koristi kada želimo da funkcija može promijeniti vrijednost
referencirane varijable (a to se može jer referenca pokazuje na isti memorijski
prostor) ili kad funkciji kao parametar šaljemo veliku strukturu koju za
koju ne želimo da se kopira u memoriji.
Ako koristimo referencu za izbjegavanje kopiranja a ne želimo da se
parametar može izmjeniti, poželjno je deklarirati referencu na konstantu:
int funkcija(const int& referenca)
{ ... }
b) funkcija koja vraća referencu - podatak koji funkcija vraća može se proslijediti kao referenca. Tako je moguće i funkciji dodijeliti neku vrijednost.
int& funkcija(int varijabla)
{ ... }
Ovu metodu možemo koristiti npr. u funkciji preko koje možemo i dohvatiti i postaviti vrijednost neke (skrivene) varijable, kao u donjem primjeru:
double& dohvati_ili_promijeni(int
index_polja)
{
return polje[index_polja];
}
...
v = dohvati_ili_promijeni(5); // dohvacamo element sa indexom
5
dohvati_ili_promijeni(4) = v; // pridjeljujemo vrijednost
elementu s indexom 4
Treba paziti da funkcija ne vraća referencu na lokalnu varijablu deklariranu
unutar funkcije, jer
tada možemo pristupiti memoriji koja više nije zauzeta. Tako se za varijablu polje
u gornjem primjeru pretpostavlja da je globalnog dosega.
Elementi C++ jezika
Razredi i objekti
Razredi (klase) se u stvarnom životu mogu prepoznati kao opće imenice
koje predstavljaju skupove elemenata sa nekim zajedničkim svojstvima (npr.
vozilo, građevina, student i sl.). Objekti su pojedini elementi skupa kojega
definira neki razred. Npr. jedan objekt razreda vozilo je autobus, drugi
je avion, itd. Također može postojati razred avion, čiji su objekti npr.
dvokrilac, mlažnjak, Boeing i sl.
U C++-u razredi se definiraju svojim pripadnim elementima: pripadni
podaci i pripadne metode (funkcije). U OOP uvodi se kontrola pristupa članovima
razreda - postoje tri specifikatora pristupa koji se definiraju za grupe
članova:
public - članovi dostupni su svim funkcijama
u programu
private - članovi dostupni su samo pripadnim
funkcijama istoga razreda
protected - članovi dostupni su pripadnim
funkcijama istoga razreda i pripadnim funkcijama svih razreda koji su izvedeni
iz početnoga, a nisu vidljivi u ostalim funkcijama
class Razred
{
public:
int public_podatak;
void Funkcija(void);
private:
double private_podatak;
protected:
char protected_podatak;
}; // mora biti ; na kraju
Pripadni podatak može biti struktura, neki drugi razred, pokazivač
ili referenca na neki drugi razred itd.
U definiciji razreda moraju se nalaziti deklaracije svih pripadnih
metoda, dok se njihove definicije mogu nalaziti i izvan definicije razreda.
U tom slučaju moramo operatorom dosega označiti o čijoj je metodi riječ.
void Razred::Funkcija(void)
{ ... }
Ako je definicija metode napisana unutar razreda, ona će biti inline
funkcija (tj. prevodilac će umetnuti tijelo te funkcije na svako mjesto
gdje se ona poziva). Želimo li da funkcija koju definiramo izvan razreda
bude inline, napisat ćemo ključnu riječ inline ispred imena funkcije.
Objekti definiranog razreda mogu se instancirati na više načina:
Razred objekt;
// objekt kao lokalna varijabla
Razred *pok;
// deklariramo pokazivac na objekt (ne i objekt!)
pok = &objekt;
// pokazuje na postojeci
pok = new Razred;
// stvaramo novi objekt
delete pok;
// brisemo objekt na koji pok pokazuje
Članu definiranog razreda može se pristupiti izvan razreda:
objekt.funkcija();
pok->funkcija();
pok->public_podatak = 1;
U tijelu pripadne funkcije razreda članovima istog objekta se pristupa:
private_podatak = 0.1; // isto kao i
this.private_podatak
funkcija();
//
this->funkcija();
// pristup preko
(*this).funkcija();
// this pokazivaca
drugi_objekt.public_podatak = 0;
this pokazivač služi funkciji razreda da pristupi objektu kojem pripada (npr. ako funkcija vraća objekt kao rezultat).
Konstruktor
Konstruktor je pripadna funkcija koja se izvršava pri inicijalizaciji
objekta. Konstruktor ima isto ime kao i razred, nije definiranog tipa i
ne vraća nikakvu vrijednost. Prevodilac sam generira pretpostavljeni konstruktor
koji ne zahtijeva parametre, ukoliko programer ne napiše vlastiti. U konstruktoru
se obično definiraju pretpostavljene vrijednosti parametara, zauzima memorija
i sl.
Ukoliko se konstruktor poziva s parametrima, inicijalizacija podataka
objekta može se obaviti listom inicijalizatora:
// definicija izvan razreda
Razred::Razred(int _x, int _y)
: x(_x), y(_y);
{ ... }
// gornji zapis radi pridruzivanje kao
dolje:
Razred::Razred(int _x, int _y)
{ this.x = _x;
this.y = _y;
}
Kopirajući konstruktor
Kopirajući konstruktor je pripadna funkcija koja se poziva pri
kopiranju objekta u memoriji. Ukoliko ne napišemo kop. konstruktor, prevodilac
će generirati pretpostavljeni za naš razred. Važno:
ukoliko u razredu koristimo pokazivače ili reference te ručno zauzimamo
dodatnu memoriju (npr. za smještaj polja ili matrice), potrebno je napisati
vlastiti kopirajući konstruktor koji će pravilno kopirati cijeli objekt i
dodatnu memoriju koju objekt zauzima. Npr. ako smo za jedan objekt zauzeli
dodatnih 500 bajtova memorije (za koje postoji pokazivač koji na njih
pokazuje), tada je u novom objektu također potrebno zauzeti jednaku količinu
memorije i pridjeliti joj pokazivač novoga objekta. Ukoliko sami ne definiramo
kop. konstruktor, prevodilac će kopirati samo objekt i tako dobiti dva objekta
koji koriste isti memorijski prostor.
Kopirajući konstruktor kao parametre uzima referencu na objekt tipa vlastitog
razreda koja može biti konstantna (const):
Razred::Razred(const
Razred &izvor) // kopirajuci konstuktor
{ this.x = izvor.x;
this.y = izvor.y;
}
Destruktor
Destruktor je pripadna funkcija koja se izvršava kada objekt treba
obrisati iz memorije (npr. prevodilac poziva destruktore za sve neuništene
objekte na kraju rada programa). Destruktor se definira interno ako ga
sami ne definiramo. Ukoliko naš razred koristi pokazivače ili reference,
trebamo napisati vlastiti destruktor koji će osloboditi zauzetu memoriju
tokom života objekta. Destruktor ima ime razreda sa znakom ~ na početku
i za razliku od konstruktora ne prima parametre.
// destruktor
Razred::~Razred()
{ ...
}
Pridruženi razredi i funkcije
Upotrebom ključne riječi friend može se dopustiti nekom drugom razredu
ili funkciji pristup do private ili protected članova razreda.
class Razred
{ friend class drugiRazred;
friend void treciRazred::funkcija(int);
friend void ObicnaFunkcija(double);
};
Pristup možemo dopustiti svim funkcijama drugog razreda, samo nekim funkcijama razreda ili običnim (globalnim) funkcijama. Pridruživanje nije dvosmjerno; specifikatori pristupa nemaju utjecaja na pridružene razrede i funkcije.
Nadgradnja pripadnih funkcija
Pripadne funkcije (kao i obične) mogu se nadgraditi tako da imaju isto
ime a liste parametara su im različite. Ovisno o navedenim parametrima
u pozivu funkcije, prevodilac poziva odgovarajuću. U OOP često se nadgrađuju
kostruktori.
class Razred
{
Razred();
// prvi konstruktor
Razred(int i); // drugi
konstruktor
};
...
Razred R1;
// poziva prvi konstruktor
Razred R2(7);
// poziva drugi konstruktor
Nadgradnja operatora
Na objekte nekog razreda može se primjeniti većina standardnih operatora
ukoliko ih nadgradimo - definiramo za određeni razred. Pri tome ostaju
pravila udruživosti i prednosti operatora. Nadgrađeni operatori mogu se
nasljeđivati.
Koristimo li aritmetiku operatora u rukovanju s objektima, vrlo često
je potrebno nadgraditi operator pridruživanja ( =
) za definirani razred. Prevodilac
generira pretpostavljeni operator pridruživanja ako ga mi samo ne definiramo, ali ako
razred sadrži pokazivače tada pretpostavljeni operator pridruživanja nije
adekvatan (jer će npr. pokazivači novog objekta pokazivati na istu memorijsku
lokaciju kao i pokazivači izvornog, dok je u većini primjena potrebno zauzeti
novu memoriju) - vidi pod kopirajući konstruktor.
Također je korisno da operator pridruživanja vraća referencu na objekt
unutar koga se nalazi (referenca na this), čime se omogućava ulančano pridruživanje.
Primjer nadgradnje operatora:
class Double
{ private:
double
data;
public:
void
Set(double _data)
{
data=_data; }
Double&
operator= (const Double &izvor)
{
data=izvor.data;
return *this;
}
Double
operator+ (const Double &pribrojnik)
{
Double rezultat;
rezultat.data=(*this).data+pribrojnik.data;
return rezultat;
}
};
int main(void)
{ Double a,b,c;
a.Set(1);
c=b=a;
a=b+c;
}