Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
POO - Curs Doc-1.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
868.86 Кб
Скачать

§10. Tablouri de obiecte. Pointeri şi referinţe la obiecte. Pointeri la membrii clasei

Deoarece clasele sunt tipuri de date abstracte, în baza lor pot fi create tablouri de obiecte într-un mod similar cu crearea tablourilor în baza unor tipuri predefinite. Pentru tipuri predefinite există două descrieri care permit creare de tablouri, şi anume, fără iniţializarea elementelor:

tip_pr nume_tab[dim];

şi cu iniţializarea elementelor:

tip_pr nume_tab[dim]={val1, val2, ..., valk};

Cu toate că descrierile pentru tablouri de obiecte sunt asemănătoare, totuşi pentru cazul claselor, trebuie aduse unele concretizări. Vor fi distinse trei posibilităţi, care corespund tipului constructorului din clasă.

a) Dacă clasa are fie constructor implicit, fie constructor fără parametri, fie constructorul poate fi redus la un constructor fără parametri, atunci pentru crearea unui tablou de obiecte va fi utilizată o descriere echivalentă cu descrierea fără iniţializare:

nume_cl nume_to[dim];

unde nume_cl este numele clasei în baza căreia este creat tabloul de obiecte, nume_to reprezintă denumirea tabloului de obiecte şi dim este dimensiunea tabloului.

b) Dacă clasa are un constructor cu un parametru sau constructorul poate fi redus la un constructor cu un parametru, atunci pentru crearea unui tablou de obiecte va fi utilizată o descriere echivalentă cu descrierea cu iniţializare:

nume_cl nume_to[dim]={val1, val2, ..., valk};

unde val1, val2, …, valk reprezintă valorile în baza cărora lucrează constructorul, creând obiectele.

c) Dacă însă constructorul are mai mult de un parametru fictiv, se obţine cazul general de descriere a tabloului de obiecte

nume_cl nume_to[N]={nume_cl(pr11,…,pr1k),…,

nume_cl(prN1,…,prNk­­)};

De exemplu, dacă considerăm că clasa carte are constructorul

carte(char *autor, char *titlu, int an);

atunci un tablou cu cinci obiecte va fi definit astfel

carte colectie[5]={

carte(”H.Schildt”,”C++ manual complet”,1997),

carte(”L.Negresu”,”Limbajele C şi C++”,2000),

carte(”B.Stroustrup”,”The C++ Programming Language”,1997),

carte(”K.Jamsa”,”Succes cu C++”,1997),

carte(”D.Somnea”,”Initiere in C++”,2006)

};

Cazul constructorului cu un singur parametru, de asemenea, se încadrează în cazul general, scriind astfel:

nume_cl nume_to[dim]={nume_cl(val1­),nume_cl(val2­)

,…,nume_cl(valk­­)};

dar desigur că această variantă este mai greoaie ca varianta b).

Utilizarea denumirii constructorului pentru descrierea fiecărui obiect generează o cale anevoioasă de creare, mai ales, când este necesară crearea unui tablou cu un număr mare de obiecte. Când se intenţionează a lucra cu tablouri mari de obiecte, atunci, de regulă, clasa în baza căreia se creează tablourile va trebui să conţină şi un constructor fără parametri sau unul care se reduce la un constructor fără parametri, pentru a evita descrieri pentru fiecare element al tabloului. De exemplu, considerând că clasa punct are un constructor cu valori implicite

punct(int x=0, int y=0);

este posibil de a defini un tablou cu o sută de elemente utilizând următoarea descriere:

punct puncte[100];

Accesarea unui element al tabloului de obiecte se realizează utilizând un indice întreg

nume_to[ind]

iar pentru a accesa un membru al unui element al tabloului de obiecte este utilizată următoarea expresie

nume_to[ind].nume_membru

indicele ind satisfăcând condiţia

0 ≤ ind ≤ dim-1

unde dim este dimensiunea tabloului de obiecte.

Deoarece obiectele sunt variabile create în baza unor clase, există posibilitatea de a accesa nu doar conţinutul lor, ci şi adresele lor. Operarea cu adresele obiectelor se face prin intermediul pointerilor la obiecte. Un pointer la obiecte de un anumit tip este definit astfel:

nume_cl *nume_var_po;

unde nume_cl determină tipul obiectelor cu care poate opera pointerul dat. Operatorii admisibili în contextul pointerilor sunt admisibili şi în contextul pointerilor la obiecte.

1) operatorul = care permite plasarea unei adrese într-un pointer

nume_var_po = expresie;

