Subiectul I, cerința 1
Încapsularea în programarea orientată pe obiecte
Principiul încapsulării, clase și obiecte, membrii unei clase, cele trei niveluri de acces, constructori și destructori, cu o problemă rezolvată printr-o clasă cu date alocate dinamic.
Principiul încapsulării
Încapsularea este unul dintre principiile fundamentale ale programării orientate pe obiecte. Ea constă în gruparea datelor și a metodelor care le prelucrează în aceeași clasă și în ascunderea datelor față de exterior.
Datele unui obiect nu pot fi citite sau modificate direct din afara clasei, ci numai prin metodele publice, care alcătuiesc interfața clasei.
În acest fel, reprezentarea internă a datelor poate fi schimbată fără rescrierea programelor care folosesc clasa, atât timp cât interfața rămâne aceeași. Verificările asupra valorilor se scriu o singură dată, în metodele clasei, și nu pot fi ocolite din exterior.
Clase și obiecte
Clasa este un tip de date definit de programator. Ea precizează ce date conține fiecare obiect de tipul ei și ce funcții se pot aplica asupra acestor date. Clasa este numai o descriere: prin declararea ei nu se rezervă memorie.
Obiectul este o variabilă de tipul dat de clasă și se mai numește instanță a clasei.
Fiecare obiect are propriile valori pentru datele descrise în clasă. Legătura dintre clasă și
obiect este aceeași cu cea dintre tipul int și o variabilă de tip int.
Membrii unei clase
Membrii unei clase sunt de două feluri:
- datele membru, numite și câmpuri sau atribute — variabilele declarate în clasă, care dau starea obiectului;
- funcțiile membru, numite și metode — funcțiile declarate în clasă, care lucrează cu datele obiectului.
Cele trei niveluri de acces
| Specificatorul | De unde se poate accesa membrul |
|---|---|
private | numai din funcțiile membru ale clasei și din funcțiile declarate prietene |
protected | din funcțiile membru ale clasei, din cele ale claselor derivate din ea și din funcțiile prietene |
public | din orice punct al programului în care obiectul este vizibil |
Dacă nu se scrie niciun specificator, membrii unei clase declarate cu class sunt implicit
private. Datele membru se declară private sau protected, iar funcțiile prin care se
lucrează cu obiectul se declară public.
Constructori
Constructorul este o funcție membru specială, care poartă numele clasei și nu are tip de rezultat. Este apelat automat la crearea unui obiect și îi inițializează datele membru.
O clasă poate avea mai mulți constructori, deosebiți prin lista de parametri:
- constructorul implicit, fără parametri;
- constructori cu parametri, prin care datele obiectului primesc valori chiar la creare;
- constructorul de copiere, care inițializează un obiect nou cu valorile altui obiect de același tip.
Dacă programatorul nu scrie niciun constructor, compilatorul generează un constructor implicit, care însă nu dă valori datelor membru.
Destructori
Destructorul este funcția membru apelată automat la distrugerea unui obiect, la încheierea
execuției blocului în care a fost declarat sau când este eliberat cu delete. Poartă numele
clasei precedat de caracterul ~, nu are parametri și nu are tip de rezultat. O clasă are cel
mult un destructor.
Rolul lui este să elibereze resursele ocupate de obiect, în primul rând memoria alocată dinamic în constructor. Fără destructor, memoria alocată pentru fiecare obiect ar rămâne ocupată până la terminarea programului.
Problema propusă
Enunț. Se citesc de la tastatură un număr natural n (1 ≤ n ≤ 30) și, pentru fiecare dintre cei n elevi ai unei clase, numele elevului (un singur cuvânt, de cel mult 29 de litere), numărul k al notelor obținute la o disciplină (1 ≤ k ≤ 20) și notele însele. Numărul notelor diferă de la un elev la altul. Să se afișeze, pentru fiecare elev, media notelor sale, iar apoi numele elevului cu media cea mai mare.
Implementarea soluției.
#include <iostream>
#include <cstring>
using namespace std;
class Elev {
private:
char nume[30];
int *note; // alocat dinamic in constructor
int nrNote;
public:
Elev(const char *n, int k, int v[]);
~Elev();
float media();
const char* numeleElevului();
};
/* Constructorul: copiaza numele, aloca memorie pentru exact atatea note
cate are elevul si le copiaza in zona alocata. */
Elev::Elev(const char *n, int k, int v[]) {
strcpy(nume, n);
nrNote = k;
note = new int[k];
for (int i = 0; i < k; i++)
note[i] = v[i];
}
/* Destructorul: elibereaza zona alocata in constructor. */
Elev::~Elev() {
delete[] note;
}
float Elev::media() {
int suma = 0;
for (int i = 0; i < nrNote; i++)
suma = suma + note[i];
return (float)suma / nrNote;
}
const char* Elev::numeleElevului() {
return nume;
}
int main() {
int n;
cin >> n;
Elev *clasa[30];
for (int i = 0; i < n; i++) {
char nume[30];
int k, v[20];
cin >> nume >> k;
for (int j = 0; j < k; j++)
cin >> v[j];
clasa[i] = new Elev(nume, k, v);
}
int celMaiBun = 0;
for (int i = 0; i < n; i++) {
cout << clasa[i]->numeleElevului() << " " << clasa[i]->media() << endl;
if (clasa[i]->media() > clasa[celMaiBun]->media())
celMaiBun = i;
}
cout << "Media cea mai mare: " << clasa[celMaiBun]->numeleElevului();
for (int i = 0; i < n; i++)
delete clasa[i];
return 0;
}
Descrierea soluției. Clasa Elev grupează datele unui elev (numele, notele și numărul
lor) și funcțiile care lucrează cu ele. Toate cele trei date membru sunt
declarate private, deci nu pot fi citite sau modificate din afara clasei; ele pot fi
accesate numai prin funcțiile declarate public.
Numărul notelor nu se cunoaște la scrierea programului și diferă de la un elev la altul, așa că
vectorul notelor se alocă dinamic în constructor, cu exact atâtea elemente câte note are
elevul, și se eliberează în destructor. Constructorul primește numele, numărul de note și
notele și este apelat automat la crearea obiectului, prin operatorul new.
Media nu se păstrează ca dată membru, ci se calculează în funcția media de fiecare dată când
este cerută, din notele obiectului. Restul programului nu are acces la vectorul de note și nu
îl poate modifica, așa că media rezultă întotdeauna din notele memorate în obiect.
În programul principal, obiectele se creează cu new și se rețin într-un vector de pointeri,
fiindcă fiecare obiect se poate crea numai după citirea datelor elevului, iar clasa nu are
constructor fără parametri. La sfârșit fiecare obiect se
eliberează cu delete, moment în care se apelează destructorul și se eliberează și vectorul de
note.
Rezolvarea se poate citi pe educamp.ro, la adresa de mai sus.