This shows you the differences between two versions of the page.
poo-is-ab:laboratoare:03 [2024/09/28 22:06] razvan.cristea0106 |
poo-is-ab:laboratoare:03 [2025/01/19 22:28] (current) razvan.cristea0106 |
||
---|---|---|---|
Line 17: | Line 17: | ||
În cadrul laboratorului anterior ne-am axat pe scrierea corectă a unei clase respectând **principiul încapsulării datelor**. În acest laborator atenția ne este îndreptată tot asupra modului în care o clasă este scrisă în așa fel încât să respecte principiile **OOP**, dar vom introduce noi tipuri de membri și de asemenea vom prezenta câteva din funcțiile membre care sunt **specifice** clasei. | În cadrul laboratorului anterior ne-am axat pe scrierea corectă a unei clase respectând **principiul încapsulării datelor**. În acest laborator atenția ne este îndreptată tot asupra modului în care o clasă este scrisă în așa fel încât să respecte principiile **OOP**, dar vom introduce noi tipuri de membri și de asemenea vom prezenta câteva din funcțiile membre care sunt **specifice** clasei. | ||
- | Pentru a înțelege despre ce vom vorbi pe parcursul laboratorului propunem ca și clasă pentru exemplificare clasa **Conifer** căreia îi vom adăuga noutățile rând pe rând. De asemenea, pentru această clasă vom pune la dispoziție **constructori** și accesori de tip **get** și **set**. | + | Pentru a înțelege despre ce vom vorbi pe parcursul laboratorului propunem ca și exemplu clasa **Conifer**, căreia îi vom adăuga noutățile rând pe rând. De asemenea, pentru această clasă vom pune la dispoziție **constructori** și accesori de tip **get** și **set** pentru fiecare membru. |
<code cpp> | <code cpp> | ||
Line 158: | Line 158: | ||
Pentru a fi și mai clar ce s-a întâmplat pe linia ''char* sir2 = sir1;'' vom ilustra grafic după cum urmează. | Pentru a fi și mai clar ce s-a întâmplat pe linia ''char* sir2 = sir1;'' vom ilustra grafic după cum urmează. | ||
- | {{ :poo-is-ab:laboratoare:shallow_copy.jpg |}} | + | {{ :poo-is-ab:laboratoare:shallow_copy.jpg?direct&600 |}} |
După cum se poate observa în imaginea de mai sus variabila **sir1** conține adresa **primului** element din vectorul de caractere (adresa caracterului 'I'). Când am facut atribuirea ''char* sir2 = sir1;'' am făcut ca pointerul **sir2** să pointeze către adresa de pointare a lui **sir1**. Astfel orice modificare pe care o facem asupra lui **sir1** se va răsfrânge asupra lui **sir2**, valabil și invers după cum se poate observa pe linia unde am schimbat valoarea primului caracter prin intermediul lui **sir2**. Cu alte cuvinte pointerii **sir1** și respectiv **sir2** partajează aceeași zonă de memorie. | După cum se poate observa în imaginea de mai sus variabila **sir1** conține adresa **primului** element din vectorul de caractere (adresa caracterului 'I'). Când am facut atribuirea ''char* sir2 = sir1;'' am făcut ca pointerul **sir2** să pointeze către adresa de pointare a lui **sir1**. Astfel orice modificare pe care o facem asupra lui **sir1** se va răsfrânge asupra lui **sir2**, valabil și invers după cum se poate observa pe linia unde am schimbat valoarea primului caracter prin intermediul lui **sir2**. Cu alte cuvinte pointerii **sir1** și respectiv **sir2** partajează aceeași zonă de memorie. | ||
Line 196: | Line 196: | ||
Iar ca și ilustrare grafică lucrurile arată în felul următor. | Iar ca și ilustrare grafică lucrurile arată în felul următor. | ||
- | {{ :poo-is-ab:laboratoare:deep_copy.jpg |}} | + | {{ :poo-is-ab:laboratoare:deep_copy.jpg?direct&600 |}} |
Pașii pe care i-am aplicat pentru a realiza **deep copy** au fost alocarea unui nou spațiu de memorie pentru **sir2** și copierea caracterelor lui **sir1** în **sir2** cu ajutorul funcției **strcpy**. Astfel atunci când am schimbat valoarea primului caracter din **sir2** modificarea nu s-a propagat și pe **sir1**, deoarece acum cei doi pointeri **nu** mai partajează aceeași zonă de memorie. | Pașii pe care i-am aplicat pentru a realiza **deep copy** au fost alocarea unui nou spațiu de memorie pentru **sir2** și copierea caracterelor lui **sir1** în **sir2** cu ajutorul funcției **strcpy**. Astfel atunci când am schimbat valoarea primului caracter din **sir2** modificarea nu s-a propagat și pe **sir1**, deoarece acum cei doi pointeri **nu** mai partajează aceeași zonă de memorie. | ||
Line 258: | Line 258: | ||
==== Destructorul ==== | ==== Destructorul ==== | ||
- | Așa cum îi spune și numele această **metodă** se ocupă de distrugerea obiectelor atunci când aceastora li se încheie durata de viață în program. La fel ca și **constructorii**, **destructorul** are o serie de trăsături ce îl face ușor de recunoscut într-o clasă precum: | + | Așa cum îi spune și numele această **metodă** se ocupă de distrugerea obiectelor atunci când aceastora li se încheie durata de viață în program. La fel ca și **constructorii**, **destructorul** are o serie de trăsături ce îl fac ușor de recunoscut într-o clasă după cum urmează: |
- nu are **tip returnat** nici măcar **void** | - nu are **tip returnat** nici măcar **void** | ||
- denumirea destructorului este **aceeeași** cu a clasei din care face parte, dar pentru a putea fi diferențiat de constructori se pune înaintea lui operatorul **"~"** | - denumirea destructorului este **aceeeași** cu a clasei din care face parte, dar pentru a putea fi diferențiat de constructori se pune înaintea lui operatorul **"~"** | ||
Line 307: | Line 307: | ||
</code> | </code> | ||
- | <note warning>Destructorul, fie că vorbim de cel generat de compilator sau de cel implementat de programator, **nu** se apelează **explicit** de către programator. Acest lucru va fi realizat de către **compilator automat** atunci când durata de viată a obiectului se încheie.</note> | + | <note warning>Destructorul, fie că vorbim de cel generat de compilator sau de cel implementat de programator, **nu** se apelează **explicit** de către programator. Acest lucru va fi realizat de către **compilator în mod automat** atunci când durata de viață a obiectului se încheie.</note> |
==== Constructorul de copiere ==== | ==== Constructorul de copiere ==== | ||
- | Constructorul de copiere (**Copy Constructor**) este o metodă specială a clasei, deoarece cu ajutorul lui putem copia conținutul unui obiect existent într-unul nou. Acest constructor este **unic** în clasă și respectă toate caracteristicile unui constructor obișnuit. Dacă nu este declarat și implementat de către programator, compilatorul se va ocupa el de generarea unui **copy constructor default**. | + | Constructorul de copiere (**Copy Constructor**) este o metodă specială a clasei, deoarece cu ajutorul lui putem copia conținutul unui obiect existent într-unul nou. Acest constructor este **unic** în clasă și respectă toate caracteristicile unui constructor obișnuit. Dacă **nu** este declarat și implementat de către programator, compilatorul se va ocupa el de generarea unui **copy constructor default**. |
<note warning>**Constructorul de copiere** generat de către compilator face **shallow copy**, iar dacă în clasă avem **cel puțin** un membru de tip **pointer alocat dinamic**, programatorul va trebui să ofere o implementare pentru acest tip de constructor care să facă o **copiere profundă (deep copy)**.</note> | <note warning>**Constructorul de copiere** generat de către compilator face **shallow copy**, iar dacă în clasă avem **cel puțin** un membru de tip **pointer alocat dinamic**, programatorul va trebui să ofere o implementare pentru acest tip de constructor care să facă o **copiere profundă (deep copy)**.</note> | ||
Line 363: | Line 363: | ||
</code> | </code> | ||
- | Astfel **constructorul de copiere** este acum specializat pentru crearea de **clone reale** pentru obiecte de tip **Conifer**. | + | Astfel **constructorul de copiere** este acum specializat pentru crearea de **clone reale** pentru obiectele de tip **Conifer**. |
==== Operatorul de atribuire ==== | ==== Operatorul de atribuire ==== | ||
Line 431: | Line 431: | ||
</code> | </code> | ||
- | <note warning>Implementarea anterioară a **operatorului de asignare** are o vulnerabilitate și anume **nu** tratează situația în care obiectul este atribuit **sieși**. Astfel pot apărea probleme cu privire la eliberarea memoriei sau coruperea datelor. Pentru a putea avea o asignare sigură trebuie să facem verificarea de **auto-asignare** care garantează că **operatorul =** va funcționa corect în toate situațiile.</note> | + | <note warning>Implementarea anterioară a **operatorului de asignare** are o vulnerabilitate și anume **nu** tratează situația în care obiectul este atribuit **sieși**. Astfel pot apărea probleme cu privire la eliberarea memoriei sau coruperea datelor. Pentru a putea avea o asignare sigură **trebuie** să facem verificarea de **auto-asignare** care garantează că **operatorul =** va funcționa corect în toate situațiile.</note> |
<code cpp> | <code cpp> | ||
Line 568: | Line 568: | ||
</code> | </code> | ||
- | Deși membrul static **numarConifere** este privat noi ne propunem cumva să avem accest la el. Soluția este să implementăm un **getter** pentru acesta. | + | Deși membrul static **numarConifere** este privat noi totuși ne propunem să avem cumva acces la el. Soluția cea mai simplă este să implementăm un **getter** pentru acesta, astfel încât să respectăm și **principiul încapsulării datelor**. |
=== Funcții statice === | === Funcții statice === | ||
- | La fel ca variabia statică **funcția statică** există pe întreaga durată de viață a programului. În **POO** o **funcție statică** la nivel de clasă este foarte asemănătoare cu o **metodă**. Totuși o metodă primește ca parametru pe prima poziție în lista de parametri **pointerul this**. În C++ chiar dacă acest parametru **nu** este vizibil în lista de parametri, la compilare există (acest procedeu ne este ascuns, dar în spate compilatorul adaugă acest parametru la toate **funcțiile membre**). | + | La fel ca variabila statică, **funcția statică** există pe întreaga durată de viață a programului. În **POO** o **funcție statică** la nivel de clasă este foarte asemănătoare cu o **metodă**. Totuși o metodă primește ca parametru pe prima poziție în lista de parametri **pointerul this**. În C++ chiar dacă acest parametru **nu** este vizibil în lista de parametri, la compilare există (acest procedeu ne este ascuns, dar în spate compilatorul adaugă acest parametru la toate **funcțiile membre**). |
- | <note important>**O **funcție statică** la nivel de clasă **nu** primește **pointerul this** ca parametru. Îi spunem **funcție statică** și **nu** metodă statică din acest motiv.**</note> | + | <note important>**O funcție statică** la nivel de clasă **nu** primește **pointerul this** ca parametru. Îi spunem **funcție statică** și nu **metodă statică** din acest motiv.</note> |
În exemplul de mai jos am declarat un **getter** pentru a obține numărul de conifere. | În exemplul de mai jos am declarat un **getter** pentru a obține numărul de conifere. | ||
Line 618: | Line 618: | ||
</code> | </code> | ||
- | Această funcție statică poate fi apelată fie prin **numele clasei** fie prin intermediul unui **obiect** după cum urmează. | + | Această **funcție statică** poate fi apelată fie prin **numele clasei** fie prin intermediul unui **obiect** după cum urmează în codul sursă de mai jos. |
<code cpp> | <code cpp> | ||
Line 642: | Line 642: | ||
==== ==== | ==== ==== | ||
- | Astfel am putut observa și înțelege cum putem folosi membrii statici și fucțiile statice la nivel de clasă. | + | Astfel am putut observa și înțelege cum putem folosi **membrii statici** și **fucțiile statice** la nivel de clasă. |
==== Membri constanți ==== | ==== Membri constanți ==== | ||
- | Evident într-o clasă pot exista și membri constanți pentru a permite distincția între obiecte. De obicei un membru constant are un scop bine definit spre exemplu poate fi un cod unic de identificare a obiectului. În lumea reală o persoană este identificată unic după CNP-ul acesteia care știm foarte bine că este unic. | + | Evident într-o clasă pot exista și **membri constanți** pentru a permite **distincția** între obiecte. De obicei un membru constant are un scop bine definit spre exemplu poate fi un cod unic de identificare a obiectului. În lumea reală o persoană este identificată unic după CNP-ul (codul numeric personal) acesteia care știm foarte bine că nu mai poate fi modificat sau atribuit altei persoane. |
- | În cazul exemplului nostru putem să asigurăm unicitatea membrilor constanți folosindu-ne de membrul static. Să urmărim modul în care am declarat în clasa **Conifer** un atribut constant. | + | În cazul exemplului nostru putem să asigurăm **unicitatea** membrilor constanți folosindu-ne de **membrul static**. Să urmărim modul în care am declarat în clasa **Conifer** un **atribut constant** denumit **codUnic**. |
<code cpp> | <code cpp> | ||
Line 683: | Line 683: | ||
</code> | </code> | ||
- | <note warning>Un atribut constant **nu** poate fi inițializat în interiorul constructorilor, deoarece **obiectul există** și compilatorul i-a alocat deja o valoare default ce nu mai poate fi modificată. Prin urmare inițializarea membrilor constanți se face **obligatoriu** în **lista de inițializare a constructorului**. Această listă de inițializare îi spune compilatorului că înainte de crearea obiectului are de atribuit valori pentru anumiți membri.</note> | + | <note warning>Un atribut constant **nu** poate fi inițializat în **interiorul constructorilor**, deoarece **obiectul deja există** și în plus, compilatorul a alocat deja o valoare random pentru **membrul constant** ce **nu** mai poate fi modificată. Prin urmare **inițializarea membrilor constanți** se face **obligatoriu** în **lista de inițializare a constructorului**. Această listă de inițializare îi spune compilatorului că înainte de crearea efectivă a obiectului are de atribuit valori pentru anumiți membri.</note> |
În exemplul de cod de mai jos am pus în lumină modul în care este inițializat un membru constant. | În exemplul de cod de mai jos am pus în lumină modul în care este inițializat un membru constant. |