unde valoarea expresiei din dreaptă a operatorului de atribuire = reprezintă o adresă. Adeseori această expresie este exprimată prin adresa unui obiect, utilizând operatorul &

nume_var_po = &nume_ob;

sau prin adresa de început al unui tablou de obiecte

nume_var_po = nume_to;

sau prin valoare din alt pointer

nume_var_po = nume_var_po1;

2) având într-un pointer adresa unui obiect, poate fi realizat accesul la un membru al obiectului prin operatorul ->

nume_var_po -> nume_membru

3) având într-un pointer adresa unui obiect, poate fi citit conţinutul obiectului prin operatorul *

nume_ob = *nume_var_po;

4) dacă într-un pointer este adresa unui obiect, atunci incrementând valoarea dată cu operatorul ++ în pointer se va obţine adresa obiectului următor:

nume_var_po++;

5) dacă într-un pointer este adresa unui obiect, atunci decrementând valoarea dată cu operatorul -- în pointer se va obţine adresa obiectului anterior:

nume_var_po--;

6) dacă într-un pointer este adresa unui obiect, atunci incrementând valoarea dată cu o valoare naturală n, utilizând operatorul += în pointer se va obţine adresa obiectului al n-lea următor:

nume_var_po+=n;

7) dacă într-un pointer este adresa unui obiect, atunci decrementând valoare dată cu o valoare naturală n, utilizând operatorul -= în pointer se va obţine adresa obiectului al n-lea anterior:

nume_var_po-=n;

Operatorii aritmetici anteriori, fiind aplicaţi unui pointer, schimbă valoarea din pointer. Pot fi aplicaţi operatorii + şi - fără a schimba valoarea din pointer

nume_var_po + n

sau

nume_var_po - n

obţinând adresa obiectului al n-lea următor sau anterior.

În contextul pointerilor la obiecte pot fi aplicaţi, de asemenea, şi operatorii relaţionali >, <, >=, <=, ==, !=.

La aplicarea operatorului de atribuire are loc controlul riguros al tipului pointerului din partea stângă a operatorului de atribuire şi tipul expresiei din partea dreaptă. Operaţia de atribuire este efectuată doar la satisfacerea corespondenţei pointerilor. Corespondenţa pointerilor este satisfăcută în următoarele condiţii:

  1. pointerul din stânga operatorului de atribuire este de acelaşi tip ca şi valoarea din partea dreaptă;

  2. dacă pointerul din stânga şi valoarea din dreapta sunt de tipuri diferite, atunci sunt admise doar tipuri “inrudite”, adică clasă de bază – clasă derivată. Tipul din stânga operatorului de atribuire este de tip clasă de bază, iar tipul din dreapta este de tip clasă derivată.

De exemplu, dacă se presupune existenţa unor obiecte şi pointeri de tip clasă de bază şi de tip clasă derivată:

cl_baza *pob, ob;

cl_d *pod, od;

atunci vor fi valabile următoarele atribuiri:

pob = &ob; // corect

pod = &od; // corect

pob = pod; // corect

şi vor fi inadmisibile următoarele:

pod = pob; // incorect

pod = &ob; // incorect

În contextul claselor şi al obiectelor, există un pointer predefinit numit this, care indică adresa obiectului curent. Obiectul curent este obiectul care efectuează operaţia de apel a unei funcţii. La implementarea unei clase în orice funcţie membră a clasei, care nu este statică, poate fi utilizat pointerul this. Funcţiile statice nu se bucură de această proprietate. De exemplu, constructorul clasei punct poate fi realizat în felul următor:

punct(int x=0, int y=0)

{

this->x=x;

this->y=y;

}

Specificul acestui constructor constă în faptul că numele parametrilor fictivi x şi y coincid cu numele membrilor clasei x şi y. Pentru a face delimitare între parametrii fictivi şi membrii clasei, numele membrilor clasei sunt precedate de pointerul this conectat prin operatorul ->.

Orice membru al clasei curente poate fi accesat prin intermediul pointerului this, precedând numele membrului cu pointerul this. Dacă nu sunt confuzii de nume, ca în cazul constructorului precedent, pointerul this poate fi omis fără careva pierderi de sens.

Totuşi, sunt situaţii când nu poate fi ocolită utilizarea pointerului this. De exemplu, pointerul this va fi utilizat dacă funcţia membru returnează adresa obiectului curent, cum este arătat în descrierea ce urmează:

nume_cl *nume_fm(lista_pf)

{

. . .

return this;

}

