Hyrje C++pc-sys-albania.weebly.com/uploads/8/1/0/3/8103491/hyrje... · 2018. 10. 4. · Title:...

72
Dr. Artan Bori¸ ci Lenda: Programim Hyrje ne C++ 1 Prej C tek C++ Nuk eshte qellimi te behet histori ne kete paragraf, por per te vendosur urat ndermjet ketyre dy gjuheve. Ne fakt, emri C++ ka kuptimin zhvillimi i gjuhes C. Ne se do te krahasonim keto emertime me ate perkatese te Fortranit nuk do te kuptonim se cfare ka mbetur prej Fortran77 ne Fortran2003. Ndryshe qendron puna me C++: gjuha C eshte nje nenbashkesi e saj; pra me pak ndryshime te vogla nje kod C mund te kompilohet prej C++. Atehere cila eshte nevoja te kemi nje gjuhe te re, ose me sakte cfare ka te re ne C++ qe nuk gjendet ne C? Nese me ane te C mund te programohet me modelin procedurial C++ ka ndertime qe lejojne programimin me objekte. Pra, C++ lejon te dy tipet e programimit, por duhet thene qe programimi procedurial ne C++ do te ishte keqperdorim i kesaj gjuhe, nderkohe qe kete pune mund ta kryeje vete C. Nga ana tjeter kombinimi i dy tipeve te programimit e ben C++ nje gjuhe teper te zhdervjellet ne krahasim me gjuhe si Java qe ka hapsire shume te ngushte (ne fakt nuk i hyn ne pune) per programim procedurial. 1.1 Programi i pare ne C++ // my first program in C++ #include <iostream> using namespace std; int main () { 1

