This shows you the differences between two versions of the page.
poo-is-ab:laboratoare:01 [2024/09/18 21:00] razvan.cristea0106 [Asemănări C/C++] |
poo-is-ab:laboratoare:01 [2025/01/19 22:27] (current) razvan.cristea0106 |
||
---|---|---|---|
Line 12: | Line 12: | ||
==== Introducere ==== | ==== Introducere ==== | ||
- | În acest laborator vom face o scurtă recapitulare a noțiunilor de bază învățate în limbajul C și vom vedea care sunt diferențele în linii mari dintre limbajele C și C++. Noi vom scrie programe în C++ pe întreg parcursul acestui semestru, deoarece vom învăța o nouă paradigmă de programare și anume cea __**Orientată Obiect (OO)**__. | + | În acest laborator vom face o scurtă recapitulare a noțiunilor de bază învățate în anul întâi la disciplina **Programarea Calculatoarelor și Limbaje de Programare (PCLP)** și vom vedea care sunt diferențele în linii mari dintre limbajele C și C++. Noi vom scrie programe în C++ pe întreg parcursul acestui semestru, deoarece vom învăța o nouă paradigmă de programare și anume cea **Orientată Obiect (OO)**. |
==== Scurt Istoric ==== | ==== Scurt Istoric ==== | ||
Line 43: | Line 43: | ||
=== Operații cu variabile === | === Operații cu variabile === | ||
- | La fel ca în C, în C++ putem face aceleași operații cu tipurile de date existente precum adunări, scăderi, înmulțiri, împărțiri etc. Operatorii existeți în C++ sunt identici cu cei din C. | + | La fel ca în C, în C++ putem face aceleași operații cu tipurile de date existente precum adunări, scăderi, înmulțiri, împărțiri etc. Operatorii existenți în C++ sunt **identici** cu cei din C. |
<code cpp> | <code cpp> | ||
Line 62: | Line 62: | ||
</code> | </code> | ||
- | <note important>Se observă că pe linia unde **a** se împarte la **b** este folosit un operator special numit operatorul de **cast explicit** care are menirea să convertească rezultatul împărtirii într-unul de tip float. Dacă variabila **a** era declarată ca //float// atunci nu mai era necesară folosirea operatorului de cast, rezultatul fiind calculat corect cu virgulă.</note> | + | <note important>Se observă că pe linia unde valoarea variabilei **a** se împarte la cea a variabilei **b** este folosit un operator special numit operatorul de **cast explicit** care are menirea să convertească rezultatul împărțirii într-unul de tip **float**. Dacă variabila **a** era declarată ca **float** atunci **nu** mai era necesară folosirea **operatorului de cast**, rezultatul fiind calculat corect cu virgulă.</note> |
=== Instrucțiunile decizionale și cele repetitive === | === Instrucțiunile decizionale și cele repetitive === | ||
- | În C++ regăsim toate instrucțiunile de bază ale limbajului C. Ca instrucțiuni decizionale amintim **if else** și **switch**, iar ca instrucțiuni repetitive menționam buclele **for**, **while** și **do while** | + | În C++ regăsim toate instrucțiunile de bază ale limbajului C. Ca **instrucțiuni decizionale** amintim **if else** și **switch**, iar ca **instrucțiuni repetitive** menționăm buclele **for**, **while** și **do while** |
== Instrucțiuni decizionale == | == Instrucțiuni decizionale == | ||
- | Forma generală a instrucțiunii if-else | + | Forma generală a instrucțiunii **if-else** |
<code cpp> | <code cpp> | ||
Line 113: | Line 113: | ||
</code> | </code> | ||
- | Instrucțiunea switch poate fi o alternativă mult mai elegantă atunci când vrem să înlocuim o secvență de instrucțiuni de forma if -> else if -> ... -> else. Deși pare o instrucțiune "demodată" aceasta este extrem de eficientă și de utilă atunci când ne dorim un cod rapid și ușor de citit. | + | Instrucțiunea **switch** poate fi o alternativă mult mai elegantă atunci când vrem să înlocuim o secvență de instrucțiuni de forma **if -> else if -> ... -> else**. Deși pare o instrucțiune "demodată" aceasta este extrem de eficientă și de utilă atunci când ne dorim un cod rapid și ușor de citit. |
Forma generală a instrucțiunii swicth | Forma generală a instrucțiunii swicth | ||
Line 172: | Line 172: | ||
== Instrucțiuni repetitive == | == Instrucțiuni repetitive == | ||
- | Instrucțiunea for este prima instrucțiune repetitivă pe care o amintim. Ați folosit-o destul de mult la parcurgerea șirurilor de caractere, a vectorilor cât și a altor structuri de date învățate pe parcursul semestrului trecut la disciplina Proiectarea Algoritmilor. | + | Instrucțiunea **for** este prima instrucțiune repetitivă pe care o amintim. Ați folosit-o destul de mult la parcurgerea șirurilor de caractere, a vectorilor cât și a altor structuri de date învățate pe parcursul semestrului trecut la disciplina **Proiectarea Algoritmilor**. |
Forma generală a instrucțiunii for | Forma generală a instrucțiunii for | ||
Line 183: | Line 183: | ||
</code> | </code> | ||
- | Pasul indică la ce iterație ne aflăm în bucla for și dacă este satisfăcută condiția de continuare. Putem lua ca exemplu de cod incrementarea cu 5 a valorilor unui vector de numere întregi. | + | Pasul indică la ce iterație ne aflăm în bucla **for** și dacă este satisfăcută condiția de continuare. Putem lua ca exemplu de cod incrementarea cu 5 a valorilor unui vector de numere întregi. |
<code cpp> | <code cpp> | ||
Line 202: | Line 202: | ||
Se poate observa că a fost declarat un vector care are capacitatea de 10 elemente din care am ocupat doar primele 8 poziții. Vom reaminti mai târziu de ce această variantă nu este chiar cea mai potrivită atunci când vom menționa despre alocarea dinamică a memoriei. | Se poate observa că a fost declarat un vector care are capacitatea de 10 elemente din care am ocupat doar primele 8 poziții. Vom reaminti mai târziu de ce această variantă nu este chiar cea mai potrivită atunci când vom menționa despre alocarea dinamică a memoriei. | ||
- | Instrucțiunea while oferă funcționalități similare cu cele ale instrucțiunii for, însă diferă prin faptul că utilizează un singur parametru, care reprezintă o expresie logică evaluată la fiecare iterație a buclei. Această expresie servește drept condiție de continuare a buclei, similar cu condiția de continuare din bucla for. Bucla while va continua să se execute atât timp cât această condiție este adevărată. | + | Instrucțiunea **while** oferă funcționalități similare cu cele ale instrucțiunii for, însă diferă prin faptul că utilizează un singur parametru, care reprezintă o **expresie logică** evaluată la fiecare iterație a buclei. Această expresie servește drept condiție de continuare a buclei, similar cu condiția de continuare din bucla for. Bucla **while** va continua să se execute atât timp cât această condiție este **adevărată**. |
Forma generală a buclei while | Forma generală a buclei while | ||
Line 213: | Line 213: | ||
</code> | </code> | ||
- | Vom lua drept exemplu de utilizare pe cel de la bucla for pentru a vedea cum putem scrie același program dar cu bucla while. | + | Vom lua drept exemplu de utilizare pe cel de la bucla for pentru a vedea cum putem scrie același program, dar cu bucla while. |
<code cpp> | <code cpp> | ||
Line 233: | Line 233: | ||
</code> | </code> | ||
- | Instrucțiunea do-while este o variantă a buclei while, cu o diferență esențială: în bucla do-while, condiția de verificare este evaluată după ce codul din interiorul buclei a fost executat, nu înainte. Aceasta înseamnă că blocul de cod din bucla do-while va fi executat cel puțin o dată, indiferent dacă condiția este adevărată sau falsă. | + | Instrucțiunea **do-while** este o variantă a buclei **while**, cu o diferență esențială: în bucla **do-while**, condiția de verificare este evaluată **după** ce codul din interiorul buclei a fost executat, **nu** înainte. Aceasta înseamnă că blocul de cod din bucla **do-while** va fi executat cel puțin **o dată**, indiferent dacă condiția este adevărată sau falsă. |
Forma generală a instrucțiunii do-while | Forma generală a instrucțiunii do-while | ||
Line 244: | Line 244: | ||
</code> | </code> | ||
- | Iar ca și exemplu de cod vom rescrie exemplul de la bucla while astfel: | + | Iar ca și exemplu de cod îl vom rescrie pe cel de la bucla while. |
<code cpp> | <code cpp> | ||
Line 267: | Line 267: | ||
<note>Puteți folosi oricare din cele 3 instrucțiuni repetitive, important este ca efectul codului scris de voi să fie cel dorit și să fiți atenți la cum puneți condițiile.</note> | <note>Puteți folosi oricare din cele 3 instrucțiuni repetitive, important este ca efectul codului scris de voi să fie cel dorit și să fiți atenți la cum puneți condițiile.</note> | ||
- | <note warning>Trebuie menționat faptul că șansele de a intra într-o buclă infinită sunt mai mari atunci când folosim instrucțiunea while, deoarece putem omite să modificăm pasul la fiecare iterație. Instrucțiunea for este mai sigură din acest punct de vedere, dar trebuie menționat că putem avea un infinite loop chiar și dacă folosim cum nu trebuie for-ul. | + | <note warning>Trebuie menționat faptul că șansele de a intra într-o **buclă infinită** sunt mai mari atunci când folosim instrucțiunea **while**, deoarece putem omite să modificăm **pasul** la fiecare iterație. Instrucțiunea **for** este mai sigură din acest punct de vedere, dar trebuie menționat că putem avea un **infinite loop** chiar și dacă folosim cum nu trebuie for-ul. |
</note> | </note> | ||
- | Pentru a avea un program care rulează la infinit utilizând bucla for putem să nu specificăm nimic între parantezele rotunde ale instrucțiunii. | + | Pentru a avea un program care rulează la infinit utilizând bucla **for** putem să nu specificăm nimic între parantezele rotunde ale instrucțiunii. |
<code cpp> | <code cpp> | ||
Line 286: | Line 286: | ||
</code> | </code> | ||
- | Astfel programul nu va mai ieși din bucla for și va multiplica valoarea lui x de 10 ori la infinit. | + | Astfel programul nu va mai ieși din bucla **for** și va multiplica valoarea lui x de 10 ori la infinit. |
=== Utilizarea pointerilor === | === Utilizarea pointerilor === | ||
- | C++, la fel ca C-ul, are tipuri de date speciale denumite pointeri, care sunt utile atunci când vrem să modificăm o valoare de la o anumită adresă din memorie. | + | C++, la fel ca C-ul, are tipuri de date speciale denumite **pointeri**, care sunt utile atunci când vrem să modificăm o valoare de la o anumită adresă din memorie. |
- | Un pointer este un tip de date care stochează o adresă din memorie la un anumit moment de timp în program. Să luam spre exemplu următoarea secvență de cod. | + | **Un pointer** este un tip de date care stochează **o adresă** din memorie la un anumit moment de timp în program. Să luam spre exemplu următoarea secvență de cod. |
<code cpp> | <code cpp> | ||
Line 304: | Line 304: | ||
</code> | </code> | ||
- | Se poate observa că avem o varibilă de tip int care a fost inițializată cu valoarea 10 și un pointer la int care a fost inițializat cu adresa lui x. Într-o ilustrare simplificată putem înțelege efectiv ce s-a întâmplat pe lina a doua de cod. | + | Se poate observa că avem o varibilă de **tip int** care a fost inițializată cu valoarea 10 și un **pointer la int** care a fost inițializat cu adresa lui x. Într-o ilustrare simplificată putem înțelege efectiv ce s-a întâmplat pe lina a doua de cod. |
{{ :poo-is-ab:laboratoare:pointer_logic.jpg |}} | {{ :poo-is-ab:laboratoare:pointer_logic.jpg |}} | ||
- | Pe linia ''int* ptr = &x;'', ceea ce se întâmplă este faptul că pointerul ptr este asociat cu adresa variabilei x (apariția săgeții de la ptr la adresa lui x din desen). Practic, ptr devine un „arătător” către locația din memorie unde este stocată variabila x. Este important să înțelegeți că un pointer nu este altceva decât o variabilă specială care conține adresa de memorie a unei alte variabile. Astfel, ptr indică către adresa lui x și poate fi folosit pentru a accesa sau modifica valoarea lui x prin intermediul acestei adrese. | + | Pe linia ''int* ptr = &x;'', ceea ce se întâmplă este faptul că pointerul **ptr** este asociat cu adresa variabilei **x** (apariția săgeții de la **ptr** la **adresa** lui **x** din desen). Practic, **ptr** devine un **"arătător"** către locația din memorie unde este stocată variabila **x**. Este important să înțelegeți că un pointer nu este altceva decât o variabilă specială care conține adresa de memorie a unei alte variabile. Astfel, **ptr** indică către adresa lui **x** și poate fi folosit pentru a accesa sau modifica valoarea lui **x** prin intermediul acestei adrese. |
- | <note warning>Un pointer poate pointa doar către **o singură** adresă, situația de mai jos fiind taxată cu o eroare de compilare.</note> | + | <note warning>**Un pointer** poate pointa doar către **o singură** adresă, situația de mai jos fiind taxată cu o eroare de compilare.</note> |
<code cpp> | <code cpp> | ||
Line 328: | Line 328: | ||
{{ :poo-is-ab:laboratoare:incorrect_pointer_logic.jpg |}} | {{ :poo-is-ab:laboratoare:incorrect_pointer_logic.jpg |}} | ||
- | Trebuie însă înțeles faptul că această situație nu este permisă și nu are sens. E ca și cum ați vrea să arătați simultan cu același deget spre două persoane diferite ceea ce este fizic imposibil. | + | Trebuie însă înțeles faptul că această situație **nu** este permisă și **nu** are sens. E ca și cum ați vrea să arătați **simultan** cu **același deget** spre două persoane **diferite** ceea ce este fizic **imposibil**. |
- | În schimb situația următoare este permisă și este complet validă. | + | În schimb situația următoare este permisă și complet validă. |
<code cpp> | <code cpp> | ||
Line 345: | Line 345: | ||
</code> | </code> | ||
- | Dacă un pointer arată către adresa de memorie a unei variabile, cum putem accesa totuși valoarea de la acea adresă spre care pointează pointerul nostru? Răspunsul este unul foarte simplu și anume printr-o operație specifică pointerilor cunoscută sub numele de __//**dereferențiere**//__. | + | Dacă un pointer arată către adresa de memorie a unei variabile, cum putem accesa totuși valoarea de la acea adresă spre care pointează pointerul nostru? Răspunsul este unul foarte simplu și anume printr-o operație specifică pointerilor cunoscută sub numele de __**dereferențiere**__. |
<code cpp> | <code cpp> | ||
Line 359: | Line 359: | ||
</code> | </code> | ||
- | <note important>Dereferențierea unui pointer în limbaj natural poate fi privită ca obținerea/vizualizarea valorii existente la o anumită adresă. Aceasta se realizează cu ajutorul operatorului de dereferențiere "*" plasat înaintea variabilei de tip pointer. A nu se confunda cu steluța de la declararea unui pointer și cu operatorul aritmetic de înmulțire a două numere.</note> | + | <note important>**Dereferențierea** unui pointer în limbaj natural poate fi privită ca **obținerea/vizualizarea** valorii existente la o anumită adresă din memorie. Aceasta se realizează cu ajutorul operatorului de **dereferențiere** **"*"** plasat înaintea variabilei de tip pointer. A **nu** se confunda cu steluța de la declararea unui pointer sau cu operatorul aritmetic de înmulțire a două numere.</note> |
- | Orice modificare pe care o suferă valoarea lui x va fi vizibilă și prin intermediul dereferențierii pointerului ptr și invers. Adică orice modificare a valorii de la adresa de pointare a lui ptr se va răsfrânge asupra lui x. Ca și mic exercițiu aflați valoarea lui x de la finalul programului următor. | + | Orice modificare pe care o suferă valoarea lui **x** va fi vizibilă și prin intermediul dereferențierii pointerului **ptr** și invers. Adică orice modificare a valorii de la adresa de pointare a lui **ptr** se va răsfrânge asupra lui **x**. Ca și mic exercițiu aflați valoarea lui **x** de la finalul programului următor. |
<code cpp> | <code cpp> | ||
Line 380: | Line 380: | ||
</code> | </code> | ||
- | <note warning>Pointerii au ca și valoare default o adresă alocată random de compilator. NULL este un pointer special care conține o adresă vidă formată doar din zerouri. Practic NULL e echivalentul lui nimic (o adresă care este mereu goală) și de obicei când declarăm un pointer pe care nu îl utilizăm imediat este bine să îl inițializăm cu NULL.</note> | + | <note warning>Pointerii au ca și valoare **default** o adresă alocată **random** de compilator. **NULL** este un pointer special care conține o **adresă vidă** formată doar din zerouri. Practic **NULL** e echivalentul lui nimic (o adresă care este mereu goală) și de obicei când declarăm un pointer pe care nu îl utilizăm imediat este bine să îl inițializăm cu **NULL**.</note> |
<code cpp> | <code cpp> | ||
Line 396: | Line 396: | ||
=== Definirea funcțiilor de către programator === | === Definirea funcțiilor de către programator === | ||
- | C++, la fel ca C-ul, suportă paradigma procedurală care presupune organizarea codului sursă în suprograme denumite funcții. Funcția după cum îi spune numele trebuie să se ocupe de un anumit lucru în program. | + | C++, la fel ca C-ul, suportă paradigma procedurală care presupune organizarea codului sursă în **subprograme** denumite și **funcții**. **Funcția** după cum îi spune și numele trebuie să se ocupe de un anumit lucru în program. |
- | <note important>Ca și regulă de bună practică o funcție **nu trebuie** să facă mai mult de un singur lucru pe care este menită să îl facă.</note> | + | <note important>Ca și regulă de bună practică o funcție **nu trebuie** să facă mai mult de **un singur lucru** pe care este menită să îl facă.</note> |
Rețeta cea mai simplă pentru declararea unei funcții este: | Rețeta cea mai simplă pentru declararea unei funcții este: | ||
- | - tipul de date returnat | + | - **tipul de date returnat** |
- | - numele funcției | + | - **numele funcției** |
- | - lista de parametri | + | - **lista de parametri** |
- | <note>A doua componentă împreună cu cea de a treia sunt cunoscute și sub denumirea de //**semnătură a funcției**//.</note> | + | <note>A doua componentă împreună cu cea de a treia sunt cunoscute și sub denumirea de __**semnătură a funcției**__.</note> |
Dacă îmbinăm cele 3 componente o funcție în pseudocod poate arăta așa -> return_type name(parameters) | Dacă îmbinăm cele 3 componente o funcție în pseudocod poate arăta așa -> return_type name(parameters) | ||
Line 420: | Line 420: | ||
</code> | </code> | ||
- | Implementarea funcțiilor se face în corpul acestora care este reprezentat de parantezele acolade. Funcțiile pot fi folosite prin apelare fie în funcția main fie în alte funcții după nevoie. Să urmărim exemplul de cod următor. | + | Implementarea funcțiilor se face în corpul acestora care este reprezentat de parantezele acolade. Funcțiile pot fi folosite prin apelare fie în funcția **main** fie în alte funcții după nevoie. Să urmărim exemplul de cod următor. |
<code cpp> | <code cpp> | ||
Line 451: | Line 451: | ||
__**Transmiterea parametrilor prin valoare**__ | __**Transmiterea parametrilor prin valoare**__ | ||
- | Acest mod de transmitere a parametrilor implică crearea unor copii ale valorilor originale ale parametrilor. Aceste copii sunt utilizate în interiorul funcției, iar orice modificare adusă acestora nu va afecta variabilele originale. După încheierea execuției funcției, aceste copii sunt eliminate din memorie, iar valorile inițiale rămân neschimbate. | + | Acest mod de transmitere a parametrilor implică crearea unor copii ale valorilor originale ale parametrilor. Aceste copii sunt utilizate în interiorul funcției, iar orice modificare adusă acestora **nu** va afecta variabilele originale. După încheierea execuției funcției, aceste copii sunt eliminate din memorie, iar valorile inițiale rămân **neschimbate**. |
Să analizăm exemplul de mai jos. | Să analizăm exemplul de mai jos. | ||
Line 472: | Line 472: | ||
</code> | </code> | ||
- | Variabilelor x și y li se face la fiecare câte o copie atunci când se apelează funcția **diferenta**. Nu doar că aceste copii sunt pierdute la încheierea execuției acestei funcții ba chiar și rezultatul întors de această funcție este transmis prin valoare. Deci în total avem 3 copii care s-au făcut în momentul în care linia de cod ''int dif = diferenta(x, y);'' a fost executată. | + | Variabilelor **x** și **y** li se face la fiecare câte o copie atunci când se apelează funcția **diferenta**. Nu doar că aceste copii sunt pierdute la încheierea execuției acestei funcții ba chiar și rezultatul întors de această funcție este transmis prin valoare. Deci în total avem 3 copii care s-au făcut în momentul în care linia de cod ''int dif = diferenta(x, y);'' a fost executată. |
Pentru a vedea efectiv dezavantajul transmiterii parametrilor prin valoare vom implementa o funcție care realizează interschimbarea valorilor a două variabile întregi. | Pentru a vedea efectiv dezavantajul transmiterii parametrilor prin valoare vom implementa o funcție care realizează interschimbarea valorilor a două variabile întregi. | ||
Line 495: | Line 495: | ||
</code> | </code> | ||
- | Deși parametrii **a** și **b** ai funcției **interschimbare** sunt copii ale variabilelor **x** și **y** din programul principal, aceștia sunt doar variabile locale care există doar în interiorul funcției. Astfel, valoarea lui **a** va deveni 21 și cea a lui b va fi 5, însă aceste modificări se aplică doar la nivel local. După terminarea execuției funcției, variabilele originale x și y nu vor fi afectate și vor păstra valorile inițiale, deoarece modificările din interiorul funcției nu se propagă în afara ei. | + | Deși parametrii **a** și **b** ai funcției **interschimbare** sunt copii ale variabilelor **x** și **y** din programul principal, aceștia sunt doar variabile locale care există doar în interiorul funcției. Astfel, valoarea lui **a** va deveni 21 și cea a lui **b** va fi 5, însă aceste modificări se aplică doar la nivel local. După terminarea execuției funcției, variabilele originale **x** și **y** nu vor fi afectate și vor păstra valorile inițiale, deoarece modificările din interiorul funcției nu se propagă în afara ei. |
Vom soluționa problema de mai sus utilizând un alt mod de transmitere a parametrilor și anume cea prin intermediul pointerilor. | Vom soluționa problema de mai sus utilizând un alt mod de transmitere a parametrilor și anume cea prin intermediul pointerilor. | ||
Line 501: | Line 501: | ||
__**Transmiterea parametrilor prin pointer**__ | __**Transmiterea parametrilor prin pointer**__ | ||
- | Acest tip de transmitere a parametrilor unei funcții este utilizat atunci când intenționăm ca modificările pe care le facem în interiorul funcției să persiste în afara ei. | + | Acest tip de transmitere a parametrilor unei funcții este utilizat atunci când intenționăm ca modificările pe care le facem în interiorul funcției să **persiste** în afara ei. |
Să reluăm exemplul prezentat pentru interschimbarea valorilor a două numere întregi. | Să reluăm exemplul prezentat pentru interschimbarea valorilor a două numere întregi. | ||
Line 529: | Line 529: | ||
</code> | </code> | ||
- | Deși parametrii **a** și **b** sunt tot variabile locale în funcția **interschimbare**, faptul că aceștia sunt pointeri schimbă modul în care subrogramul funcționează. În loc să fie doar copii ale valorilor lui **x** și **y**, acești pointeri conțin adresele de memorie ale variabilelor originale. Astfel, orice modificare efectuată asupra variabilelor accesate prin intermediul pointerilor va afecta direct valorile lui **x** și **y**. Acest mecanism permite funcției să modifice variabilele din programul principal, deoarece nu lucrează cu copii ale acestora, ci direct cu locațiile lor din memorie. | + | Deși parametrii **a** și **b** sunt tot variabile locale în funcția **interschimbare**, faptul că aceștia sunt pointeri schimbă modul în care subrogramul funcționează. În loc să fie doar copii ale valorilor lui **x** și **y**, acești pointeri conțin adresele de memorie ale variabilelor originale. Astfel, orice modificare efectuată asupra variabilelor accesate prin intermediul pointerilor va afecta **direct** valorile lui **x** și **y**. Acest mecanism permite funcției să modifice variabilele din programul principal, deoarece nu lucrează cu copii ale acestora, ci direct cu locațiile lor din memorie. |
- | <note>Putem folosi transmiterea prin pointer atunci când vrem sa facem anumite modificări asupra variabilelor, dar se poate utiliza și atunci când ne dorim doar să evităm copierea inutilă a valorilor, spre exemplu afisarea valorilor unui vector de numere întregi.</note> | + | <note>Putem folosi transmiterea prin pointer atunci când vrem sa facem anumite modificări asupra variabilelor, dar se poate utiliza și atunci când ne dorim doar să **evităm** copierea inutilă a valorilor, spre exemplu afisarea valorilor unui vector de numere întregi.</note> |
<code cpp> | <code cpp> | ||
Line 555: | Line 555: | ||
</code> | </code> | ||
- | Deși în acest caz nu modificăm numărul de elemente sau valorile din vector, am reușit să evităm două copieri inutile care ar fi avut loc dacă am fi transmis vectorul și numărul de elemente prin valoare. Atunci când un vector este transmis ca parametru, numele său este echivalent cu adresa primului element din memorie, ceea ce permite funcției să lucreze direct cu datele din vectorul original. Din acest motiv, nu este necesar să folosim operatorul **"&"** pentru a obține adresa vectorului, deoarece aceasta este implicit furnizată atunci când transmitem variabila **vec**. | + | Deși în acest caz **nu** modificăm numărul de elemente sau valorile din vector, am reușit să evităm două copieri inutile care ar fi avut loc dacă am fi transmis vectorul și numărul de elemente prin valoare. Atunci când un vector este transmis ca parametru, numele său este echivalent cu adresa primului element din memorie, ceea ce permite funcției să lucreze direct cu datele din vectorul original. Din acest motiv, nu este necesar să folosim operatorul **"&"** pentru a obține adresa vectorului, deoarece aceasta este implicit furnizată atunci când transmitem variabila **vec**. |
=== Pointeri la funcții === | === Pointeri la funcții === | ||
- | Pointerii la funcții (Function Pointers) sunt o caracteristică avansată a limbajelor de programare C și C++, care permit stocarea adreselor funcțiilor în variabile. Un pointer la funcție nu stochează o valoare, ci adresa din memorie unde este definită o funcție, oferind posibilitatea de a apela acea funcție indirect, prin intermediul pointerului. | + | Pointerii la funcții (**Function Pointers**) sunt o caracteristică avansată a limbajelor de programare C și C++, care permit stocarea adreselor funcțiilor în variabile. Un pointer la funcție nu stochează o valoare, ci adresa din memorie unde este definită o funcție, oferind posibilitatea de a apela acea funcție indirect, prin intermediul pointerului. |
- | Cum ne putem da seama dacă o funcție are asociată o adresă din memorie? Răspunsul este unul simplu și o apelăm fără parametri. | + | Cum ne putem da seama dacă o funcție are asociată o adresă din memorie? Răspunsul este unul simplu și anume o apelăm fără parametri. |
<code cpp> | <code cpp> | ||
Line 579: | Line 579: | ||
</code> | </code> | ||
- | Practic, numele funcției reprezintă adresa din memorie unde este stocată acea funcție. Cu alte cuvinte, atunci când folosim numele funcției, accesăm automat locația sa din memorie, ceea ce permite atribuirea adresei acesteia unui pointer la funcție. | + | Practic, numele funcției reprezintă adresa din memorie unde este stocată acea funcție. Cu alte cuvinte, atunci când folosim numele funcției, accesăm automat locația sa din memorie, ceea ce permite atribuirea adresei acesteia unui **pointer la funcție**. |
- | Rețeta cea mai simplă pentru declararea unui function pointer este următoarea -> **return_type (*name)(parameters)** | + | Rețeta cea mai simplă pentru declararea unui **function pointer** este următoarea -> **return_type (*name)(parameters)** |
- | Să construim un function pointer pentru funcția de la exemplul anterior. | + | Să construim un **function pointer** pentru funcția de la exemplul anterior. |
<code cpp> | <code cpp> | ||
Line 604: | Line 604: | ||
</code> | </code> | ||
- | <note warning>Atât tipul returnat de function pointer cât și lista parametrilor acestuia trebuie să fie identice cu cele ale funcției spre care pointează pentru a nu avea eroare de compilare.</note> | + | <note warning>Atât **tipul returnat** de function pointer cât și **lista parametrilor** acestuia trebuie să fie **identice** cu cele ale funcției spre care pointează pentru a nu avea eroare de compilare.</note> |
Utilitatea pointerilor la funcții devine evidentă atunci când dorim să evităm duplicarea codului și să facem programul mai flexibil și mai modular. În loc să scriem cod redundant pentru a apela funcții similare în contexte diferite, putem utiliza pointeri la funcții pentru a selecta dinamic funcția corespunzătoare în timpul execuției. | Utilitatea pointerilor la funcții devine evidentă atunci când dorim să evităm duplicarea codului și să facem programul mai flexibil și mai modular. În loc să scriem cod redundant pentru a apela funcții similare în contexte diferite, putem utiliza pointeri la funcții pentru a selecta dinamic funcția corespunzătoare în timpul execuției. | ||
- | Să luăm ca exemplu sortarea unui vector de numere întregi mai întâi crescător și apoi descrescător. Vom utiliza Bubble sort pentru a nu complica foarte mult logica programului. | + | Să luăm ca exemplu sortarea unui vector de numere întregi mai întâi crescător și apoi descrescător. Vom utiliza Bubble Sort pentru a păstra simplitatea programului. |
<code cpp> | <code cpp> | ||
Line 683: | Line 683: | ||
</code> | </code> | ||
- | Se poate observa că singura diferență, între cele două funcții de sortare, apare la condiția din if pentru a stabili ordinea în care va fi afișat vectorul. Pentru a putea simplifica masiv codul am putea utiliza pointeri la funcții. | + | Se poate observa că singura diferență, între cele două funcții de sortare, apare la condiția din **if** pentru a stabili ordinea în care vor fi ordonate elementele vectorului. Pentru a putea simplifica masiv codul am putea utiliza **pointeri la funcții**. |
<code cpp> | <code cpp> | ||
Line 756: | Line 756: | ||
Se poate observa cât de elegant am evitat acum codul duplicat prin construirea a doi comparatori (cele două funcții care întorc o valoare booleană) pentru a decide ordinea de sortare și trimiterea adreselor acestora ca al treilea parametru de la funcția de sortare. Așadar prin utilizarea function pointer-ului ca parametru beneficiem de flexibilitate și eleganță în cod chiar dacă sintaxa nu este cea mai prietenoasă. Acesta este doar un simplu exemplu demonstrativ de utilizare a pointerilor la funcții. Pentru mai multe exemple de cum pot fi folosți pointerii la funcții puteți citi de [[https://www.geeksforgeeks.org/function-pointer-in-c/|aici]]. | Se poate observa cât de elegant am evitat acum codul duplicat prin construirea a doi comparatori (cele două funcții care întorc o valoare booleană) pentru a decide ordinea de sortare și trimiterea adreselor acestora ca al treilea parametru de la funcția de sortare. Așadar prin utilizarea function pointer-ului ca parametru beneficiem de flexibilitate și eleganță în cod chiar dacă sintaxa nu este cea mai prietenoasă. Acesta este doar un simplu exemplu demonstrativ de utilizare a pointerilor la funcții. Pentru mai multe exemple de cum pot fi folosți pointerii la funcții puteți citi de [[https://www.geeksforgeeks.org/function-pointer-in-c/|aici]]. | ||
- | <note important>Tipul de date **bool** în C++ este un **tip de bază**, care simplifică exprimarea condițiilor în programare. O variabilă booleană poate avea doar două valori: **true** sau **false**, făcând astfel codul mai clar și mai ușor de înțeles. Se recomandă utilizarea tipului bool în locul tipului int atunci când rezultatul unei comparații este strict adevărat sau fals. În C, pentru a folosi acest tip de date, este necesară includerea antetului stdbool.h prin directiva ''#include <stdbool.h>'' la începutul programului.</note> | + | <note important>Tipul de date **bool** în C++ este un **tip de bază**, care simplifică exprimarea condițiilor în programare. O variabilă booleană poate avea doar două valori: **true** sau **false**, făcând astfel codul mai clar și mai ușor de înțeles. Se recomandă utilizarea tipului **bool** în locul tipului **int** atunci când rezultatul unei comparații este strict **adevărat** sau **fals**. În C, pentru a folosi acest tip de date, este necesară includerea antetului **stdbool.h** prin directiva ''#include <stdbool.h>'' la începutul programului.</note> |
=== Variabile constante === | === Variabile constante === | ||
- | La fel ca în C, în C++ putem declara variabile constante folosind cuvântul cheie **const**. Așa cum le spune și numele variabilele constante sunt cele care odată inițializate cu o valoare nu mai pot fi modificate. | + | La fel ca în C, în C++ putem declara variabile constante folosind cuvântul cheie **const**. Așa cum le spune și numele variabilele constante sunt cele care odată inițializate cu o valoare **nu** mai pot fi modificate. |
- | <note warning>O variabilă constantă se ințializează pe linia unde a fost declarată altfel va genera o eroare de compilare</note> | + | <note warning>O **variabilă constantă** se ințializează pe linia unde a fost **declarată** altfel va genera o eroare de compilare.</note> |
== Utilizarea keyword-ului const pe variabile obișnuite == | == Utilizarea keyword-ului const pe variabile obișnuite == | ||
Line 772: | Line 772: | ||
{ | { | ||
const float pi = 3.14f; // variabila constanta corect declarata | const float pi = 3.14f; // variabila constanta corect declarata | ||
- | const double k; // eroare de compilare variabila constanta neinitializata | + | const double k; // eroare de compilare variabila constanta este neinitializata |
int a = 2; | int a = 2; | ||
Line 829: | Line 829: | ||
== Utilizarea keyword-ului const pe parametrii funcțiilor == | == Utilizarea keyword-ului const pe parametrii funcțiilor == | ||
- | Este similar cu ceea ce ați văzut în cele 2 situații pentru variabile și pentru pointeri. Reluați exemplele de cod de mai sus care conțin funcții și încercați să vă dați seama unde ar fi necesari parametri constanți. | + | Este similar cu ceea ce ați văzut în cele 2 situații pentru variabile și pentru pointeri. Reluați exemplele de cod de mai sus care conțin funcții și încercați să vă dați seama unde ar fi necesar ca subprogramele să aibă parametrii constanți. |
==== Diferențe C/C++ ==== | ==== Diferențe C/C++ ==== | ||
Line 835: | Line 835: | ||
=== Citirea și afișarea variabilelor === | === Citirea și afișarea variabilelor === | ||
- | În C erați obișnuiți să citiți și să afișați variabilele utilizând funcțiile **scanf** și **printf**. În C++ vom folosi operatorul **>>** pentru citire și operatorul **<<** pentru afișare. | + | În C erați obișnuiți să citiți și să afișați variabilele utilizând funcțiile **scanf** și **printf**. În C++ vom folosi operatorul **%%>>%%** pentru citirea datelor de la tastatură sau din fișiere și operatorul **%%<<%%** pentru afișarea datelor în fișiere sau consolă. |
- | == Citirea și afisarea în C == | + | == Citirea și afișarea în C == |
<code c> | <code c> | ||
Line 855: | Line 855: | ||
</code> | </code> | ||
- | == Citirea și afisarea în C++ == | + | == Citirea și afișarea în C++ == |
<code cpp> | <code cpp> | ||
Line 873: | Line 873: | ||
</code> | </code> | ||
- | <note>Puteți folosi în C++ și funcțiile scanf și printf desigur, dar ca și recomandare ar fi mai indicat să utilizați operatorii limbajului C++, deoarece sunt mai specializați pentru ceea ce vom învăța pe parcursul semestrului.</note> | + | <note>Puteți folosi în C++ și funcțiile **scanf** și **printf** desigur, dar ca și recomandare ar fi mai indicat să utilizați operatorii limbajului C++, deoarece sunt mai specializați pentru ceea ce vom învăța pe parcursul semestrului.</note> |
=== Alocarea dinamică a memoriei === | === Alocarea dinamică a memoriei === | ||
Line 980: | Line 980: | ||
== Alocare dinamică în C++ == | == Alocare dinamică în C++ == | ||
- | În C++ alocarea și dezalocarea memoriei sunt mai simple, deoarece aici nu mai avem //funcții// ci operatori specifici. Pentru a aloca memoria în C++ se folosește //operatorul// **new**, iar pentru a elibera memoria folosim //operatorul// **delete**. | + | În C++ alocarea și dezalocarea memoriei sunt mai simple, deoarece aici **nu** mai avem **funcții** ci **operatori specifici**. Pentru a aloca memoria în C++ se folosește **operatorul new**, iar pentru a elibera memoria folosim **operatorul delete**. |
**Alocarea dinamică pentru o singură adresă de memorie** | **Alocarea dinamică pentru o singură adresă de memorie** | ||
Line 991: | Line 991: | ||
int main() | int main() | ||
{ | { | ||
- | int* ptr1 = new int; // alocare dinamica fara initializare, compilatorul va atribui o valoare random | + | int* ptr1 = new int; // alocare dinamica fara initializare, compilatorul va atribui o valoare in mod aleator |
int* ptr2 = new int(10); // alocare dinamica cu initializare | int* ptr2 = new int(10); // alocare dinamica cu initializare | ||
Line 997: | Line 997: | ||
std::cout << *ptr2 << '\n'; | std::cout << *ptr2 << '\n'; | ||
- | /*delete ptr1, ptr2; // desi nu da eroare de compilare va elibera doar spatiul pentru ptr1, nu se recomanda aceasta scriere poate genera memory leak-uri usor*/ | + | /*delete ptr1, ptr2; // desi nu da eroare de compilare va elibera doar spatiul pentru ptr1, nu se recomanda aceasta scriere pentru ca va genera memory leak-uri usor*/ |
delete ptr1; | delete ptr1; | ||
Line 1006: | Line 1006: | ||
</code> | </code> | ||
- | <note warning>Numărul de delete-uri trebuie să fie egal cu numărul de new-uri.</note> | + | <note warning>**Numărul** de delete-uri trebuie **să fie egal** cu **numărul** de new-uri.</note> |
**Alocarea dinamică pentru un bloc de memorie** | **Alocarea dinamică pentru un bloc de memorie** | ||
Line 1039: | Line 1039: | ||
</code> | </code> | ||
- | <note warning>În C++ **nu** există operator pentru realocarea memoriei. **Nu** este recomandată utilizarea funcției **realloc** pe un vector care a fost alocat cu operatorul **new** deoarece va duce la un comportament nedefinit. Dacă vrem să realocăm vectorul va trebui mai întâi să îl ștergem și apoi să îl realocăm cu noua dimensiune.</note> | + | <note warning>În C++ **nu** există operator pentru **realocarea memoriei**. **Nu** este recomandată utilizarea **funcției realloc** pe un vector care a fost alocat cu **operatorul new**, deoarece va duce la un comportament nedefinit. Dacă vrem să realocăm vectorul va trebui mai întâi să îl ștergem și apoi să îl realocăm cu noua dimensiune.</note> |
<code cpp> | <code cpp> | ||
Line 1093: | Line 1093: | ||
=== Tipul referință === | === Tipul referință === | ||
- | În C++, referințele reprezintă o extensie importantă a modului în care putem gestiona variabilele. O referință este, în esență, un alias pentru o variabilă existentă. Spre deosebire de un pointer, care stochează adresa unei variabile, o referință acționează direct asupra variabilei pe care o referențiază, fără a necesita acces explicit la adresa acesteia. | + | În C++, referințele reprezintă o extensie importantă a modului în care putem gestiona variabilele. O referință este, în esență, un **alias** pentru o variabilă existentă. Spre deosebire de un pointer, care stochează adresa unei variabile, o referință acționează direct asupra variabilei pe care o referențiază, fără a necesita acces explicit la adresa acesteia. |
Când atribuim o referință unei variabile, orice operație efectuată asupra referinței este de fapt aplicată direct asupra variabilei originale. Se comportă similar cu pointerii doar că există niște diferențe. | Când atribuim o referință unei variabile, orice operație efectuată asupra referinței este de fapt aplicată direct asupra variabilei originale. Se comportă similar cu pointerii doar că există niște diferențe. | ||
- | - Referința când este declarată trebuie instant inițializată | + | - Referința când este declarată trebuie **instant inițializată** |
- | - După inițializare referința nu mai poate fi schimbată | + | - După inițializare referința **nu** mai poate fi schimbată |
- | - Referințele nu pot fi nule | + | - Referințele **nu** pot fi nule |
- | - Referințele nu trebuie dereferențiate | + | - Referințele **nu** trebuie dereferențiate |
<code cpp> | <code cpp> | ||
Line 1127: | Line 1127: | ||
</code> | </code> | ||
- | Pentru a înțelge mai bine, în desenul de mai jos se poate observa de fapt cine este ref și că nu face altceva decât să partajeze aceeași zonă de memorie ca și x. | + | Pentru a înțelge mai bine, în desenul de mai jos se poate observa de fapt cine este **ref** și că nu face altceva decât să partajeze aceeași zonă de memorie ca și **x**. |
{{ :poo-is-ab:laboratoare:reference_logic.jpg |}} | {{ :poo-is-ab:laboratoare:reference_logic.jpg |}} | ||
Line 1190: | Line 1190: | ||
Funcția afisareVector folosește referințe constante la pointeri (const int* const& v), pentru a garanta că atât adresa vectorului, cât și conținutul acestuia nu vor fi modificate în timpul afișării, menținând integritatea datelor. | Funcția afisareVector folosește referințe constante la pointeri (const int* const& v), pentru a garanta că atât adresa vectorului, cât și conținutul acestuia nu vor fi modificate în timpul afișării, menținând integritatea datelor. | ||
- | <note important>C++ introduce posibilitatea de a inițializa pointerii cu nullptr, care este specific doar pentru acest tip de date. Acesta funcționează similar cu vechiul NULL, dar cu un avantaj important: nullptr este un tip de date dedicat pointerilor, ceea ce previne atribuirea sa accidentală altor tipuri de variabile, cum se putea întâmpla cu NULL în C++. În C++, NULL este definit doar ca un macro care reprezintă valoarea 0 și poate fi atribuit chiar și variabilelor care nu sunt pointeri, lucru care poate duce la erori neintenționate.</note> | + | <note important>C++ introduce posibilitatea de a inițializa pointerii cu **nullptr**, care este specific doar pentru acest tip de date. Acesta funcționează similar cu **NULL**, dar cu un avantaj important: **nullptr** este un tip de date dedicat **pointerilor**, ceea ce previne atribuirea sa accidentală altor tipuri de variabile, cum se putea întâmpla cu **NULL** în C++. În C++, **NULL** este definit doar ca un **macro** care reprezintă valoarea 0 și poate fi atribuit chiar și variabilelor care nu sunt pointeri, lucru care poate duce la confuzii nedorite.</note> |
<code cpp> | <code cpp> | ||
Line 1209: | Line 1209: | ||
==== Concluzii ==== | ==== Concluzii ==== | ||
- | Am putut observa în cadrul acestui laborator care sunt asemănările dar și diferențele în linii mari dintre C și C++. De acum înainte suntem pregătiți să explorăm paradigma orientată obiect pe care o vom începe din laboratorul următor. | + | Am putut observa în cadrul acestui laborator care sunt **asemănările**, dar și **diferențele** în linii mari dintre C și C++. De acum înainte suntem pregătiți să explorăm **Paradigma Orientată Obiect** pe care o vom începe din laboratorul următor. |