Va fi o situaţie similară dacă funcţia membru returnează obiectul curent, ceea ce este reprezentat în următoarea descriere:

nume_cl nume_fm(lista_pf)

{

. . .

return *this;

}

Dacă funcţia membru utilizează obiectul curent pentru anumite procesări, va fi, de asemenea, necesară utilizarea pointerului this:

tip_r nume_fm(lista_pf)

{

. . .

nume_cl ob = *this;

. . .

}

În procesul elaborării de algoritmi pot fi utilizate şi referinţe la obiecte. Definirea unei referinţe de tip obiect poate fi realizată astfel:

nume_cl &nume_vr = nume_ob;

unde nume_vr este numele variabilei de tip referinţă, iar nume_ob este denumirea obiectului în baza căruia este definită referinţa.

Referinţele la obiecte se folosesc des anume în calitate de parametri fictivi ai unor funcţii şi în calitate de valoare returnată a funcţiei. Referinţele la obiecte ne dau eficienţă din punct de vedere a utilizării memoriei. Referinţele se utilizează des în constructorii de copiere, în procesul de supraîncărcare a operatorilor, mai ales a celor de intrare/ieşire.

O noţiune nouă în contextul claselor sunt pointerii la membrii clasei. Ei nu sunt nişte adrese, ci mai degrabă nişte deplasamente în raport cu originea clasei. Fiindcă membrii clasei sunt de două tipuri, adică date membre şi funcţii membre, de două tipuri vor fi şi pointerii la membrii clasei.

  1. Pointeri la date membre.

Un pointer la date membre poate fi definit utilizând următoarea descriere:

tip nume_cl::*nume_pmd;

unde nume_pmd este identificatorul care reprezintă numele pointerului, nume_cl este numele clasei a cărui membru va fi legat cu pointerul, iar tip descrie tipul pointerului. Tipul pointerului coincide cu tipul membrului cu care va fi legat. A defini un pointer nu înseamnă a-l şi iniţializa. Pentru iniţializare este utilizat operatorul & în felul următor:

nume_pmd=&nume_cl::nume_md;

Pentru a accesa un câmp al unui obiect folosind pointer la membrul clasei este utilizat operatorul .*

nume_ob.*nume_pmd

unde nume_ob este obiectul al cărui membru este accesat, iar nume_pmd este pointer la membrul dat al clasei.

Dacă este utilizat nu numele unui obiect, ci un pointer la obiect, atunci operatorul .* va fi înlocuit cu operatorul ->* după cum urmează:

nume_po->*nume_pmd

Pentru a exemplifica utilizarea pointerilor la membrii clasei de tip date, va fi rescrisă clasa punct, declarând toţi membrii publici:

class punct

{

public:

int x;

int y;

punct(int x=0, int y=0);

void afisare();

void stingere();

void miscare(int xn, int yn);

};

Acum poate fi definit un pointer la datele membre x şi y ai clasei punct:

int punct::*pxy; //definire pointer la membru

pxy=&punct::x; //initializare

punct p(100,200),*pp; //definire obiect, pointer

pp = &p;

p.*pxy=150; //schimbare valoarea din 100 in 150

pxy=&punct::y; //schimbare valoare pointer

pp->*pxy=150; //schimbare valoarea din 200 in 150

  1. Pointeri la funcţii membre.

Întrucât descrierea unei funcţii constă din mai multe elemente, tot aşa şi definirea pointerilor la funcţii membre va fi diferită de cazul datelor membre, realizându-se astfel:

tip (nume_cl::*nume_pmf)([tip1 pf1[, tip2 pf2

[, …,tipk pfk]...]]);

unde nume_pmf este numele pointerului la funcţii membre, după el urmând descrierea parametrilor fictivi.

Iniţializarea pointerului la funcţii membre este similară cu iniţializarea pointerului la date membre:

nume_pmf=&nume_cl::nume_mf;

unde nume_mf este numele funcţiei membre.

Apelarea funcţiei membre prin intermediul pointerului la funcţia membră este realizată prin intermediul operatorului .* astfel

(nume_ob.*nume_pmf)(pr1, pr2, …,prk)

Exemplificarea va fi făcută tot în baza clasei punct.

void (punct::*pas)();

pas=&punct::afisare;

punct p(100,200);

(p.*pas)(); //apelarea functiei afisare

pas=&punct::stingere;

punct p(100,200);

(pas.*pas)(); //apelarea functiei stingere

Dacă mai multe funcţii membre au acelaşi prototip, atunci putem avea un pointer de tip „pointer la membrul clasei” care poate fi folosit cu toate funcţiile respective.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]