Transcript of Hyrje C++pc-sys-albania.weebly.com/uploads/8/1/0/3/8103491/hyrje... · 2018. 10. 4. · Title:...

  • Dr. Artan Boriçi

    Lenda: Programim

    Hyrje ne C++

    1 Prej C tek C++

    Nuk eshte qellimi te behet histori ne kete paragraf, por per te vendosur urat ndermjet

    ketyre dy gjuheve. Ne fakt, emri C++ ka kuptimin zhvillimi i gjuhes C. Ne se do te

    krahasonim keto emertime me ate perkatese te Fortranit nuk do te kuptonim se cfare ka

    mbetur prej Fortran77 ne Fortran2003. Ndryshe qendron puna me C++: gjuha C eshte

    nje nenbashkesi e saj; pra me pak ndryshime te vogla nje kod C mund te kompilohet prej

    C++. Atehere cila eshte nevoja te kemi nje gjuhe te re, ose me sakte cfare ka te re ne

    C++ qe nuk gjendet ne C?

    Nese me ane te C mund te programohet me modelin procedurial C++ ka ndertime qe

    lejojne programimin me objekte. Pra, C++ lejon te dy tipet e programimit, por duhet

    thene qe programimi procedurial ne C++ do te ishte keqperdorim i kesaj gjuhe, nderkohe

    qe kete pune mund ta kryeje vete C. Nga ana tjeter kombinimi i dy tipeve te programimit

    e ben C++ nje gjuhe teper te zhdervjellet ne krahasim me gjuhe si Java qe ka hapsire

    shume te ngushte (ne fakt nuk i hyn ne pune) per programim procedurial.

    1.1 Programi i pare ne C++

    // my first program in C++

    #include

    using namespace std;

    int main ()

    {

    1

  • cout i;

    cout

  • 2 Ndryshoret dhe tipet e te dhenave

    Edhe ne C++ identifikuesit nuk duhet te jene fjale kyce te gjuhes ose te percaktuara si

    te tilla prej kompilatorit. Fjalet kyce te rezervuara jane:

    asm, auto, bool, break, case, catch, char, class, const, const_cast,

    continue, default, delete, do, double, dynamic_cast, else, enum,

    explicit, export, extern, false, float, for, friend, goto, if, inline,

    int, long, mutable, namespace, new, operator, private, protected,

    public, register, reinterpret_cast, return, short, signed, sizeof,

    static, static_cast, struct, switch, template, this, throw, true, try,

    typedef, typeid, typename, union, unsigned, using, virtual, void,

    volatile, wchar_t, while

    Nuk lejohen qe operatoret te parqiten si identifikues meqe ato jane fjale te rezervuara:

    and, and_eq, bitand, bitor, compl, not, not_eq, or, or_eq, xor, xor_eq

    3 Tipet themelore te te dhenave

    Nje byte eshte sasia minimale e kujteses ne C++. Ne nje byte mund te vendoset nje

    sasi relativisht e vogel te dhenash: nje shenje ose nje numer te plote te vogel (zkonisht

    prej 0 deri ne 255). Nga ana tjeter, kompjuteri mund te manipuloje tipe te dhenash me

    komplekse qe formohen duke u grupuar ne disa byte, sic jane numrat e gjate ose numrat

    jo te plote. Me poshte jepet nje liste me tipet ekzisuese themelore te te dhenave ne C++

    se bashku me kufinjte e vlerave qe ato mund te marrin:

    Tipet themelore te te dhenave

    Emri Pershkrimi Madhesia Kufinjte

    char Karakter ose i plote i vogel 1byte me shenje: -128 deri 127

    pa shenje: 0 deri 255

    3

  • int I plote 1fjale me shenje:

    -2147483648 deri 2147483647

    pa shenje: 0 deri 4294967295

    short int

    dhe short I plote i shkurter 2bytes me shenje: -32768 deri 32767

    pa shenje: 0 deri 65535

    long int

    dhe long I plote i gjate 4bytes me shenje:

    -2147483648 deri 2147483647

    pa shenje: 0 deri 4294967295

    bool Vlere Booleane 1byte true ose false

    float Numer me pike notuese 4bytes 3.4e +/- 38 (7 shifra)

    double Numer me pike notuese

    me precizion te dyfishte 8bytes 1.7e +/- 308 (15 shifra)

    long double Numer i gjate me pike rrjedhese

    me precizion te dyfishte 8bytes 1.7e +/- 308 (15 shifra)

    wchar\_t Karakter i gjere 2bytes 1 karakter i gjere

    Vlerat ne shtyllat Madhesia dhe Kufinjte varen prej arkitektures se sistemit ne te cilin

    kompilohet dhe ekzekutohet programi. Vlerat e dhena ketu jane ato qe ndeshen me shpesh

    ne sistemet me 32 bit. Po keshtu short dhe long jane praktikisht te njevlereshme me

    short int dhe long int.

    3.1 Deklarimi i ndryshoreve

    Deklarim i eshte i ngjashem si ne C: qe te perdorim nje ndryshore ne C++, do te duhet

    qe ta deklarojme me pare ate duke treguar se cfare tipi te dhenash duam qe ajo te jete.

    Tipet ‘char, short, long, int’ mund te jene me shenje ose pa shenje. Default per

    shumicen e kompilatoreve te C++ eshte deklarimi me shenje. Perjashtim ben ‘char’ qe

    konsiderohet i ndryshem prej ‘signed char’ dhe ‘unsigned char’, te cilave u caktohen vlera

    4

  • numerike.

    Po keshtu ‘signed’ dhe ‘unsigned’ mund te perdoren te vetem me nenkuptimin ‘int

    signed’ dhe ‘int unsigned’. Keshtu, deklarimet

    unsigned NextYear;

    unsigned int NextYear;

    jane te njevlefshme.

    3.2 Fusha e veprimit te ndryshoreve

    Ndryshret mund te jene globale nese ato deklarohen ne fillim te programit perpara cdo

    funksioni dhe lokale nese ato deklarohen brenda trupit te nje funksioni:

    #include

    // Ndryshore Globale

    int Integer;

    char aCharacter;

    char string [20];

    unsigned int NumberOfSons;

    int main ()

    {

    // Ndryshore Lokale

    unsigned short Age;

    float ANumber, AnotherOne;

    // Urdhera

    cout > Age;

    5

  • ...

    }

    Ndryshoreve globale mund tu referohet kudo ne kod madje edhe brenda funksioneve, gjith-

    nje pas deklarimit te tyre. Ndresa fusha e ndryshoreve lokale kufizohet brenda blloqeve

    te perfshira ne kllapa gjarperueshe.

    3.3 Inicializimi i ndryshoreve

    Nese duam te inicializojme nje ndryhsore te plote ne castin e deklarimit, atehere do te

    shkruajme:

    int a = 0;

    ose

    int a (0);

    3.4 Kordat

    Kordat ne C++ nuk jane tip themelor por nje klase. Per deklaruar dhe perdorur kordat

    duhet te perfshijme nje skedar koke te quajtur ”string” i cili i referohet namespace std,

    p.sh.

    // shebull kordash

    #include

    #include

    using namespace std;

    int main ()

    {

    string korde = "Kjo eshte nje korde";

    cout

  • return 0;

    }

    3.5 Konstantet

    Konstantet ne C++ u nenshtrohen te njejta rregullave si ne. Ne vecanti mund te percak-

    tojme konstante me ane te direktives se preprocesorit #define. Po keshtu ato mund te

    deklarohen me ane te prefiksit const perpara tipit, si p.sh.

    const float pi=3.1415926;

    Ne rast se tipi nuk jepet, atehere ai nenkuptohet int.

    4 Operatoret

    Operatoret ne C++ jane thuajse ato te gjuhes C. Me poshte po japim tabelen permbled-

    hese te tyre:

    Perparesia Operatori Pershkrimi Grupimi

    1 :: fusha Djathtas

    2 () [] . -> ++ --

    dynamic_cast

    static_cast

    reinterpret_cast

    const_cast typeid prapashtesa Djathtas

    3 ++ -- ~ ! sizeof

    new delete unar (parashtese)

    * & ridrejtimi dhe referneca (treguesit)

    + - operator unar te shenjes Majtas

    4 (tipi) ndryshim tipi Majtas

    5 .* ->* tregues per tek anetar Djathtas

    7

  • 6 * / % shumezues Djathtas

    7 + - mbledhes Djathtas

    8 > zhvendoses Djathtas

    9 < > = krahasues Djathtas

    10 == != barazisese Djathtas

    11 & bit AND Djathtas

    12 ^ bit XOR Djathtas

    13 | bit OR Djathtas

    14 && logjik AND Djathtas

    15 || logjik OR Djathtas

    16 ?: kushtezues Majtas

    17 = *= /= %= += -=

    >>=

  • e njejte per çdo shkronje.

    6 Tabelat

    Ne C, si ne Fortran mund te ndertohen tabela me elemente homogjene te tipave baze.

    Keshtu, per te deklaruar nje tabele me 10 elemente ”int” mund te shkruajme:

    int x[10];

    Te vihet re se ketu perdoren kllapat katrore. Indekset e elementeve do te fillojne nga 0,

    ne 9 dhe elementet e ”x”-it do te shkruhen:

    x[0], x[1], x[2], ..., x[9]

    Ne pergjithesi, nese nje tabele ka ”n” elemente, indekset do te shkojne nga 0, ne ”n-1”.

    Mund te ndertohen edhe tabela shumepermasore. P.sh.:

    int x[10] [15] [20];

    Indekset mund te jene shprehje me vlera te plota. Keshtu mund te kete kuptim pohimi:

    n = name[i+j] [1] + name[k] [2];

    7 Strukturat e Kontrollit

    C++ ka te njejtat struktura kontrolli si ato te gjuhes C:

    - kushtezuese: if (condition) statement1 else statement2

    - iterative: while (expression) statement

    do statement while (condition);

    for (initialization; condition; increase) statement;

    - perzgjedhese:

    switch (expression)

    9

  • {

    case constant1:

    group of statements 1;

    break;

    case constant2:

    group of statements 2;

    break;

    .

    .

    default:

    default group of statements

    }

    - pohimet break, continue dhe goto

    - funksionin exit te percaktuar ne librarine cstdlib

    7.1 Pohimi ”if”; operatoret e krahasimit; pohimet e perbera

    Pohimi baze kushtezues ne C/C++ eshte pohimi if:

    c = getchar ( );

    if ( c == ’?’ )

    cout

  • == e barabarte me;

    != e ndryshme nga;

    > me e madhe se;

    < me e vogel se ;

    >= me e madhe e barabarte me;

  • 7.2 Pohimi ”while”; Kalimi i vleres brenda nje shprehjeje; po-

    himi zero

    Mekanizmi baze i ciklimit ne C eshte pohimi ”while”. Ja nje shembull qe kopjon hyrjen e

    vet tek dalja e vet duke kojuar karakteret nje e nga nje. Mbani mend se ’\0’ eshte fundi

    i skedarit.

    main ( ) {

    char c;

    while ( (c=getchar( )) != ’\0’ )

    putchar(c);

    }

    Sintaksa e pergjithshme eshte:

    while ( shprehje ) pohim

    Kuptimi i saj eshte:

    (a) Vlereso shprehjen.

    (b) Nese vlera e saj eshte e vertete, pra jo zero, ekzekuto pohimin dhe kthehu tek (a).

    Meqenese shprehja kontrollohet perpara ekzekutimit te pohimit, pjesa e pohimit mud te

    ekzekutohet zero here, gje qe eshte shpesh e deshirueshme. Ashtu si tek ”if”, shprehja

    dhe pohimi mund te jene sa te duam te nderlikuara. Shembulli yne merr nje karakter, e

    kalon tek ”c” dhe pyet nese ai eshte ’\0’. Ne qofte se jo, ekzekutohet pjesa e pohimit, qe

    shtyp karkterin. Pastaj ”while” perseritet. Kur me ne fund, karakteri hyres eshte ’\0’,

    ”while” perfundon, keshtu ben edhe ”main”.

    Verejme se eshte perdorur pohimi i kalimt te vleres,

    c = getchar( )

    brenda nje shprehjeje. Kjo praktike shkurton kodin dhe e ben ate me te qarte (mundohuni

    p.sh. te rishkruani kopjimin e skedarit pa perdorur kalimin e vleres brenda nje shprehjeje).

    12

  • Kjo menyre funksionon meqe pohimi i kalimit te vleres merr vlere njelloj si çdo shprehje

    tjeter. Vlera e saj eshte ajo anes se djathte. Kjo nenkupton se mund te bejme kalime te

    shumefishta vlere si me poshte:

    x = y = z = 0;

    Vleresimi behet nga e djathta ne te majte.

    Verejme qe kllapat ne pohimin e kalimit te vleres brenda kushtezimit jane te nevojshme:

    ne qofte se do te shkruanim

    c = getchar( ) != ’\0’

    ”c” do te jete 0 ose 1 ne varesi te kapjes ose jo te karakterit tr̈ fundit te skedarit. Kjo, pe

    arsye se, ne mungese te kllapave, operatori i kalimit te vleres, ‘=’ vleresohet pas operatorit

    krahasues ‘!=’. Ne rast dyshimi, madje edhe nese jo, vendos klappat.

    Meqe putchar(c) kthen ”c” si vlere te vet te funksionit, ne edhe mund te kopjojme hyrjen

    tek dalja duke nderfutur thirjet per tek getchar dhe puchar:

    main( ) {

    while( putchar(getchar( )) != ’\0’ ) ;

    }

    Çfare pohimi eshte duke u perseritur? Asnje, ose me sakte, pohimi zero, meqe e gjithe

    puna eshte bere brenda pjeses testuese te ”while”. Ky version eshte pak i ndryshem prej

    te meparshmit meqe ‘0” perfundimtare kpjohet tek dalja perpara se ne te vendosim te

    ndalojme.

    7.3 Klauzola ”else”; Shprehjet kushtezuese

    Ne sapo perdorem nje ”else” pas nje ”if”. Forma e pergjithshme e ”if” eshte:

    if (shprehje) pohim1 else pohim2

    ku pjesa ”else” eshte fakultative por shpesh e dobishme. Shembulli kanonik i jep ”x”-it

    vleren e minimumit ndermjet ”a” dhe ”b”:

    13

  • if ( a < b )

    x = a;

    else

    x = b;

    C jep nje forme kushtezuese alternative qe eshte shpesh me e permbledhur. Quhet “sh-

    prehje e kushtezuar” sepse eshte nje kushtezues qe ne te vertete merr nje vlere dhe mund

    te perdoret kudo ku mund te perdoret nje shprehje. Vlera e

    a

  • Kushtet verifikohen me rradhe dhe vetem nje bllok pohimesh dhe vetem nje ekzekutohet:

    ose i pari per te te cilin kenaqet ”if”-i, ose ai i ”else”-it te fundit. Kur perfundon ky

    bllok, pohimi tjeter qe ekzekutohet eshte ai pas ”else”-it te fundit. Ne qofte se nuk duhet

    ndermarre asnje veprim per rastin default, atehere mos vendos ”else”-in e fundit.

    7.4 Pohimi ”for”

    Ne nje fare menyre pohimi ”for” eshte nje pohim i pergjithesuar ”while”, qe na lejon qe

    te vendosim pjesen inicializuese dhe inkrementuese te nje cikli ne nje pohim te vetem se

    bashku me testin. Forma e pergjithshme eshte:

    for( inicializim; shprehja; inkrementim )

    pohim

    Kuptimi eshte sikur te shkruanim:

    inicializim;

    while( shprehje ) {

    pohim

    inkrementim;

    }

    Keshtu, programi i meposhtem mbledh elementet e nje tabele:

    sum = 0;

    for( i=0; i

  • for( ; ; ) ...

    dhe

    while( 1 ) ...

    jane te dy cikle te pafundem.

    Cikli ”for” eshte me kompakt se ”while” sepse e mban kodin aty ku perdoret dhe ngan-

    jehere eleminon nevojen e perdorimit te pohimeve te perbera, si ne kete shembull ku

    elementet e nje tabele 2-permasore behen zero:

    for( i=0; i

  • case ’b’:

    bflag++;

    break;

    case ’c’:

    cflag++;

    break;

    default:

    cout

  • case ’a’:

    aflag++;

    break;

    case ’b’:

    bflag++;

    break;

    ...

    }

    // pohimet break na sjellin direkt ketu

    Pohimi ‘break’ funksionon edhe per pohimet ‘while’; ai shkakton daljen e menjehershme

    prej ciklit.

    Pohimi ‘continue’ ecen vetem brenda ‘for’ dhe ‘while’; ai ben qe te filloje iteracioni

    i radhes ne cikel. Ne mund te kishim perdorur nje ‘continue’ ne shmebullin tone per te

    vazhduar iteracionin tjeter te ‘for’, por perdorimi i ‘break’ ne vend te tij duket me i qarte.

    8 Operatoret e rritjes dhe zvogelimit

    Gjuhet C/C++ permbajne operatoret ”++” (rritja me 1) dhe ”- -” (zvogelimi me 1).

    Supozojme se duam te numerojme rreshtat ne nje skedar.

    main( ) {

    int c,n;

    n = 0;

    while( (c=getchar( )) != ’\0’ )

    if( c == ’\n’ )

    ++n;

    cout

  • ++n eshte ekuivalente me n=n+1 por eshte me e qarte, veçanerisht kur n eshte shprehje e

    nderlikuar. Duhet thene veç se keto operatore vlejne vetem per tipat ”int” dhe ”char”

    (dhe per treguesit, qe do te shqyrtohen me tej).

    Operatoret e ketij tipi mund te vihen si para, ashtu edhe prapa ndryshores qe do t’i

    ndryshohet vlera. Ndryshimi vihet re nese kalojme kete e vlere tek nje ndryshore tjeter.

    Keshtu,

    x = ++k;

    do te thote qe ne fillim ”k”-se do t’i rritet vlera me 1, dhe pastaj kjo vlere do t’i jepet

    ”x”-it. Nese shkruajme:

    x = k++;

    atehere, ne fillim ”x”-it i jepet vlera e ”k”-se dhe pastaj ”k”-se i rritet vlera me 1. Ne te

    dy rastet, per variablen ”k” nuk ka rendesi nese operatori i qendron para apo pas. Dallimi

    behet ne vleren qe merr ”x”-i.

    9 Funksionet

    Funksionet bejne te mundur strukturimin e nje programi ne forme modulare. Ashtu si ne

    C argumentat pasohen me vlere ose me reference kur perdorim treguesit. Nje funksion

    duhet te jete percaktuar perpara se te mund t’i referohemi perndryshe ata duhet qe

    patjeter te deklarohen me para dhe te percaktohen pas main. Argumentave mund t’i

    caktojme edhe vlera default si ne shembullin e meposhtem:

    // vlerat default ne funksione

    #include

    using namespace std;

    int divide (int a, int b=2)

    {

    19

  • int r;

    r=a/b;

    return (r);

    }

    int main ()

    {

    cout

  • {

    int x=5,y=2;

    float n=5.0,m=2.0;

    cout

  • return (a * factorial (a-1));

    else

    return (1);

    }

    int main ()

    {

    long number;

    cout > number;

    cout

  • b = &a;

    vendos adresen e ‘a’ tek ‘b’. Ne nuk mund te bejme shume me kaq pervec se ta printojme

    ate ose t’ia kalojme nje rutine tjeter. Kjo per aresye se ‘b’-se nuk i kemi dhene deklarimin

    e duhur. Por ne qofte se deklarojme ‘b’ si tregues tek nje numer i plote, atehere jemi ne

    gjendje te mire:

    int a, *b, c;

    b = &a;

    c = *b;

    Tani ‘b’ permban adresen e ‘a’ dhe ‘c = *b’ do te thote te perdoret vlera e ‘b’-se si nje

    adrese, d.th. si nje tregues. Efekti eshte qe ne te marrim perseri permbajtejen e ‘a’-se,

    ndonese ne menyre indirekte. (Eshte gjithnje e vertete se ‘*&x’ eshte e njejte me ‘x’ ne

    rast se ’x’ ka nje adrese.)

    Perdorimi me i shpeshte i treguesve ne C eshte shetitja e shpejte pergjate tabelave.

    Ne fakt, emri i nje tabele ne C perfqason adresen e elementit zero te saj, prandaj ajo

    nuk mund te vendoset ne anen e majte te nje shprehjeje. (Adresa e diçkaje nuk mund te

    ndryshohet duke kaluar vlera tek ajo.) Ne se shkruajme:

    char *y;

    char x[100];

    ‘y’ eshte i tipit tregues tek karakter (megjithese nuk tregon ende ne ndonje vend). Ne

    mund ta bejme ‘y’ te tregoje tek nje element i ‘x’-it me dy menyra:

    y = &x[0];

    y = x;

    Meqe ‘x’ eshte adresa e ‘x[0]’ menyra e dyte eshte legale dhe konsistene. Nga ana tjeter

    ‘*y’ jep ‘x[0]’. Per me teper,

    *(y+1) /* jep x[1] */

    *(y+i) /* jep x[i] */

    23

  • ndersa vargu i pohimeve,

    y = &x[0];

    y++;

    ben qe ‘y’ te tregoje tek ‘x[1]’.

    Le te perdorim treguesit ne nje funksion ‘length’ qe llogarit gjatesine e nje tabele

    karakteresh. Rikujtojme qe me mareveshje te gjithe tabelat e karaktereve mbarojne me

    ‘\0’. (Ne qofte se jo programi do te rrezohet patjeter). Menyra e vjeter eshte:

    length(s)

    char s[ ]; {

    int n;

    for( n=0; s[n] != ’\0’; )

    n++;

    return(n);

    }

    Duke e rishkruar me tregues:

    length(s)

    char *s; {

    int n;

    for( n=0; *s != ’\0’; s++ )

    n++;

    return(n);

    }

    Tani mund te kuptohet perse duhet te percaktojme ne cfare gjeje ‘s’ tregon: nese do ta

    shtonim ate me ane te ‘s++’ atehere do ta shtonim me sasine e duhur.

    Versioni me tregues eshte me eficient (kjo eshte thuajse gjithnje e vertete), ndersa edhe

    me kompakt eshte:

    24

  • for( n=0; *s++ != ’\0’; n++ );

    ‘*s’ kthen nje karakter; ‘++’ shton treguesin me nje, ne kete menyre marrim karakterin

    e radhes heren tjeter. Sic mund te shihet, duke i bere gjerat me eficiente i bejme ato

    njekohesiht me pak te qarta. Por ‘*s++’ eshte nje idom kaq e perdorur saqe duhet te

    mesohet patjeter.

    Duke shkruar nje hap perpara, ketu eshte funksioni ‘strcopy’ qe kopjon tabelen e

    karaktereve ‘s’ tek nje tjeter ’t’:

    strcopy(s,t)

    char *s, *t; {

    while(*t++ = *s++);

    }

    Testi perkundrejt ‘\0’ eshte lene menjeane, meqe ‘\0’ eshte identikisht zero; nje kodim te

    tille ndeshet shpesh. (Duhet vendosur hapsire prapa ‘=’: shih paragrafet qe vijojne).

    Per argumentet e nje funksioni dhe vetem per to deklarimet

    char s[ ];

    char *s;

    jane te njevlefshme: nje tregues tek nje tip ose nje tabele me madhesi te papercaktuar

    jane e njejta gje.

    Nese e gjitha kjo duket misterioze, eshte mire te mesohen keto forma permendesh deri

    sa te behen natyre e dyte. Shpesh nuk duhet dicka me e nderlikuar.

    10.1 Treguesit tek Funksionet

    Tregimi tek funksionet perdoret kryesisht per te pasuar nje funksion si argument te nje

    funksioni tjeter. Per te deklaruar nje tregues tek nje funksion duhet te deklaruar prototipi

    i funksionit me emrin e funksionit te futur brenda kllapave dhe te filluar me nje yll:

    // tregues tek funksionet

    25

  • #include

    using namespace std;

    int mbledhje (int a, int b)

    { return (a+b); }

    int zbritje (int a, int b)

    { return (a-b); }

    int veprimi(int x, int y, int (*funksioni_qe_thirret)(int,int))

    {

    int g;

    g = (*funksioni_qe_thirret)(x,y);

    return (g);

    }

    int main ()

    {

    int m,n;

    int (*minus)(int,int) = zbritje;

    m = veprimi(7, 5, mbledhje);

    n = veprimi(20, m, minus);

    cout

  • 11 Kujtesa dinamike

    Kujtesa dinamike sherben per te ndryshuar kerkesen per kujtese gjate ekzekutimit te

    programit. Per te bere te mundur kete perdoret operatori new pasuar prej tipit, p.sh.

    udhezimet:

    int * ndim;

    ndim = new int [5];

    bejne qe te caktohet kujtese ne menyre dinamike per pese elemente te plote e te kthehet

    nje tregues, ndim, qe tregon tek elementi i pare i plote ne kujtese. Ne qofte se sistemi nuk

    ka kujtese te lire atehere programi ndalon automaikisht. Ne rast se duam qe kjo te mos

    ndodhe shkruajme:

    int * ndim;

    ndim = new (nothrow) int [5];

    if (ndim == 0) {

    // tregues zero: programi nuk mund te caktoje kujtese; merr masa te tjera

    };

    Pra, vendosja e (nothrow) pas new ben qe ne rast se nuk ka kujtese te lire, ndim i caktohet

    treguesi zero, pra nje tregues qe nuk tregon ne asnje vend ne kujtese.

    Per te kthyer mbrapsht veprimin e krijimit te kujteses perdoret operatori delete,

    p.sh.:

    // kujtesa dinamike

    #include

    using namespace std;

    int main ()

    {

    int i,n;

    27

  • int * p;

    cout > i;

    p= new (nothrow) int[i];

    if (p == 0)

    cout

  • typedef char fushe [50];

    Ne kete rat kemi kater tipe te dhenash, C, FJALE, tregues_char, fushe perkatesisht

    si char, unsigned int, char*, char[50] te cilet mund te perdoren per deklarime.

    typedef perdoret per shkurtime te tipeve qe perdoren shpesh ne nje program, ose kur

    duam qe me vone te ndryshojme tipin dhe te evitojme redaktimin e tekstit.

    12.2 Strukturat

    Perdorimi kryesor i strukturave eshte per te grupuar nje sere ndryshoresh te tipeve te

    ndryshem qe te mund te trajtohen si nje njesi e vetme. P.sh. nese do te shkruanim nje

    kompilator ose asembler, do te mund te na duhet per cdo identifikues nje infomacion se

    emri (tabele karakteresh), numri i rreshtit burimor (i plote), nje lloj informacioni (ndoshta

    nje karakter) dhe me gjase nje numerator perdorimi (nje tjeter i plote):

    char id[10];

    int line;

    char type;

    int usage;

    Me to ne shume thjeshte mund te bejme nje strukture si me poshte. Fillimisht i tregojme

    C si do te dukej nje strukture, pra cfare lloj gjerash ajo permban; me pas ne mund

    te rezervojme kujtese per te ose ne te njejtin pohim ose veçmas. Me e thjeshta eshte

    percaktimi dhe rezerivimi i kujteses e gjitha njeheresh:

    struct {

    char id[10];

    int line;

    char type;

    int usage;

    } sym;

    29

  • Ketu perkufizohet struktura ‘sym’ me formen e dhene; id, line, type, usage jane

    pjestare te struktures. Ne mund t’i referohemi nje pjestari si me poshte:

    emer-strukture . emer-pjestari

    Keshtu p.sh. mund te shkruajme:

    sym.type = 077;

    if( sym.usage == 0 ) ...

    while( sym.id[j++] ) ...

    etj.

    Megjithese emrat e pjestareve pasojne emrin e struktures, ato duhet te jene te vetem dhe

    nuk mund te perdoren ne ndonje strukture tjeter.

    Deri tani nuk kemi ndonje avantazh te perdorimit te strukturave. Ai duket kur kemi

    tabela strukturash, apo kur deshirojme te kalojme te dhena te komplikuara ndermjet

    funksionesh. Supozojme se duam te ndertojme nje tabele 100 vendeshe ku elementet te

    jene bashkesi vlerash te ndryshoreve p.sh. char[10], line, type, usage. Pa perdorur

    struktura, do te ishim te detyruar t’i shtonim te gjitha ndryshoreve edhe nje permase me

    100 vende:

    char id[100][10];

    int line[100];

    char type[100];

    int usage[100];

    Nese ndryshoret fillestare vihen ne strukture, kjo na lejojn qe te riorganizojme informa-

    cionin ne menyre qe te gjitha te dhenat rreth nje emri te vetem (atij te struktures), te

    jene bashke:

    struct {

    char id[10];

    int line;

    30

  • char type;

    int usage;

    } sym[100];

    Ky deklarim ben qe ”sym” te jete nje tabele strukturash (identike ne forme) me 100

    elemente;çdo element i tabeles i ka te gjithe pjesetaret e struktures (por vlerat e tyre

    mund te jene te ndryshme). Ne mund t’i referohemi elementeve te veçante si me poshte:

    sym[i].usage++; /* inkremento ‘usage’ te elementit te i-te te ‘sym’ */

    for( j=0; sym[i].id[j++] != ’\0’; ) ...

    etj.

    Keeshtu per te printuar nje liste identifikuesish qe nuk jane perdorur se bashku me numrin

    e tyre te rreshtit,

    for( i=0; i

  • main( ) {

    ...

    if( (indeks = shiko(emer)) >= 0 )

    sym[indeks].usage++; /* rrit me 1 "usage" te "sym[indeks]" */

    else

    install(emer, newline, newtype);

    ...

    }

    shiko(s)

    char *s; {

    int i;

    extern struct {

    char id[10];

    int line;

    char type;

    int usage;

    } sym[ ];

    for( i=0; i 0 )

    return(i);

    return(-1);

    }

    krahaso(s1,s2) /* kthe 1 nese s1==s2, perndryshe kthe 0 */

    char *s1, *s2; {

    while( *s1++ == *s2 )

    if( *s2++ == ’\0’ )

    32

  • return(1);

    return(0);

    }

    Ne fakt deklarimi i struktures ne funksionin ”shiko” s’eshte i nevojshem, nese deklarimi i

    saj i jashtem gjendet para perdorimit te saj ne kodin burimor te skedarit, siç do te shohim

    me tej.

    Tani le te shohim versionin me tregues:

    struct symtag {

    char id[10];

    int line;

    char type;

    int usage;

    } sym[100], *psym;

    psym = &sym[0]; /* inicializim i "psym" */

    Kjo e ben ‘psym’ nje tregues strukture te te njejtit lloj me tabelen ‘sym’ dhe e inicializon

    ate qe te tregoje adresen e elementit te pare te ‘sym’.

    E njejta gje mund te behej ne dy hapa. Ne te parin:

    struct symtag {

    ... trupi i struktures

    };

    dhe pastaj te shkruanim:

    struct symtag sym[100];

    struct symtag *psym;

    qe e deklaron tabelen dhe treguesin dhe qe mund te shkruhet edhe:

    struct symtag sym[100], *psym;

    33

  • Menyra me te cilen i referohemi nje pjestari te nje strukture me tregues eshte si me poshte:

    treg -> pjestar-strukture

    Simboli ‘-¿’ tregon qe po tregojme tek nje anetar i nje strukture; ‘-¿’ perdoret ne ate

    kontekst. treg eshte tregues tek (baza e) struktures qe permban anetain e struktures.

    Keshtu mund te kemi konstruksione si:

    psym->type = 1;

    psym->id[0] = ’a’;

    etj.

    Per shprehje me te komplikuara eshte mire qe te vendosen kllapat. P.sh.:

    struct { int x, *y; } *p;

    p->x++ inkrementon x

    ++p->x e njejta gje behet edhe ketu!

    (++p)->x inkrementon p para se te merret x

    *p->y++ perdor y si tregues, dhe pastaj e inkrementon ate

    *(p->y)++ e njejta gje behet edhe ketu

    *(p++)->y perdor y si tregues, dhe pastaj inkrementon p

    Menyra e mbajtjes mend te ketyre eshte se -¿, ., ( ) dhe [ ] jane te lidhura ngushte.

    Nje shprehje qe permban nje prej tyre trajtohet si nje njesi. p-¿x, a[i], y.x dhe f(b) jane

    ekzaktesisht emra sic eshte ‘abc’.

    Nese ‘p’ eshte tregues i nje strukture, veprimet aritmetike mbi ‘p’ marrin ne kon-

    siderate madhesine struktures. Per shmbull, ‘p++’ inkrementon ‘p’ ne menyre qe te

    merret elementi pasardhes i tabeles se strukturave. Duhet pasur parasysh se madhesia e

    struktures NUK eshte shuma e madhesive te elementeve te saj: per aresye te shtrirjes se

    objekteve te permasave te ndryshme, ne struktura mund te kete ”hapsira boshe”.

    Me sa u tha, jemi ne gjendje te kalojme ne versionin me trregues te shembullit te

    meparshem.

    34

  • struct symtag {

    char id[10];

    int line;

    char type;

    int usage;

    } sym[100];

    main( ) {

    struct symtag *shiko( );

    struct symtag *psym;

    ...

    if( (psym = shiko(emer)) ) /* tregues jo-zero */

    psym -> usage++; /* tregon se gjendet */

    else

    install(emer, newline, newtype);

    ...

    }

    struct symtag *shiko(s)

    char *s; {

    struct symtag *p;

    for( p=sym; p < &sym[nsym]; p++ )

    if( krahaso(s, p->id) > 0)

    return(p);

    return(0);

    }

    Funksioni ‘krahaso’ nuk ndryshon. ‘p -> id’ i referohet nje korde karakteresh.

    Ne ‘main’ testohet treguesi i kthyer prej ‘shiko’ perkundrejt zeros, duke u bazuar

    ne faktin se nje tregues eshte me perkufizim gjithnje jozero kur ai tregon tek diçka.

    35

  • Manipulimet e tjera me tregues jane triviale.

    I vetmi kompleksitet eshte bashkesia e rreshtave si:

    struct symtag *shiko( );

    Kjo na sjell ne nje fushe qe do e trajtojme nxitimthi; çeshtja e tipeve te funksionit.

    Deri tani gjithe funksionet e trajtuara na kthenin numra te plote (ose karaktere qe eshte

    thuajse e njejta gje). Si te bejme kur funksioni kthen diçka tjeter, p.sh. nje tregues te nje

    strukture? Rregulli eshte qe funksioni qe s’kthen nje numer te plote, duhet ta deklaroje

    se çfare do ktheje. Informacioni per tipin shkon para emrit te funksionit (qe e ben emirn

    te veshtire per tu pare).

    Shembuj:

    char f(a)

    int a; {

    ...

    }

    int *g( ) { ... }

    struct symtag *shiko(s) char *s; { ... }

    Funksioni ‘f’ kthen nje karakter, ‘g’ kthen nje tregues mbi nje te plote dhe ‘shiko’ kthen

    nje tregues mbi nje strukture te se njejtes forme me ”symtag”. Nese do t’i perdorim keto

    funksione, duhet te bejme nje deklarim aty ku do t’i perdorim ato (siç u be ne funksionin

    kyesor te shembullit me lart).

    Te vihet re paralelizmi ndermjet deklarimeve

    struct symtag *shiko( );

    struct symtag *psym;

    Kjo tregon se ‘shiko’ dhe ‘psym’ perdoren te dy ne te njejten menyre - si treguesa te nje

    strukture, paçka se njeri eshte funksion dhe tjetra ndryshore.

    36

  • 12.3 Bashkimet

    Bashkimet lejojne referimin e se njejtes pjese kujtese me ane te tipeve te ndryshme te te

    dhenave. Deklarimi i tyre eshte i ngjashem me strukturat megjithese funksionaliteti eshte

    krejt i ndryshem:

    union emer_bashkimi {

    tipi_1 anetar_1;

    tipi_2 anetar_2;

    tipi_3 anetar_3;

    .

    } emer_objekti;

    Anetaret e nje deklarimi union zene te njejten hapsire fizike ne kujtese. Madhesia e saj

    eshte ajo e anetarit me te madhe branda deklarimit, p.sh.:

    union tipet {

    char c;

    int i;

    float f;

    } mytypes;

    percakton tre elemente:

    mytypes.c

    mytypes.i

    mytypes.f

    secili me tipe te ndryshme te dhenash. Meqe ata i referohen te njetit vend nje kujtese,

    ndryshimi i permbajtjes se njerit do te sjelle ndryshimin e gjithe te tjereve. Nje nga per-

    dorimet e union eshte bashkimi i nje tipi elementar me nje tabele strukturash elementesh

    me te vegjel, p.sh.:

    37

  • union mix_t {

    long l;

    struct {

    short lart;

    short poshte;

    } s;

    char c[4];

    } mix;

    percakton tri emra qe lejojne referimin e te njejtit grup prej 4 byte: mix.l, mix.s,

    mix.c. Keto mund te perdoren sipas menyres qe ne duam t’i referohemi ketyre 4 byte:

    sikur te ishin nje tip i vetem long, sikur te ishin dy elemente short apo nje tabele

    char. Rreshtimi real dhe rendi i anetareve te nje bashkimi varet prej platofrmes, prandaj

    problemi i portabilitetit te kodit duhet patur parasysh kur perdorim union.

    12.4 Bashkimet Anonime

    Ne rast se nje bashkim deklarohet pa emer, ate here ai eshte anonim. Kjo ben qe te

    mund t’i referohemi anetareve te tij ne menyre te drejtperdrejte. Ndryshimi i vetem ne

    deklarimet

    // strukture me bashkim te zakonshem

    struct {

    char titulli[50];

    char autori[50];

    union {

    float dollare;

    int euro;

    } cmimi;

    } liber;

    dhe

    38

  • // strukture me bashkim anonim

    struct {

    char titulli[50];

    char autori[50];

    union {

    float dollare;

    int euro;

    };

    } liber;

    eshte fakti qe emri i bashkimit mungon ne rastin e dyte. Kjo ben qe referimi i anetareve

    dollare dhe euro te nje objekti te ketij tipi ne rastin e pare te behet:

    liber.cmimi.dollare

    liber.cmimi.euro

    ndersa ne rastin e dyte behet:

    liber.dollare

    liber.euro

    Rikujtojme se dollare dhe euro jane anetare te nje bashkimi qe zene te njejten hapsire

    fizike ne kujtese, prandaj nuk mund te perdoren per te vendosur vlera te ndryshme ne te

    njejten kohe.

    12.5 Numratoret (enum)

    Numratoret krijojne nje tip te ri te dhenash qe nuk kufizohet ne tipet themelore, pra eshte

    nje tip ktejt i ri. Forma e tyre eshte:

    enum emer_numratori {

    vlera1,

    vlera2,

    39

  • vlera3,

    .

    } emer_objekti;

    P.sh. duam te krijojme nje tip te ri ndryshoreje qe permban ngjyrat:

    enum ngjyre {gjelber, kuq, blu};

    Verjem se nuk kemi asnje tip themelor ne kete deklarim. Vlerat e mundshme qe merr

    ky tip jane ato konstante te vendosura brenda kllapave gjarperueshe. Keshtu, me ty

    deklaruar ky tip pohimet e meposhteme jane legale:

    ngjyre ngjyra1;

    ngjyra1 = blu;

    if (ngjyra1 == gjelber) ngjyra1 = red;

    Numratoret jane kompatibel ne tip me ndryshoret numerike; kjo do te thote qe konstan-

    teve u vihet ne korrespondence nje vlere e plote. Ne rast se nuk jepet, nenkuptohet qe

    vlera e pare shorerohet me 0, e dyta me +1, etj. Po ne kemi ne dore te ndryshojme keto

    vlera te plota si p.sh.:

    enum muaji { janar=1, shkurt, mars, prill, maj, qershor, korrik, gusht, shtator, tetot,

    Ne kete rast ndryshorja viti i tipit numrator muaji mund te permbaje nje prej vlerave

    te mesiperme qe jane ekuivalente me vlerat prej 1 deri ne 12 (dhe jo prej 0 deri ne 11

    sepse ne e vendosem janarin te barabarte me 1).

    13 Hyrje tek Klasat

    Klasa eshte nje koncept i zgjeruar i nje strukture te dhenash: ajo mban jo vetem te dhena

    por edhe funksione. Nje objekt eshte konkretizimi i nje klase. Ne analogji me ndryshoret,

    klasa do te ishte tipi ndersa objekti do te ishte ndryshorja. Ato deklarohen si me poshte:

    40

  • class emri_i_klases {

    fusha_e_veprimit_1:

    anetar_1;

    fusha_e_veprimit_2:

    anetar_2;

    ...

    } emrat_e_objekteve;

    Anetaret jane te dhena ose funksione, ndersa fusha e veprimit eshte tri llojesh:

    private, referohet vetem prej anetareve te se njejtes klase ose miqve te tyre;

    protected, referohet prej anetareve te se njejtes klase ose miqve te tyre si edhe prej

    anetareve te klasave qe rrjedhin prej tyre;

    public, referohet prej secilit vend ku shfaqet objekti.

    Kur nuk jepet fusha e veprimit, ajo nenkuptohet te jete private, p.sh.

    class Klase_Drejtkendeshash {

    int x, y;

    public:

    void vendos_vlerat (int,int);

    int siperfaqe (void);

    } drejtkendesh;

    deklaron ‘drejtkendesh’ si objekt te klases ‘Klase Drejtkendeshash’. Anetaret e kesaj klase

    jane: ndryshoret ‘x’ dhe ‘y’ me fushe referimi private, meqe fusha e veprimit nuk jepet;

    funksionet publike ‘vendos vlerat’ dhe ‘siperfaqe’.

    Anetaret publike te objekteve mund te referohen kudo ne program njelloj si te dhenave

    ne nje strukture te dhenash, p.sh.:

    drejtkendesh.vendos_vlerat(3,4);

    siperfaqja_ime=drejtkendesh.siperfaqe();

    41

  • Te dhenat ‘x’ dhe ‘y’ nuk mund te referohen jashte trupit te klases.

    Me poshte kemi shembullin e plote te klases ‘Klase Drejtkendeshash’:

    // Shembull klasash 1

    #include

    using namespace std;

    class Klase_Drejtkendeshash {

    int x, y;

    public:

    void vendos_vlerat (int,int);

    int siperfaqe () {return (x*y);}

    };

    void Klase_Drejtkendeshash::vendos_vlerat (int a, int b) {

    x = a;

    y = b;

    }

    int main () {

    Klase_Drejtkendeshash drejtkendesh;

    drejtkendesh.vendos_vlerat(3,4);

    cout

  • ndryshoret brenda funksionit kane te njejten fushe veprimi sikur funksionit te jete perku-

    fizuar brenda klases. Keshtu, ndryshoret ‘x’ dhe ‘y’ jane ndryshore private te klases

    ‘Klase Drejtkendeshash’ dhe si te tilla mund te referohen vetem brenda klases, pra edhe

    ne funksionin ‘vendos vlerat’, anetar i kesaj klase.

    Deklarimi jashte klases behet shpesh per ta bere kodin me te lexueshem. Nga ana

    tjeter, nese deklarimi brenda klases e ben nje funksion inline, deklarimi jashte eshte si cdo

    deklarim tjeter i zakonshem funksioni.

    Per nje klase ne mund te deklarojme nje ose me shume objekte, p.sh.:

    // Shembull klasash 2

    #include

    using namespace std;

    class Klase_Drejtkendeshash {

    int x, y;

    public:

    void vendos_vlerat (int,int);

    int siperfaqe () {return (x*y);}

    };

    void Klase_Drejtkendeshash::vendos_vlerat (int a, int b) {

    x = a;

    y = b;

    }

    int main () {

    Klase_Drejtkendeshash drejtkendesh, drejtkendesh1;

    drejtkendesh.vendos_vlerat(3,4);

    drejtkendesh1.vendos_vlerat(5,6);

    cout

  • cout

  • class Klase_Drejtkendeshash {

    int gjeresi, gjatesi;

    public:

    Klase_Drejtkendeshash (int,int);

    int siperfaqe () {return (gjeresi*gjatesi);}

    };

    Klase_Drejtkendeshash::Klase_Drejtkendeshash (int a, int b) {

    gjeresi = a;

    gjatesi = b;

    }

    int main () {

    Klase_Drejtkendeshash drejtkendesh (3,4);

    Klase_Drejtkendeshash drejtkendesh1 (5,6);

    cout

  • caktohet ne menyre dinamike dhe leshohet duhet perdorur fshirjen e nje operatori.

    Desktruktori duhet te kete te njetin emer te klases, te filluar me shenjen gjarperueshe

    ‘ ’. Po keshtu ai nuk duhet te kete asnje tip kthimi.

    Perdorimi i destruktoreve eshte veçanerisht i pershtshem kur nje objekt cakton kujtese

    dinamike gjate kohes se tij te ekzistences dhe ne momentin e shkaterimit te tij ne duam

    te leshojme kujtesen qe i ishte zene objektit.

    // Shembull klasash 4: konstruktoret dhe destruktoret

    #include

    using namespace std;

    class Klase_Drejtkendeshash {

    int *gjeresi, *gjatesi;

    public:

    Klase_Drejtkendeshash (int,int);

    ~Klase_Drejtkendeshash ();

    int siperfaqe () {return (*gjeresi * *gjatesi);}

    };

    Klase_Drejtkendeshash::Klase_Drejtkendeshash (int a, int b) {

    gjeresi = new int;

    gjatesi = new int;

    *gjeresi = a;

    *gjatesi = b;

    }

    Klase_Drejtkendeshash::~Klase_Drejtkendeshash () {

    delete gjeresi;

    delete gjatesi;

    }

    46

  • int main () {

    Klase_Drejtkendeshash drejtkendesh (3,4), drejtkendesh1 (5,6);

    cout

  • gjeresi = 5;

    gjatesi = 5;

    }

    Klase_Drejtkendeshash::Klase_Drejtkendeshash (int a, int b) {

    gjeresi = a;

    gjatesi = b;

    }

    int main () {

    Klase_Drejtkendeshash drejtkendesh (3,4);

    Klase_Drejtkendeshash drejtkendesh1;

    cout

  • int a,b,c;

    void multiply (int n, int m) { a=n; b=m; c=a*b; };

    };

    kompilatori nenkupton se ‘CExample’ ka nje konstruktor default. Ne kete menyre objektet

    e kesaj klase mund te deklarohen pa argumenta:

    CExample ex;

    Por sapo te jete deklaruar prej vete nesh nje konstruktor per klasen, kompilatori nuk na

    me asnje konstruktor default nga ane e tij. Keshtu nqse shkruajme:

    class CExample {

    public:

    int a,b,c;

    CExample (int n, int m) { a=n; b=m; };

    void multiply () { c=a*b; };

    };

    do duhet te deklarojme objektet si p.sh.:

    CExample ex (2,3);

    Ndersa, deklarimi:

    CExample ex;

    do te ishte i gabuar ne kete rast, meqenese ne kemi deklaruar konstruktorin ne menyre

    eksplicite per klasen ne fjale.

    Pervec krijimit te nje konstruktori default, kompilatori deklaron ne menyre implicite

    edhe tre anetare te tjere, te cilet jane funksionet e veçanta: konstruktori i kopjimit,

    operatori i caktimit per kopjimin dhe destruktori default.

    Konstruktori i kopjimit dhe operatori i caktimit per kopjimin kopjojne te gjitha

    te dhenat qe ndodhen ne nje objekt tjeter tek te dhenat e objektit aktual. Prandaj,

    deklarimet e meposhteme te dy objekteve do te jene korrekte:

    49

  • CExample ex (2,3);

    // konstruktori implicit i kopjimit (te dhenat kopjohen prej ex):

    CExample ex2 (ex);

    Treguesit tek Klasat

    Treguesit mund te tregojne edhe tek klasat. Kjo per aresyen e thjeshte se, me tu deklaruar,

    klasat perbejne nje tip me vete. Keshtu, p.sh.

    Klase_Drejtkendeshash *tregues_drejtkendesh

    eshte nje tregues tek nje objekt i klases ‘Klase Drejtkendeshash’. Ashtu si me strukturat

    e te dhenave, per t’iu referuar drejtperdrejt nje anetari te nje objekti te treguar prej nje

    treguesi ne mund te perdorim operatorin shigjete ’−¿’. Le te japim nje shembull me disa

    kombinime:

    // Klasa Shembull 6: tregues tek klasat

    #include

    using namespace std;

    class Klase_Drejtkendeshash {

    int gjeresia, gjatesia;

    public:

    void vendos_vlerat (int, int);

    int siperfaqe (void) {return (gjeresia * gjatesia);}

    };

    void Klase_Drejtkendeshash::vendos_vlerat (int a, int b) {

    gjeresia = a;

    gjatesia = b;

    }

    50

  • int main () {

    Klase_Drejtkendeshash a, *b, *c;

    Klase_Drejtkendeshash * d = new Klase_Drejtkendeshash[2];

    b= new Klase_Drejtkendeshash;

    c= &a;

    a.vendos_vlerat (1,2);

    b->vendos_vlerat (3,4);

    d->vendos_vlerat (5,6);

    d[1].vendos_vlerat (7,8);

    cout

  • shprehja lexohet

    *x tregohet prej x

    &x adresa e x

    x.y anetari y i objektit x

    x->y anetari y i objektit te treguar prej x

    (*x).y anetari y i objektit te treguar prej by x (ekuivalent me paraardhesin)

    x[0] objekti i pare i treguar prej x

    x[1] objekti i dyte i treguar prej x

    x[n] objekti i (n+1)-te i treguar prej x

    Klasat e perkufizuara me struct dhe union

    Klasat mund te perkufizohet edhe prej fjaleve kyçe struct dhe union.

    Konceptet e klases dhe struktures se te dhenave jane aq te ngjashme sa qe te dy keto

    fjale kyçe kane ne C++ te njejtin funksionalitet perveç se anetaret e klases se deklaruar

    me struct kane fushe veprimi publike ne vend te asaj private qe kane ato te deklaruar

    me class. Ky eshte ndryshimi i vetem, perndryshe per çdo qellim tjeter te dyja fjalet

    kyçe jane ekuivalente.

    Koncepti brenda union eshte i ndryshem prej atij te stuct dhe class. Kjo per aresyen

    se union i vendos te dhenat, anetare te saj, nje e nga nje ne kujtese. Por ato jane edhe

    klasa dhe si te tilla mund te permbajne funksione si anetare te tyre. Refernca deafault ne

    union eshte publike.

    52

  • 14 Klasat II

    14.1 Operatoret e Mbingarkuar

    C++ permban mundesine e perdorimit te operatoreve standarte per te kryer veprime me

    klasa perveç atyre me tipet themelore. P.sh.

    int a, b, c;

    a = b + c;

    eshte kod i vlefshem ne C++ meqe ndryshoret e ndryshme te mbledhjes jane te gjitha

    te tipeve themelore. Kjo nuk do te thote qe kjo te shtrihet menjehere edhe per raste si i

    meposhtemi:

    struct {

    string product;

    float price;

    } a, b, c;

    a = b + c;

    Rreshti i fundit do te shkaktoje gabim ne kompilim meqe nuk kemi treguar si do te sillet

    klasa ne lidhje me operatorin e mbledhjes. Kjo arrihet me ane te mbingarkimit te opera-

    toreve, nje tipar i C++, me ane te te cilit ne mund te pershkruajme klasa qe te kryejne

    veprime me operatoret standarte. Me poshte jepet lista e operatoreve te mbingarkueshem:

    Overloadable operators

    + - * / = < > += -= *= /= >

    = == != = ++ -- % & ^ ! |

    ~ &= ^= |= && || %= [] () , ->* -> new

    delete new[] delete[]

    Per te mbingarkuar nje operator deklarohen funksionet operatoriale, te cilet jane funk-

    sione te zakonshme emrat e te cileve jane fjala kyce operator e ndjekur prej shenjes se

    operatorit qe duam te mbingarkojme:

    53

  • tipi operator shenja (parametra) { /*...*/ }

    Me poshte jepet nje shembull i mbingarkimit te operatorit te mbledhjes (+) per klasen e

    vektoreve me dy permasa:

    // Vektoret: shmebull i mbingarkimit te operatoreve

    #include

    using namespace std;

    class CVector {

    public:

    int x,y;

    CVector () {};

    CVector (int,int);

    CVector operator + (CVector);

    };

    CVector::CVector (int a, int b) {

    x = a;

    y = b;

    }

    CVector CVector::operator+ (CVector param) {

    CVector temp;

    temp.x = x + param.x;

    temp.y = y + param.y;

    return (temp);

    }

    int main () {

    54

  • CVector a (3,1);

    CVector b (1,2);

    CVector c;

    c = a + b;

    cout

  • i perfshire ne main() nuk do te ishte i vlefshem nese nuk do te kishim kontruktorin bosh,

    pra ate pa parametra.

    Ne fakt, me mire do te ishte qe konstruktori te kishte brenda bllokut nje percaktim te

    ndryshoreve te anetarit:

    CVector () { x=0; y=0; };

    Kjo nuk u be me lart per te thejshtuar leximin e kodit ne ndihme te ilustrimit te konceptit

    te mbingarkimit.

    Pervec konstruktorit deafult dhe konstruktorit te kopjimit, ne rastin kur keto nuk

    deklarohen, klasat perfshijne edhe nje percaktim deafult te operatorit te caktimit (=) me

    klasen vete si parameter:

    CVector d (2,3);

    CVector e;

    e = d; // operatori i caktimit per kopjimin

    Operatori i caktimit per kopjimin eshte i vetmi anetar funksion operatorial i ndertuar ne

    menyren deafult. Natyrisht, ai mund te ripercaktohet per caredo funksionaliteti tjeter,

    p.sh. per kopjimin e anetareve te vecante te klases ose per te kryer procedura shtese

    inicializuese.

    Mbingarkimi i operatoreve nuk detyron veprimin perkates te kete lidhje me kuptimin

    matematik ose ate te zakonshem te operatorit, megjithese kjo rekomandohet. P.sh. kodi

    mund te mos jete shume intuitiv nese perdoret (+) per te zbritur dy klasa.

    Megjithese prototipi i nje funksioni operator+ trajton si parameter ate qe qendron

    djathtas tij, kjo mund te mos jete gjithnje e vertete per operatore te tjere. Me poshte

    jepet nje tabele me menyren e deklarimit te funksioneve operatoriale (zevendeso kudo @

    me operatorin perkates):

    Shprehje Operatori Funksioni Anetar Funksioni Global

    @a + - * & ! ~ ++ -- A::operator@() operator@(A)

    56

  • a@ ++ -- A::operator@(int) operator@(A,int)

    a@b + - * / % ^ & | < > ==

    != = > && || , A::operator@ (B) operator@(A,B)

    a@b = += -= *= /= %= ^=

    &= |= = [] A::operator@ (B) -

    a(b, c..) () A::operator() (B, C...) -

    a->x -> A::operator->() -

    ku a eshte nje objekt i klases A, b objekt i klases B dhe c objekt i klases C.

    Verejme qe disa operatore mund te mbingarkohen me dy menyra: si nje funksion

    anetar ose si nje funksion global. Megjithese perdorimi i tyre nuk ndryshon, duhet patur

    parasysh se funksionet te cilat nuk jane anetare te nje klase nuk mund t’i referohen

    anetareve private ose te mbrojtur te asaj klase pervecse ne rastin kur funksioni global

    eshte mik i saj (miqesia shpjegohet me poshte).

    14.2 Fjala kyçe this

    Fjala kyçe this paraqet nje tregues tek nje objekt, funksioni anetar i te cilit eshte duke

    u ekzekutuar. Ai eshte tregues tek vete objekti.

    Nje prej perdorimeve te tij eshte te kontrolloje nese nje parameter i pasuar tek nje

    funksion anetar eshte vete objekti, p.sh.:

    // Shembull me ‘this’

    #include

    using namespace std;

    class CDummy {

    public:

    int is_it_me (CDummy& param);

    };

    57

  • int CDummy::is_it_me (CDummy& param)

    {

    if (&param == this) return true;

    else return false;

    }

    int main () {

    CDummy a;

    CDummy* b = &a;

    if ( b->is_it_me(a) )

    cout

  • 14.3 Anetaret Statike

    Nje klase mund te permbaje anetare statike, te dhena ose funksione.

    Te dhenat, anetare statike te nje klase, njihen gjithashtu si ”ndryshore te klases”,

    meqe ekziston nje velre e vetme per cdo objekt te se njejtes klase. Permbajtja e tyre nuk

    ndryshon prej nje objekti te klases tek tjetri.

    Nje anetar statik mund te perdoret p.sh. si nje ndryshore brenda nje klase qe mund

    te permbaje nje numerues te objekteve te krijuar te klases:

    // Anetaret statike tek klasat

    #include

    using namespace std;

    class CDummy {

    public:

    static int n;

    CDummy () { n++; };

    ~CDummy () { n--; };

    };

    int CDummy::n=0;

    int main () {

    CDummy a;

    CDummy b[5];

    CDummy * c = new CDummy;

    cout

  • }

    Cilat do te ishin rezultet? Te verifikohen me ane te kodit.

    Ne fakt, anetaret statike kane te njejtat veti si ndryshoret globale qe gezojne edhe

    fushen e veprimit ne klase. Per kete aresye, si dhe per te shmangur deklarimin e tyre disa

    here, ne mund te deklarojme prototipin ne klase por jo percaktimin se bashku me inicial-

    izimin. Ne menyre qe te inicializojme nje anetar statik te dhenash ne duhet te perfshijme

    nje percaktim formal jashte klases, ne fushe globale, si ne shembullin e mesiperm:

    int CDummy::n=0;

    Meqe kemi te bejme me nje vlere te vetme te ndryshores per te gjithe objektet e se njejtes

    klase, atij mund t’i referohemi si nje anetar te nje objekti cfaredo te asaj klase ose ne

    menyre te drejtperdrejte me ane te emrit te klases (por kujdes, kjo e fundit vlen vetem

    per anetaret statike):

    cout

  • 15 Miqesia dhe Trashegimia

    15.1 Funksionet Miq

    Anetaret priavte dhe te mbrojtur te nje klase nuk mund te referohen jashte klases ku jane

    deklaruar. Prej ketij rregulli nejne perjashtim miqte.

    Miqte jane funksione ose klasa te deklaruara si te tilla.

    Ne qofte se duam te deklarojme nje funksion te jashtem si mik te nje klase, pra

    te lejojme kete funksion t’i referohet anetareve private dhe te mbrojtur te kesaj klase,

    atehere do duhet te deklarojme nje prototip te ketij funksioni te jashtem brenda klases

    duke vendosur perpara tij fjalen kyce friend:

    // Funksionet Miq

    #include

    using namespace std;

    class K_Drejtkendesh {

    int gjeresi, lartesi;

    public:

    void vendos_vlerat (int, int);

    int siperfaqe () {return (gjeresi * lartesi);}

    friend K_Drejtkendesh drejtk_dyfishuar (K_Drejtkendesh);

    };

    void K_Drejtkendesh::vendos_vlerat (int a, int b) {

    gjeresi = a;

    lartesi = b;

    }

    K_Drejtkendesh drejtk_dyfishuar (K_Drejtkendesh param_drejtk)

    61

  • {

    K_Drejtkendesh drejtk;

    drejtk.gjeresi = param_drejtk.gjeresi*2;

    drejtk.lartesi = param_drejtk.lartesi*2;

    return (drejtk);

    }

    int main () {

    K_Drejtkendesh drejtk_1, drejtk_2;

    drejtk_1.vendos_vlerat (2,3);

    drejtk_2 = drejtk_dyfishuar (drejtk_1);

    cout

  • 15.2 Klasat Mike

    Ashtu sic eshte e mundur per te percaktuar nje funksion mik, ne mund te percaktojme

    nje klase si nje mike te nje tjetre. Ne kete menyre klasa mike ka te drejte t’i referohet

    anetareve private dhe te mbrojtur te klases tjeter:

    // Klasa mike

    #include

    using namespace std;

    class K_Katror;

    class K_Drejtkendesh {

    int gjeresi, lartesi;

    public:

    int siperfaqe ()

    {return (gjeresi * lartesi);}

    void kthe_ne_katror (K_Katror a);

    };

    class K_Katror {

    private:

    int brinje;

    public:

    void vendos_brinjen (int a)

    {brinje=a;}

    friend class K_Drejtkendesh;

    };

    void K_Drejtkendesh::kthe_ne_katror (K_Katror a) {

    63

  • gjeresi = a.brinje;

    lartesi = a.brinje;

    }

    int main () {

    K_Katror katror;

    K_Drejtkendesh drejtk;

    katror.vendos_brinjen(4);

    drejtk.kthe_ne_katror(katror);

    cout

  • 15.3 Trashegimia ndermjet klasave

    Nje tipar thelbesor i klasave ne C++ eshte trashegimia. Trashegimia lejon krijimin e

    klasava te cilat rrjedhin prej klasave te tjera; kjo ben qe ato te perfshijne automatik-

    isht disa prej anetareve te tyre ”prinder”. P.sh. supozojme se duam te deklarojme nje

    sere klasash qe pershkruajne shumekendesha si K_Drejtkendesh, K_Trekendesh. Ato

    kane veti te perbashketa sic jane baza dhe lartesia. Keto veti mund te pershkruhen ne

    boten e klasave me ane te klases K_Shumkendesh, prej se ciles mund te dreivojme klasat

    K_Drejtkendesh dhe K_Trekendesh.

    Klasa K_Shumkendesh do te permbaje anetare qe jane te perbashket per te dy tipet

    e shumekendeshave. Ne rastin tone: baza dhe lartesia, por me tipare te vecanta qe

    ndryshojne prej njerit shumekendesh tek tjetri.

    Klasat qe rrjedhin prej te tjerave trashegojne te gjithe anetaret e referueshem te klases

    baze. Kjo do te thote se ne qofte nje klase baze perfshin nje anetar A dhe ajo e derivuar

    nje anetar B, atehere kjo e fundit do te permbaje edhe anetarin A dhe ate B.

    Per te derivuar nje klase prej nje tjetre perdoret shenja ‘:’ ne deklarimin e clases se

    rrjedhur sipas formatit te meposhtem:

    class emer_i_klases_se_rrjedhur: public emer_i_klases_baze

    { /*...*/ };

    ku public mund te zevendesohet me nje prej referencave te tjera si protected ose

    private. Referenca pershkruan nivelin minimal te referueshmerise se anetareve qe trashe-

    gohen prej klases baze.

    // Klasat e derivuara

    #include

    using namespace std;

    class K_Shumkendesh {

    protected:

    65

  • int baze, lartesi;

    public:

    void vendos_vlerat (int a, int b)

    { baze=a; lartesi=b;}

    };

    class K_Drejtkendesh: public K_Shumkendesh {

    public:

    int siperfaqe ()

    { return (baze * lartesi); }

    };

    class K_Trekendesh: public K_Shumkendesh {

    public:

    int siperfaqe ()

    { return (baze * lartesi / 2); }

    };

    int main () {

    K_Drejtkendesh drejtk;

    K_Trekendesh trek;

    drejtk.vendos_vlerat (4,5);

    trek.vendos_vlerat (4,5);

    cout

  • baze, lartesi dhe vendos_vlerat().

    Rikujtojme se anetaret e mbrojtur jane te ngjashem me ata private. I vetmi ndryshim

    ka te beje me trashegimine: kur nje klase trashegon prej nje tjetre, anetaret e rrjedhur

    mund t’i referohen atyre te mbrojtur te trasheguar nga klasa baze, por jo atyre private.

    Keshtu, meqe donim baze dhe lartesi te referohen prej anetareve te klasave te rrjedhura

    K_Drejtkendesh dhe K_Trekendesh dhe jo vetem prej anetareve te K_Shumkendesh, kemi

    perdorur referimin e mbrojtur ne vend te atij privat.

    Tipet e ndryshme te referimit mund te permblidhen si me poshte:

    Referimi public protected private

    anetare te se njejtes klase po po po

    anetare te klasave te rrjedhura po po jo

    jo anetare po jo jo

    ku me ”jo anetare” nenkuptojme cdo referim jashte klases si ato prej main(), prej nje klase

    ose funksioni tjeter. Ne shmembullin tone anetare e trasheguar prej K_Drejtkendesh dhe

    K_Trekendesh kane te njejtat leje per tu referuar ashtu sic kishin brenda klases baze

    K_Shumkendesh:

    K_Shumkendesh::baze // referim i mbrojtur

    K_Drejtkendesh::baze // referim i mbrojtur

    K_Shumkendesh::vendos_vlerat() // referim publik

    K_Drejtkendesh::vendos_vlerat() // referim publik

    Kjo per aresye se kemi perdorur fjalen kyçe public per te caktuar mardhenien e trashegimise

    se seciles prej klasave te rrjedhura:

    class K_Drejtkendesh: public K_Shumkendesh { ... }

    Ne kete rast public pas shenjes ‘:’ tregon nivelin minimal te referimit te te gjithe

    anetareve te trasheguar prej klases qe ndjek ate (ne kete rast K_Shumkendesh). Meqe

    67

  • public eshte niveli me i larte i referimit, duke shkruar kete fjale, klasa e rrjedhur do te

    trashegoje tek gjithe anetare e saj te njetat nivele qe ata kishin ne klasen baze.

    Ne rast se japim referim me te kufizuar si p.sh. protected, te gjithe anetare publike

    te klases baze trashegohen si te mbrojtur ne klasen e rrjedhur. Ne rast se shkruajme

    private, atehere te gjithe anetaret e klases baze trashegohen si private. P.sh. ne rast se

    bije eshte nje klase e rrjedhur prej klases nene:

    class bije: protected nene;

    kjo ben qe protected te jete niveli maksimal i referimit per anetaret e klases bije qe

    trashegohen prej klases nene. Kjo do te thote se te gjithe anetaret publike tek nene behen

    te mbrojtur tek bije. Sigurisht, kjo gje nuk kufizon klasen bije te deklaroje anetaret

    e vet publike. Pra, niveli maksimal i referimit vendoset per anetaret e trasheguar prej

    klases nene.

    Ne rast se nuk japim ne menyre eksplicite nivelin e referimit per trashegimin, kompi-

    latori do ta nenkuptoje ate private per klasat e deklaruar me class dhe public per ato

    te deklaruara me struct.

    15.4 Çfare trashegohet prej klases baze?

    Ne parim, nje klase e rrjedhur trashegon cdo anetar te klases baze me perjashtim te:

    • konstruktorin dhe desktruktorin e tij

    • anetaret e tij operator=()

    • miqte e tij

    Megjithse konstructoret dhe destruktoret e klases baze nuk trashegohen ne vetvete, kon-

    struktori i saj default (pra, konstruktori pa parametra) dhe deskturktori i vet thirren

    gjithnje kur krijohet ose shkaterrohet nje objekt i ri i klases se rrjedhur. Ne rast se klasa

    baze nuk ka konstruktor default ose ne rast se duam te thirret nje konstruktor i mbin-

    garkuar kur krijohet nje objekt i klases se rrjedhur, kete mund ta japim ne cdo perkufizim

    te konstruktorit te klases se rrjedhur:

    68

  • emer_konstruktori_te_rrjedhur (parametra) : emer_konstruktori_baze (parametra) {...}

    Per shembull:

    // konstruktoret e klasave te rrjedhura

    #include

    using namespace std;

    class mother {

    public:

    mother ()

    { cout

  • son daniel(0);

    return 0;

    }

    mother: no parameters

    daughter: int parameter

    mother: int parameter

    son: int parameter

    Verejme ndryshimin ndermjet kontruktorit mother qe thirret kur krijohet nje objekt i ri

    daughter dhe atij te nje objekti son. Kjo per aresye te deklarimit te daughter dhe son:

    daughter (int a) // nuk jepet asgje: therritet default

    son (int a) : mother (a) // jepet konstruktori: therritet pikirisht ky

    15.5 Trashegimia e shumefishte

    Eshte e mundur qe nje klase te trashegoje anetare prej me shume se nje klase. Kjo

    behet duke ndare me presje klasat e ndryshme baze ne deklarimin e klases se rrjedhur.

    Per shembull, ne rast se na duhet nje klase qe printon ne ekran, K_Dalje, dhe duam qe

    klasat K_Drejtkendesh dhe K_Trekendesh te trashegojne anetaret e saj pervec te atyre

    te K_Shumkendesh, ne mund te shkruajme:

    class K_Drejtkendesh: public K_Shumkendesh, public K_Dalje;

    class K_Trekendesh: public K_Shumkendesh, public K_Dalje;

    Me poshte jepet shembulli i plote:

    70

  • // Trashegimia e shumfishte

    #include

    using namespace std;

    class K_Shumkendesh {

    protected:

    int baze, lartesi;

    public:

    void vendos_vlerat (int a, int b)

    { baze=a; lartesi=b;}

    };

    class K_Dalje {

    public:

    void dalje (int i);

    };

    void K_Dalje::dalje (int i) {

    cout

  • int siperfaqe ()

    { return (baze * lartesi / 2); }

    };

    int main () {

    K_Drejtkendesh drejtk;

    K_Trekendesh trek;

    drejtk.vendos_vlerat (4,5);

    trek.vendos_vlerat (4,5);

    drejtk.dalje (drejtk.siperfaqe());

    trek.dalje (trek.siperfaqe());

    return 0;

    }

    72