 |
| |
Davor Đošan
C++ - predstavljanje |
|
C++ je programski jezik sa mnoštvom
odličnih osobina. Cilj ovog članka je da upoznamo programere početnike
sa nekima od njih, kao i da ljudima koji se već bave programiranjem
skrenemo pažnju na neke aspekte o kojima možda nisu razmišljali.
Malo istorije
| C++ je razvio Bjarne Stroustrop i možemo da
ga posmatramo kao bolji C |
Programski jezik C, prethodnik jezika C++, je postao jedan od najpopularnijih
programskih jezika. Njegova prvenstvena namena je bila za sistemsko
programiranje. C kao jezik omogućava programerima pisanje veoma
efikasnog koda.
U toku 80-tih i 90-tih dolazi do velike ekspanzije u razvoju objektno
orijentisanih tehnologija i u to vreme se pojavljuje programski
jezik SmallTalk. Objektno orijentisano programiranje (OOP) je počelo
da zamenjuje strukturirano. Sve ovo je dovelo do razvoja mnogobrojnih
objektno orijentisanih programskih jezika (Object-Pascal, Modula-2,
Mesa, Cedar, Neon, Objective-C) od kojih je većina nestala u toku
80-tih.
C++ je razvio Bjarne Stroustrop i možemo da ga posmatramo kao bolji
C. To je možda i najtačnija definicija ovog programskog jezika.
Iako C++ nije sasvim novi programski jezik, on ipak predstavlja
značajno proširenje mogućnosti C-a. Možemo smatrati da je C podskup
C++-a. Od svog prethodnika je nasledio sve njegove dobre osobine
kao i većinu loših, ali obezbeđuje i jezička unapređenja, a dodate
su mu i objektne mogućnosti. Treba napomenuti da korištenje C++
ne znači automatski i OOP. Mi možemo pisati u C++-u regularne strukturirane
programe.
Bolji C
| Cilj je da predstavimo te nove osobine da bi
se uvidele njihove prednosti. |
Dizajneri C++-a su hteli da dodaju objektne mehanizme a da se pri
tome ništa ne izgubi od jednostavnosti i efikasnosti koje su učinile
C tako popularnim.
C++ sadrži C kao podskup što praktično znači da on nasleđuje sve
dobre osobine koje ima C kao sto su na primer efikasnost i veliki
broj ugrađenih tipova. Dodate su takođe i nove osobine koje su učinile
jezik još robustnijim, ali se mnoge od njih ne koriste od strane
programera početnika. Cilj je da predstavimo te nove osobine da
bi se uvidele njihove prednosti. Neke od osobina koje ćemo predstaviti
su: konstante, inline funkcije, reference, deklaracije, klase, preklapanje
funkcija i operatora i dinamičko alociranje memorije.
C++ omogućava programerima ugrađivanje novih tipova podataka korišćenjem
klasa. Klasa je tip podataka definisan od strane korisnika. Kompajler
tretira nove tipove na isti način kao i ugrađene. Ovo je veoma moćna
osobina. Klase omogućavaju apstrakciju podataka a to je ključna
osobina OOP.
Nova forma komentara
Komentari su veoma poželjni unutar programa da bi program bio čitljiviji
i razumljiviji drugima. U C-u komentari su sledećeg oblika:
/* Ovo je tradicionalni C komentar */
C++ takođe podržava tradicionalne C komentare, ali takođe i omogućava
lakši mehanizam za komentare. Taj mehanizam ima sledeći izgled:
// Ovo je C++ komentar
Sve iza // i kraja linije je komentar.
Ono što je preporučljivo je da, iako C++ podržava komentare iz C-a,
ipak koristimo njegov mehanizam komentara. Time bismo mogli izbeći
greške tipa:
/* int i = 10 /* i inicijalizujemo sa 10 */
………….
*/
Vidimo da će u ovom slučaju biti prijavljena greška jer ćemo naići
na */ unutar linije i to će se smatrati za kraj komentara.
Konstante
U C-u, konstante se često definišu korišćenjem #define. #define
je u stvari makro. To znači sledeće. Ukoliko imamo definiciju:
#define PI 3.14159265358979323846
Prilikom prevođenja programa svako pojavljivanje PI u programu biće
zamenjeno sa 3.14159265358979323846. C++ nam dozvoljava da svaku
promenljivu deklarišemo kao konstantu dodavanjem const ključne reči
prilikom njene deklaracije. Definicija konstante za ovaj prethodni
slučaj bi izgledala:
const double PI = 3.14159265358979323846;
Konstantan objekat se moze biti inicijalizovan, ali se njegova vrednost
nikada ne može promeniti. Činjenica da konstantan objekat ne može
biti promenjen omogućava kompajleru da generiše efikasniji kod.
Veoma moćan način korišćenja konstanti je u kombinaciji sa pokazivačima.
Ako dafinišemo pokazivač na konstantu on se neće moći koristiti
za promenu objekta na koji pokazuje. Na primer:
int i = 10;
const int *pi = &i;
*pi = 15; // GRESKA! pi je konsantan pokazivac!
Nije moguće promeniti vrednost i preko pokazivača *pi. Ovakav tip
pokazivača se koristi u funkcijama koje vraćaju pokazivač na privatni
član klase. On bi korisniku omogućio da čita privatni član ali ne
i da ga menja.
Na žalost, korisnik ipak moze da menja ove podatke korišćenjem eksplicitne
konverzije tipa podataka. Mogli bismo da uradimo menjanje i iz prethodnog
primera kada bismo uradili sledeće:
*((int*) pi) = 15;
Međutim, mi vraćanjem konstantnih pokazivača iz funkcija upozoravamo
korisnika da postoji opasnost ukoliko menja te podatke, a on može
da ih menja ali na spostveni rizik.
Postoje dva načina da stavimo const pri deklaraciji pokazivača.
Jedan način smo već videli a drugi bi bio:
int i = 10;
int j = 11;
int* const ptr = &i;
Na ovaj način, sam pokazivač postaje konstantan. To znači da pokazivač
ne može biti promenjen, tj. ne može da pokazuje na neki drugi objekat
osim onog koji mu je inicijalno naveden te je nemoguće napisati
nešto tipa:
ptr = &j;
Treba zapamtiti da će dodavanjem const ključne reči izazivati duze
vreme kompajliranja ali time neće doći do generisanja dodatnog koda
od strane kompajlera.
Inline funkcije
Drugi
uobičajen način korišćenja #define makroa je u slučajevima kada
hoćemo da izbegnemo pozivanje funkcija. To su slučajevi kada je
funkcija toliko mala da vreme izvršavanja tela funkcije traje kraće
nego poziv funkcije. C++ nam obezbeđuje inline ključnu reč koja
informiše kompajler da tu funkciju treba da ugradi u kod, a ne da
generiše kod za pozivanje rutine. Na primer makro:
#define max (x, y) ((x)>(y)?(x):(y))
može u C++-u da bude zamenjen za celobrojne vrednosti inline funkcijom:
inline int max (int x, int y)
{
return (x > y ? x : y);
}
Upotreba makroa može da bude problematična u nekim slučajevima.
Zamislimo da smo imali sledeći izraz:
max (f(x), z++);
To bi nam u slučaju upotrebe makroa postalo:
((f(x)) > (z++) ? (f(x) : (z++));
Upotrebom inline funkcija bismo ove probleme izbegli.
Prilikom definisanja klasa moguće je unutar njene definicije navesti
telo funkcije. Ovakve funkcije će takođe biti tretirane kao inline
funkcije. Na primer:
class A {
int a;
public:
A() { }
// inline
int Value()
{
return a;
}
// inline
}
Reference
Za razliku od C-a, C++ nam omogućava korišćenje referenci. Referenca
je u stvari ime već postojećeg objekta. Reference su slične pokazivačima
i moraju biti deklarisane pre nego što ih koristimo. Definišimo
jedan podatak tipa int i jednu referencu na njega:
int n = 10;
int& r= n;
U ovom slučaju r nam je alias za n. I jedno i drugo se odnose na
isti objekat. Tako izraz:
r = -10;
menja i r i n u -10.
Veoma je važno primetiti da su inicijalizacija i dodela vrednosti
dve različite stvari za reference.
Više o referencama se može pročitati u članku “Prenošenje podataka
po referenci”.
Deklaracije
U C++-u deklaracije za promenljive se mogu staviti bilo gde u kodu,
tj. bilo gde u programskom bloku. Ako pretpostavimo da pretražujemo
neku linkovanu listu da bismo pronašli neki ključ funkcija bi mogla
izgledati kao:
int IsMember (const int key)
{
int found = 0;
if (NotEmpty())
{
List* ptr = head;
// Declaration
while (ptr && !found)
{
int item = ptr->data;
// Declaration
ptr = ptr->next;
if (item == key)
found = 1;
}
}
return found;
}
Stavljanjem deklaracija promenljivih bliže mestima gde se one koriste
kod postaje mnogo čitljiviji.
Preklapanje funkcija i operatora
Jedna od mnogih odličnih osobina jezika C++ je svakako i mogućnost
preklapanja imena funkcija i operatora. Pod tim se podrazumeva da
za jedno ime funkcije ili simbol operatora možemo da navedemo više
različitih definicija. Broj argumenata i njihov tip pri pozivu funkcije
govori kompajleru koju definiciju treba da uzme na tom mestu.
Uzmimo na primer da hoćemo da napravimo funkciju koja pretražuje
po nekom ključu niz koji može da sadrži int, float ili double vrednosti.
Definicija te funkcije bi bila:
int Search ( const int* data,
const int key);
int Search ( const float* data,
const float key);
int Search ( const double* data,
const double key);
Kompajler će osigurati da se prava funkcija pozove na osnovu argumenata
koje prosledimo funkciji. Ukoliko neki od argumenata ne odgovara
kompajler će pokušati implicitnu konverziju da bi dobio odgovarajuci
tip.
Preklapanje se najčešće koristi za funkcije članice klasa i operatore
koje definišemo u klasama. Većina klasa ima preklopljene konstruktore,
jer obično postoji više načina da se kreira objekat nekog tipa.
Takođe se vrlo često preklapaju i operatori definisani unutar funkcija.
Alociranje memorije
C++ nam obezbeđuje operatore new i delete koji mogu da alociraju
ne samo ugrađene tipove, nego i sve ostale tipove podataka koje
smo definisali. Ovo nam omogućava jedinstven mehanizam za alociranje
i oslobađanje memorije.
Na primer za alociranje jednog integer-a i niza integer-a bismo
koristili sledeće:
int *pi;
pi = new int;
*pi = 1;
int *array = new int [10];
for (int i=0;i < 10; i++)
array[i] = i;
Treba napomenuti da memorija koju alociramo sa new nije inicijalizovana.
Sva memorija koju alociramo sa new treba da bude oslobođena sa delete.
Delete poziva destruktor za objekat koji se uništava. Takođe, potrebno
je eksplicitno naglasiti brisanje niza podataka i to na sledeći
način:
delete [] array;
Kompajler čuva informacije o tome koliko članova ima neki niz, a
sa parom uglastih zagrada mi ga samo informišemo da je u pitanju
brisanje niza. Inače bi bio pobrisan samo prvi član niza. Takođe,
treba naglasiti da ukoliko pokusamo delete na pokazivač koji nije
alociran sa new ponašanje programa biće nedefinisano.
new i delete su globalni C++ operatori i oni mogu biti redefinisani
ukoliko za to postoji potreba.
Klase
Mehanizam klasa omogućava korisnicima da definišu sopstvene tipove
podataka. Klase se obično koriste za definisanje apstrakcija koje
se ne preslikavaju prirodno na ugrađene tipove podataka. Objekat
nije ništa drugo do intanca neke klase. Uprošćena definicija za
klasu Vector bi bila:
class Vector
{
public:
Vector(double new_x=0.0,double new_y=0.0);
Vector operator + ( const Vector & v);
void PrintOn (ostream& os);
…………….
private:
double x, y;
};
Kada se u programu definiše objekat tipa vektor imamo sledeće:
Vector v1, v2, v3(0.0,0.0);
Vektori v1 i v2 u ovom slučaju imaju neinicijalizovane vrednosti
i potrebno ih je naknadano inicijalizovati, dok je v3 inicijalizovan
na pravi način.
|