Refactorizarea
Refactorizarea (sau code refactoring) este un proces prin care codul este restructurat (pentru a putea fi reutilizat mai departe in alte contexte), fara a schimba in esenta, ceea ce el face. Definitia este cam lunga, dar mai pe scurt: restructuram codul astfel incat sa il facem mai usor de organizat.
Avem doua metode prin care putem face asta:
- Folosind functii
- Cu parametrii pasati printr-o valoare
- Cu parametrii pasati printr-o referinta constanta
- Cu parametrii pasati printr-o referinta non-constanta
- Folosind fisiere separate
- Fisiere sursa si headere
Functii in C++
#include <iostream> #include <vector> #include <algorithm> using namespace std; vector < int > V; int main() { int N; cin >> N; for(int i = 1; i <= N; i++) { int val; cin >> val; V.push_back(val); } //Suma numerelor int sum = 0; for(unsigned int i = 0; i < V.size(); i++) sum = sum + V[i]; cout << "Suma este: " << sum << "\n"; //Media numerelor cout << "Media este: " << float(sum / V.size()) << "\n"; //Elementul din mijloc unsigned int elemente = V.size(); sort(V.begin(), V.end()); int mijloc = elemente / 2; if (elemente % 2 == 1) // daca numarul de elemente este impar cout << V[mijloc]; else // daca numarul de elemente este par cout << (V[mijloc-1] + V[mijloc])/2; return 0; }
Sa ne imaginam un program care calculeaza suma si media elementelor dintr-un vector. De asemenea dorim sa aflam si elementul median. Anumite parti din acest program pot sa fie separate si reutilizate pe viitor:
- Citirea vectorului
- Calcularea sumei
- Calcularea mediei aritmetice
Functiile pentru calcularea sumei si a mediei aritmetice nu efectueaza nici o operatie de citire sau scrierere, ceea ce le face sa fie mai de ajutor, in general.
Daca nu sunteti stapani pe bibleoteca <vector> puteti sa citii mai multe despre ea pe cppreference.com.
Evitati copierea
#include <iostream> #include <vector> #include <algorithm> using namespace std; int suma(const vector < int > &V) { const unsigned int elemente = V.size(); if(elemente == 0) throw domain_error("Vectorul nu are elemente"); int sum = 0; for(unsigned int i = 0; i < V.size(); i++) sum = sum + V[i]; return sum; } float media(const vector < int > &V) { const unsigned int elemente = V.size(); if(elemente == 0) throw domain_error("Vectorul nu are elemente"); int sum = 0; for(unsigned int i = 0; i < V.size(); i++) sum = sum + V[i]; return float(sum / V.size()); } int median(vector < int > V) { const unsigned int elemente = V.size(); if(elemente == 0) throw domain_error("Vectorul nu are elemente"); sort(V.begin(), V.end()); const int mijloc = elemente / 2; if (elemente%2 == 1) // daca numarul de elemente este impar return V[mijloc]; else // daca numarul de elemente este par return (V[mijloc-1] + V[mijloc])/2; } void citire(int &N, vector < int > &V) { cin >> N; for(int i = 1; i <= N; i++) { int val; cin >> val; V.push_back(val); } } int main() { vector < int > Vector; int N; citire(N, Vector); //Suma numerelor cout << "Suma este: " << suma(Vector) << "\n"; //Media numerelor cout << "Media este: " << media(Vector) << "\n"; //Elementul din mijloc cout << "Elementul din mijloc este: " << median(Vector); return 0; }
In functia noastra “median” este normal sa facem o copie a vectorului deoarece functia noastra schimba vectorul in sine (prin sortarea lui), dar poate in alte parti ale programului vom avea nevoie de vector asa cum a fost el introdus initial.
Din pacate copierea parametrilor mari este costisitoare si in general dorim sa o evitam. Uneori poate dorim sa returnam ceea ce am obtinut tot prin parametru, insa daca operam pe o copie, acest lucru este mai dificil.
O solutie in C++ (Pascal, C#, etc) o reprezinta parametrii prin referinta (sau reference parameters). In C++ acesti parametrii de referinta se marcheaza prin simbolul &.
Daca functia nu schimba parametrii, poate fi declarata ca si const (constanta).
Parametrii pasati printr-o referinta constanta
Parametrul “V” este doar o alta denumire pentru “Vector” (nu se copiaza).
Cuvantul “const” promite ca functia nu va schimba vectorul nostru “V” si compilatorul verifica acest lucru.
Alte utilizari alte const
Cuvantul “const” poate fi folosit si altfel:
- Pentru a declara variabile constante (un fel de “#define”)
- Pentru a declara parametrii si variabile locale cu valori fixe
Sfat: folositi const de fiecare data cand este posibil.
Alte exemple ale folosirii const
Pentru eficienta putem utiliza “const” atunci cand salvam variabila “V.size()” prin variabila “elemente”, care pe urma nu urmeaza sa se schimbe.
Functia de citire
Aceasta functie lucreaza de asemenea cu vectorul original (fara a-l copia), dar il schimba (pentru ca il umple cu elemente), asa ca parametrul “V” este apelat prin “&”, fara “const”.
Trei tipuri de parametrii
-
Parametrii pasati prin valoare:
int median(vector < int > V)
Parametrul este o noua variabila, initializandu-se ca si o copie a parametrului.
-
Parametrii pasati printr-o referinta:
float media(const vector < int > &V)
Parametrul este doar o alta denumire pentru parametrul nostru, dar functia l-ar putea schimba.
-
Parametrii pasati printr-o referinta constanta:
void citire(int &N, vector < int > &V)
Parametrul este doar o alta denumire pentru parametrul nostru, si functia nu il va schimba.
Alte folosiri ale accesarii prin &
Putem sa accesam prin referinta si atunci cand dorim sa refolosim aceeasi locatie din memorie (mult mai bine decat sa refolosim o valoare). De exemplu:
vector < int > v; //...citirea... int &x = v[3]; x = 1 + 2 * x;
Aici x nu este o noua variabila, ci o alta denumire pentru v[3]. Totul este schimbat prin x, dar orice schimbare este aplicata lui v[3].