This shows you the differences between two versions of the page.
poo-is:laboratoare:02 [2020/10/01 12:05] alexandru.ionita99 [3. Functii membre ale unei structuri] |
— (current) | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Laboratorul 02: Elemente Specifice C++ ====== | ||
- | |||
- | Laboratorul de azi are ca scop acomodarea voastra cu limbajul C++. Asta presupune tranzitia de la limbajul C, dar si intelegerea conceptelor elementare, noi, ce tin de C++. Asadar, materialul de astazi poate fi vazut ca un fel de 'Crash Course' pentru C++. El reprezinta minimul necesar care trebuie stiut pentru a intelege continutul laboratoarelor urmatoare, dar nu elimina necesitatea voastra de a exersa cu limbajul pentru a deveni familiari cu acesta. | ||
- | |||
- | Inainte de fiecare laborator, vom prezenta surse aditionale de informare, unde veti putea gasi explicatii suplimentare si exemple concrete. | ||
- | |||
- | In cazul laboratorului curent, recomandam urmatoarele capitole din cartea [[https://discourse-production.oss-cn-shanghai.aliyuncs.com/original/3X/2/3/2380479dcb8e375425884a10da232730bbc7f88d.pdf|Absolute C++]] | ||
- | * Capitolul 1 (Chapter 1. pag 1-45): C++ Basics | ||
- | * Capitolul 2 (Chapter 2. pag 45-99): Flow of Control (if-else, for etc) | ||
- | * Capitolul 10 (Chapter 10. pag 419-471): Pointers and Dynamic Arrays | ||
- | |||
- | |||
- | ===== 1. Comentarii "stil" C++ ===== | ||
- | |||
- | Putem scrie comentarii pe o linie folosind simbolurile <code>//</code> | ||
- | Ce este intre 'forward-slash-uri' si sfarsitul liniei se ignora. | ||
- | |||
- | <code c++> | ||
- | // Comentariu C++ | ||
- | |||
- | /* Comentariu C */ | ||
- | |||
- | /* | ||
- | * Comentariu Stil Documentatie (Asemanator C) | ||
- | */ | ||
- | </code> | ||
- | |||
- | ===== 2. Struct si Union ===== | ||
- | Structurile si uniunile pot fi referite doar prin nume, deci fara a fi nevoiti sa utilizam cuvintele cheie **struct** sau **union** de fiecare data. | ||
- | Asta arata ca struct-urile si union-urile sunt in C++ tipuri de date recunoscute automat. | ||
- | |||
- | <code c++> | ||
- | struct str | ||
- | { | ||
- | // Cod aferent structurii | ||
- | }; | ||
- | |||
- | union uni | ||
- | { | ||
- | // Cod aferent union | ||
- | }; | ||
- | |||
- | str a, b; | ||
- | uni c, d; | ||
- | |||
- | void f(str x) {/* cod functie */} | ||
- | </code> | ||
- | |||
- | ===== 3. Functii membre ale unei structuri ===== | ||
- | |||
- | Pentru a modifica campurile unei variabile de tip struct, putem utiliza **functiile membre** (**metode**). Aceste functii au urmatoarele caracteristici: \\ | ||
- | * variabila struct asupra careia se lucreaza nu este data ca parametru | ||
- | ===== 4. Functii Inline ===== | ||
- | |||
- | O functie inline e definita in felul urmator: | ||
- | <code c++> | ||
- | inline TIP_RETURNAT NUME_FUNCTIE(LISTA_ARGUMENTE) {/* cod functie */} | ||
- | </code> | ||
- | |||
- | **De ce sunt utile functiile inline?** | ||
- | La apelul unei functii inline, in loc de **apelarea** propriu-zisa a functiei, se **substituie** codul efectiv al functiei la compilare. Avantajul este in termeni de viteza. Nu se va mai 'cauta' definitia functiei, va fi 'direct' unde trebuie. Dezavantajul este cresterea dimensiunii fisierului binar generat. Asadar, functiile **inline** reprezinta un trade-off intre viteza si spatiu. | ||
- | |||
- | Pentru a rula exemplul de mai jos, click [[https://repl.it/@thesergiu/lab2inline#main.cpp|aici]]. | ||
- | <code c++> | ||
- | #include <iostream> | ||
- | using namespace std; | ||
- | |||
- | // Declaram functia 'max' drept inline | ||
- | inline int max(int a, int b) | ||
- | { | ||
- | return (a > b) ? a : b; | ||
- | } | ||
- | |||
- | /* Echivalent in C */ | ||
- | #define max(a,b) (((a) > (b)) ? (a) : (b)) | ||
- | |||
- | int main() { | ||
- | | ||
- | int nr1 = 2, nr2 = 6; | ||
- | cout << max(nr1, nr2) << endl; | ||
- | |||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | |||
- | |||
- | ===== 5. Supradefinirea functiilor ===== | ||
- | In C++ este permisa supradefinirea functiilor, adica existenta mai multor functii cu acelasi nume, dar care difera prin tipul si/sau numarul parametrilor. Decizia de a apela o | ||
- | functie sau alta se face dupa tipul sau numarul parametrilor. | ||
- | |||
- | Aceasta inseamna ca o functie este recunoscuta nu numai prin numele sau, ci prin **"semnatura"**, adica **nume, tip si lista de parametri**. | ||
- | |||
- | In exemplul de fata, se considera patru functii cu acelasi nume (cub). Trei dintre ele au un unic parametru (int, float si double), iar a patra are doi parametri. Ultima functie apeleaza functia cub specifica tipului float. Pentru a urmari apelurile, fiecare functie este insotita de un mesaj de identificare la consola. | ||
- | |||
- | Pentru a experimenta cu codul de mai jos, click [[https://repl.it/@thesergiu/lab2supradefinire#main.cpp|aici]]. | ||
- | <code c++> | ||
- | #include <iostream> | ||
- | using namespace std; | ||
- | |||
- | int cub (int n) | ||
- | { cout << "Apel functie cub int\n"; return n*n*n; } | ||
- | |||
- | double cub (double n) | ||
- | { cout << "Apel functie cub double\n"; return n*n*n; } | ||
- | |||
- | float cub (float n) | ||
- | { cout << "Apel functie cub float\n"; return n*n*n; } | ||
- | |||
- | float cub (float x, float a) | ||
- | { cout << "Apel functie cub diferenta\n"; | ||
- | return cub (x-a); // Aici se va apela cub cu float | ||
- | } | ||
- | |||
- | int main() { | ||
- | | ||
- | cout << cub(3) << endl << endl; | ||
- | |||
- | double var = 3.33335678976; | ||
- | cout << cub(var) << endl << endl; | ||
- | | ||
- | float var2 = 3.2; | ||
- | cout << cub(var2) << endl << endl; | ||
- | |||
- | cout << cub(9.9, 3.3) << endl << endl; | ||
- | |||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | |||
- | ===== 6. Operatorii de scriere (>>) si citire (<<) ===== | ||
- | Prin acesti operatori se pot face scrieri si citiri la consola in formate predefinite. Aceasta ne scuteste de folosirea functiilor de tip printf/scanf etc. si ofera o forma comoda de I/O. | ||
- | |||
- | Codul de mai jos poate fi executat de [[https://repl.it/@thesergiu/lab3operatoriIO#main.cpp|aici]]. | ||
- | |||
- | <code c++> | ||
- | #include <iostream> | ||
- | using namespace std; | ||
- | |||
- | int main() | ||
- | { | ||
- | int i, j, k; | ||
- | |||
- | cout << "Introduceti 3 intregi:\n"; | ||
- | cin >> i >> j >> k; | ||
- | cout << "Intregii sunt:\n"; | ||
- | cout << i << " " << j << " " << k << "\n"; | ||
- | | ||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | |||
- | |||
- | ===== 7. Tipul referinta ===== | ||
- | |||
- | O declaratie de forma | ||
- | |||
- | <code c++> | ||
- | int x; | ||
- | int &y = x; | ||
- | </code> | ||
- | |||
- | precizeaza ca y este o referinta catre un intreg (catre x). Din punct de vedere sintactic, y este tot un intreg, care nu "exista" insa (nu are suport propriu de memorie) ci este o alta referire | ||
- | la variabila x. Utilitatea principala a referintelor este impunerea transferului prin referinta a parametrilor la o functie. | ||
- | |||
- | Acest fapt este ilustrat prin functia **schimba** de mai jos. | ||
- | |||
- | <code c++> | ||
- | void schimba (int & a,int & b) | ||
- | { int temp = a; a = b; b = temp; } | ||
- | </code> | ||
- | |||
- | Declaratiile de parametri spun ca a si b sunt referinte catre intregi, | ||
- | deci la un apel de forma: | ||
- | <code c++> | ||
- | int x = 7, y = 3; | ||
- | schimba(x, y); | ||
- | </code> | ||
- | in variabilele a si b din functie se vor gasi referinte catre intregii x si y din programul apelant. Aceasta face ca functia sa poata modifica efectiv aceste variabile. | ||
- | |||
- | De comparat cu metoda de implementare a transferului prin referinta la C standard, unde trebuiau utilizati explicit pointeri | ||
- | |||
- | <code C> | ||
- | void schimba_C (int * a, int * b) | ||
- | { int temp = *a; *a = *b; *b = temp; } | ||
- | </code> | ||
- | |||
- | cu apelul | ||
- | |||
- | <code C> | ||
- | int x = 7, y = 3; | ||
- | schimba_C (&x, &y); | ||
- | </code> | ||
- | |||
- | Codul complet il puteti rula de [[https://repl.it/@thesergiu/lab2referinte#main.cpp|aici]]. | ||
- | <code c++> | ||
- | #include <iostream> | ||
- | using namespace std; | ||
- | |||
- | void schimba (int & a,int & b) | ||
- | { int temp = a; a = b; b = temp; } | ||
- | |||
- | int main() { | ||
- | | ||
- | int a = 4, b = 20; | ||
- | cout << "Inainte de interschimbare: " << endl; | ||
- | cout << a << " " << b << endl; | ||
- | |||
- | // Apelam interschimbarea | ||
- | schimba(a,b); | ||
- | |||
- | cout << "Dupa interschimbare:" << endl; | ||
- | |||
- | cout << a << " " << b << endl; | ||
- | |||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | |||
- | ===== 8. Alocari dinamice de memorie ===== | ||
- | Alocarea de memorie se face in C++ cu operatorul **new** (care face parte din limbaj). | ||
- | Enumeram urmatoarele forme de alocare: | ||
- | * Alocare simpla | ||
- | |||
- | <code c++> | ||
- | // Variabila | ||
- | int var = new int; | ||
- | </code> | ||
- | |||
- | * Alocarea unui tablou | ||
- | |||
- | <code c++> | ||
- | int dimensiune = 10; | ||
- | int *vect = new int[dimensiune]; | ||
- | </code> | ||
- | |||
- | * Alocarea cu initializare | ||
- | |||
- | <code c++> | ||
- | // Varianta C++ | ||
- | int *vect = new int[20](); | ||
- | |||
- | /* Echivalent, in C, ati fi procedat astfel: */ | ||
- | int i; | ||
- | int *vect = malloc(sizeof(int) * 20); | ||
- | for (i = 0; i < 20; i++) { | ||
- | vect[i] = 0; | ||
- | } | ||
- | |||
- | </code> | ||
- | |||
- | <note important>Atentie la utilizazarea pointerilor adecvati.</note> | ||
- | |||
- | Daca se doreste o alocare oarecare (fara a avea un tip precizat), se poate folosi tipul char (adica alocare la nivel de octet), sau unsigned char, "botezat" eventual cu typedef. | ||
- | |||
- | <code c++> | ||
- | // vrem sa alocam 20 de bytes, pentru un sir de caractere | ||
- | // 1 byte == sizeof(char) | ||
- | char *s1 = new char[sizeof(char) * 20 + 1]; | ||
- | // sau | ||
- | char *s2 = new char[20*sizeof(unsigned char) + 1] | ||
- | </code> | ||
- | |||
- | Metoda prezentata mai sus este nefolosita in industrie, tipul ** char* ** fiind inlocuit de tipul C++ **string**. Pentru mai multe detalii asupra clasei **string**, click [[https://www.tutorialspoint.com/cplusplus/cpp_strings.htm|aici]]. De asemenea, puteti inspecta Capitolul 9 (Chapter 9, pag 367-) : Strings, din cartea [[https://discourse-production.oss-cn-shanghai.aliyuncs.com/original/3X/2/3/2380479dcb8e375425884a10da232730bbc7f88d.pdf|Absolute C++]] pentru explicatii suplimentare, dar si exemple concrete. | ||
- | |||
- | ===== 9. Functii cu parametri impliciti ===== | ||
- | |||
- | Functia print_tab de mai jos are trei parametri, dintre care ultimul este de tip pointer la char, care este initializat implicit pe sirul constant "Mesaj implicit". | ||
- | Functia se poate apela **cu un parametru explicit**, sau ca si cand ar fi **doar cu doi parametri**, caz in care se va utiliza al treilea parametru **implicit**. | ||
- | |||
- | <code c++> | ||
- | // Definitie | ||
- | void print_tab (int *a, int n, char *mesaj = "Mesaj implicit\n") | ||
- | { | ||
- | for (int i = 0; i < n; i++) | ||
- | cout << a[i] << " "; | ||
- | cout << "\n" << mesaj; | ||
- | } | ||
- | |||
- | // Apeluri | ||
- | print_tab (q, 10, "Mesaj explicit\n"); | ||
- | print_tab (q, 10); | ||
- | </code> | ||
- | |||
- | ===== 10. Experiment ===== | ||
- | Ca sa testati toate conceptele prezentate in acest laborator, rulati codul de mai jos apasand [[https://repl.it/@thesergiu/lab2experiment#main.cpp|aici]]. | ||
- | |||
- | <code c++> | ||
- | #include <cstdlib> | ||
- | #include <iostream> | ||
- | |||
- | using namespace std; | ||
- | |||
- | |||
- | int cub (int n) | ||
- | { cout << "Apel functie cub int\n"; return n*n*n; } | ||
- | |||
- | double cub (double n) | ||
- | { cout << "Apel functie cub double\n"; return n*n*n; } | ||
- | |||
- | float cub (float n) | ||
- | { cout << "Apel functie cub float\n"; return n*n*n; } | ||
- | |||
- | float cub (float x, float a) | ||
- | { | ||
- | float m; | ||
- | cout << "Apel functie cub diferenta\n"; | ||
- | return cub (x-a); // Aici se va apela cub cu float | ||
- | } | ||
- | |||
- | void schimba (int & a,int & b) | ||
- | { int temp = a; a = b; b = temp; } | ||
- | |||
- | void print_tab (int *a, int n, char *mesaj = "Mesaj implicit\n") | ||
- | { | ||
- | for (int i = 0; i < n; i++) | ||
- | cout << a[i] << " "; | ||
- | cout << "\n" << mesaj; | ||
- | } | ||
- | |||
- | int main(int argc, char *argv[]) | ||
- | { | ||
- | int i = 12; float f = 12.5F; double d = 12.5; | ||
- | int j = 21, k; int *p, *q; | ||
- | // | ||
- | // Test functii cub | ||
- | // | ||
- | cout << cub(i) << " " << cub (f) << " " << cub (d) << "\n"; | ||
- | cout << cub (f, 2.5) << "\n"; | ||
- | cout << "i = " << i << " j = " << j << " inainte de schimba(i,j)\n"; | ||
- | schimba (i, j); | ||
- | cout << "i = " << i << " j = " << j << " dupa schimba(i.j)\n"; | ||
- | // | ||
- | // Test << si >> | ||
- | // | ||
- | cout << "Introduceti 3 intregi:\n"; | ||
- | cin >> i >> j >> k; | ||
- | cout << "Intregii sunt:\n"; | ||
- | cout << i << " " << j << " " << k << "\n"; | ||
- | // | ||
- | // Test operatori new si delete | ||
- | // | ||
- | p = new int; *p = 30; // Alocare simpla | ||
- | cout << *p << "\n"; delete p; | ||
- | p = new int (100); // Alocare cu initializare | ||
- | cout << *p << "\n"; delete p; | ||
- | q = new int [10]; // Alocare tablou de 10 int | ||
- | for (i = 0; i < 10; i++) | ||
- | *(q + i) = i + 1; //q[i]=i+1; | ||
- | for (i = 0; i < 10; i++) | ||
- | cout << q[i] << " "; | ||
- | cout << "\n"; | ||
- | // | ||
- | // Test functie cu argument implicit | ||
- | // | ||
- | print_tab (q, 10, "Mesaj explicit\n"); | ||
- | print_tab (q, 10); | ||
- | delete [] q; // Eliberare tablou | ||
- | // | ||
- | // La compilatoarele mai vechi, eliberarea de tablouri (i.e. operatorul delete []) nu este posibila. | ||
- | // Se foloseste in acest caz delete simplu. | ||
- | // | ||
- | |||
- | |||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | |||