Sintaksa e pointerëve në C++
Programi 1.7 Call-by-reference kundrejt call-by-pointer.
Referencat janë sikur konstantet pointer në atë se vlera të cilën e ruajnë është adresa e objektit të cilit i referohen. Ato janë ndryshe në atë se aplikohet një operator automatik i padukshëm i dereferencimit në referencë. Ky dallim përkthehet në lehtësi të notacionit, posaqërisht pasi që mundëson që parametrat të përcillen sipas referencës pa “bagazhin shtesë” të operatorit & në argumentet aktuale dhe operatorin * i cili shkakton “rrëmujë” në programet dhe funksionet në stilin e C-së.
69 Përndryshe, edhe pointerët mund të përcillen sipas referencës. Kjo metodë përdoret për t’i mundësuar funksionit që të ndryshojë vendin ku pointon pointeri i përcjellur si parametër. Pointeri që përcillet me thirje sipas vlerës nuk mund të ndryshohet që të pointojë në lokacion të ri (sepse parametri formal ruan vetëm një kopje të vlerës së atij vendi).
Një çështje tjetër me rëndësi është zgjedhja ndërmjet përcjelljes së parametrave sipas vlerës ose sipas referencës. Edhe pse kjo më herët u diskutua në kontekst të vektorëve, kjo vlenë për të gjitha tipet e parametrave.
1.6 Strukturat dhe pointerët
Rikujtojmë se vargu është koleksion i objekteve (vlerave, variablave) të tipit të njëjtë. Vargu ka dy përparësi kryesore: së pari vargu indeksohet dhe mund të lëvizim nëpër secilin element të vargut me anë të unazave dhe së dyti, kur përdoren funksionet, mund të përcillet emri i vargut dhe kështu të përdoret vetëm një parametërt për të dërguar tërë përmbledhjen.
Një tip tjetër i tipit përmbledhës në C++ është struktura. Struktura ruan një koleksion të objekteve të cilat nuk është e domosdoshme të jenë të tipit të njëjtë. Pasi që objektet në koleksion nuk janë të tipit të njëjtë, nuk mundemi që thjeshtë të kalojmë me unaza nëpër to, sikur në rastin e vargjeve.
Secili objekt në strukturë është anëtarë (angl. member) dhe qaset përmes operatorit pikë të anëtarit (angl. dot member operator). Deklarimi i strukturës bazë bëhet përmes përdorimit të fjalës së rezervuar “struct”, emrit të strukturës dhe listës së anëtarëve të përmbyllur në kllapa të mëdha. Për shembull:
struct Student { string emri; string mbiemri; int numriStudentit; double notaMesatare; } emri mbiemri numriStudentit notaMesatare
70
Figura 1.18. ilustron se struktura Student përbëhet prej katër objekteve të ndryshme. Nëse kemi deklarimin,
Student s;
atëherë, për notën mesatare do të kemi s.notaMesatare. Programi 1.8 ilustron deklarimin e strukturës, qasjen e anëtarëve të të dhënae dhe përcjelljen e tyrë si parametra të funksionit. Vëreni se strukturat zakonisht nuk përcillen sipas vlerës për shkak se mbingarkimi i përcjelljes sipas vlerës mund të jetë shumë i “shtrenjtë” (për shkak të krijimit të kopjeve të parametrave, në këtë rast). Mekanizmi i përcjelljes së parametrave përcaktohet sipas asaj që u theksua tek diskutimi për këtë çështje (1.2.5).
1 // Shtypi informacionet per studentin 2 void printInfo( const Student &s ) 3 {
4 cout << "ID : " << s.numriStudentit << endl;
5 cout << "Emri : " << s.emri << " "
6 << s.mbiemri << endl;
7 cout << "Mesatarja: " << s.notaMesatare << endl;
8 } 9 10 // main i thjeshte. 11 int main( ) 12 { 13 Student meri; 14 15 meri.emri = "Meri" ; 16 meri.mbiemri = "Shala" ; 17 meri.notaMesatare = 4.0; 18 meri.numriStudentit = 123456789 ; 19 20 printInfo(meri); 2 22 return 0; 23 }
Programi 1.8 - Programi që ilustron deklarimin e strukturës, qasjen e anëtarëve të saj dhe përcjelljen e parametrave
Struktura e C++ është zgjeruar dukshëm nga homologia e saj në C, për të mundësuar funksionet si anëtarë dhe për qasjen në anëtarë.
71 Në diskutimin për teknikat e avansuara të programimit, hyn edhe deklarimi i pointerit në strukturë, për të ju qasur anëtarëve të strukturës së pointuar. Supozojmë se kemi:
Student *ptr = &s; //ptr pointon në sturkuturën s Atëherë ne mund t’i qasemi notës mesatare përmes (*ptr).notaMesatare. Kllapat janë të domosdoshme në këtë rast, për të rregulluara prioriteitn, pasi qe operatori i anëtarit, duke qenë operator postfix, ka prioritet më të lartë sesa operatori prefix i dereferencimit. Meqenëse përdorimi i kllapave bëhet i mërzitshëm, C++ ofron një postfix operatorë tjetër plotësues, operatorin ->
(member selection operator – operatori për selektim të anëtarit), i cili i qaset anëtarëve të strukturës në të cilën pointohet.
Pra, operatori -> përdoret për të ju qasur anëtarëve të strukturës së pointuar, prandaj, edhe forma ptr->notaMesatare jep qasjen e njëjtë si urdhëri më parë.
1.6.1 Të dhënat ekzogjene kundrejt atyre indigjene dhe kopjimi i
cekët kundrejt atij të thellë
C++-i i lejon shfrytëzuesit që të definojë operatorët në struktura. Për shembull, shfrytëzuesi mund të shkruaj funksionin me deklarimin:
bool operator<(const Student &lhs, const Student &rhs);
i cili kthen “true” nëse Studenti i parë (lhs: left-hand side – i anës së majtë) është më i vogël se studenti i dytë (rhs: right-hand side – i anës së djathtë), bazuar në ndonjë kriter të definuar përbrenda funksionit, nga ana e shfrytëzuesit. (Shkurtesat lhs dhe rhs, për left-hand side dhe right-hand side do të përdroren nëpër shembuj, respektivisht). Përdorimi i mekanizmit të klasave, mundëson që ky funksion të përfshihet si anëtarë i strukturës, ngjashëm me anëtarët e të dhënave.
Operatori i ndarjes së vlerës me kopjimi = dhe operatori i barazisë == gjithashtu mund të definohen, por nëse nuk bëjmë asgjë, definicioni standard përdoret për kopjim dhe krahasimi i barazisë bëhet ilegal. Në mënyrë specifike, në mënyrë standarde, kopjimi i strukturës implementohet si kopje anëtarë për anëtarë. Me fjalë tjera, secili anëtarë kopjohet nga njëra strukuturë në tjetrën, e jo në nivel të strukturës, si tërësi.
Problemi me këtë mekanizëm është ilustruar në deklarimin vijues:
struct Profesor (
72 string *emri; string "mbiemri; int IDPunetori; } ; Supozojmë se kemi: Profesor s, t;
Nëse supozojmë se t është inicializuar, atëherë s=t është kopjim anëtarë për anëtarë. Mirëpo, dy anëtarët e parë të strukturës janë thjeshtë pointerë, kështu që vetëm adresat kopjohen. Prandaj rezultati është që s.emri tani është duke e bashkëndarë memorien (angl. sharing memory) me t.emri, dhe këto nuk janë kopje të pavarura të stringut. Nëse më vonë jepet urdhëri:
delete t.emri;
për të recikluar memorien e alokuar dinamikisht, s ndodhet në telashe serioze. Ky problem është i ilustruar në Figurën 1.19, e cila thekson dallimin ndërmjet të dhënave ekzogjene dhe atyre indigjene. (indigjene-vendëse, të brendshme; ekzogjene-jo vendëse, të jashtme).
s “Meri” 12345 t 12345 “Shala” emri mbiemri IDPunetori
Fig. 1.19 – Kopjimi i cekët, ku kopjohen vetëm pointerët
Të dhënat indigjene (të vendit) janë tërësisht të përmbajtura nga ana e
strukturës. Për shembull, në strukturën Student, anëtarët emri dhe mbiemri janë stringje dhe janë tërësishtë të vetë-përmbajtur. Disavantazh i reprezentimit të objektit në mënyrë indigjene është se madhësia e objektit është fikse, zakonisht është e madhe dhe prandaj e kushtueshme për t’u kopjuar (kur përcillet në funksionet, etj).
Të dhënat ekzogjene (të jashtme), përkundrazi, qëndrojnë jashtë strukturës dhe
janë të qasura përmes pointerit. Avantazh i të dhënave ekzogjene është se të dhënat e zakonshme (përbashkëta) mund të bashkëndahen (angl. shared) mes
73 disa instancave; kur përdoret operatori standard i ndarjes së vlerës, kopja është vetëm kopje e pointerëve, jo edhe e vlerave të pointuara. Zakonisht kjo sjellje është e dëshirueshme. Për shembull, në Java, kjo është standarde.