Clase in C++ .
Definitie. Declarare. Implementare. Utilizare
Background Incepand cu anul 1979, Bjourne Stroustrup a inceput dezvoltarea unei serii de imbunatatiri a limbajului C prezent in momentul respectiv, denumindu-l “C cu clase” pana in anul 1983 cand a ajuns cunoscut drept “C++”. Principalele avantaje ale acestui nou limbaj de programare proveneau din introducerea claselor, functiilor virtuale, supraincarcarii operatorilor, sabloanelor precum si a exceptiilor, printre altele. Dintre toate acestea, clasele au reprezentat cel mai important atu in fata “fratelui mai mare” , C. Pentru a discuta mai departe despre acestea, trebuie intelese mai intai doua notiuni:
clasa in sine si
obiectul.
Definitie O definitie “bruta” a clasei ar fi aceea ca este un concept extins al unui tip de date abstract : in loc sa contina numai informatii – variabile - , contine si functii.
Un obiect este o instantiere a unei clase. Mai precis, clasa ar fi tipul de date si obiectul ar fi variabila.
Asadar, printr-o analogie la limita, am putea spune despre clase ca reprezinta echivalentul tipului de date
struct din C.
Declarare Schema unei clase este:
class nume_clasa
{
specificator_de_acces1:
membru1;
specificator_de_acces2:
membru2;
...
} nume_obiect;
Explicatii:- pentru declararea unei clase se foloseste cuvantul cheie (rezervat) “
class” la inceput (ca la
struct);
- apoi dam un nume valid clasei - in schema de fata acesta fiind
nume_clasa;
- in interior se observa imediat specificatorul de acces (
specificator_de_accesX); acesta poate fi de trei tipuri:
1.
private – membrii cu aceasta proprietate pot fi accesati doar din interiorul altor membrii ai aceleiasi clase sau din interiorul prietenilor lor
2.
protected – membrii cu aceasta proprietate pot fi accesati ca si cei private (din alti membrii ai aceleiasi clase sau din prieteni) dar si din membrii claselor derivate
3.
public – ultimul specificator permite ca membrii sai sa poata fi accesibili de oriunde este obiectul vizibil
- membrii clasei (
membru1, membru2, ...membruX) pot fi atat variabile, structuri de date (deci si clase) sau functii.
- in final, numele obiectului (
nume_obiect) sau lista de obiecte din moment ce pot fi mai multe, este optional (ca la
struct).
Ca o observatie, se poate mentiona faptul ca datele si functiile declarate intr-o clasa devin proprii clasei respective. De aceea, ele se vor numi
date membre, respectiv
functii membre (metode) ale acelei clase.
Implementare Pentru o intelegere mai ampla a acestor notiuni, vom trece la aplicarea lor in practica. Vom crea un nou proiect C++ gol in IDE unde vom avea doua fisiere:
main.cpp si
main.h . In header vom defini clasa si metodele acesteia, iar in
main.cpp o vom folosi.
//main.h
class Elev
{
private:
float media_generala;
float medii_la_obiecte[20];
int nr_obiecte;
int corigent;
public:
Elev(); //constructorul
int este_corigent() { return corigent==1; };
float MG();
void introducere_medii(int, float* );
void returnare_medii(float *);
};
//definirea functiilor clasei
Elev::Elev()
{
media_generala = 0;
nr_obiecte = 0;
corigent = 0; //presupunem ca a trecut
}
void Elev::introducere_medii(int contor, float v[10])
{
float suma = 0;
for(int i=1; i<=contor; i++)
suma += v[i];
nr_obiecte ++;
medii_la_obiecte[ nr_obiecte ] = suma / contor;
if(medii_la_obiecte[ nr_obiecte ] <=4 )
corigent = 1; //este corigent la cel putin un obiect
}
float Elev::MG()
{
float suma;
for(int i=1; i<=nr_obiecte; i++)
suma += medii_la_obiecte[i];
media_generala = suma / nr_obiecte;
return media_generala;
}
void Elev::returnare_medii(float v[20])
{
for(int i=1; i<=nr_obiecte; i++)
v[i] = medii_la_obiecte[i];
}
Utilizare//main.cpp
#include <iostream>
#include "main.h"
using namespace std;
int main(void)
{
Elev Ionut;
float fizica[10] = {0, 5, 7, 8 , 8, 6};
float engleza[10] = {0, 10, 10, 9};
float sport[10] = {0, 10, 10};
float biologie[10] = {0, 7, 8, 10, 10 };
float aux[20];
//calculam situatia elevului Ionut
Ionut.introducere_medii(5, fizica);
Ionut.introducere_medii(3, engleza);
Ionut.introducere_medii(2, sport);
Ionut.introducere_medii(4, biologie);
//preluam mediile la obiecte si afisam situatia
//intrebam daca Ionut este corigent
if(Ionut.este_corigent())
cout<<"Elevul Ionut este corigent."<<'\n';
else
cout<<"Ionut este un elev silitor, asa ca nu a ramas corigent la nici o materie."<<'\n';
//afisam media generala
cout<<"Media generala a lui Ionut este: "<<Ionut.MG()<<'\n';
//si restul mediilor
Ionut.returnare_medii( aux );
cout<<"Mediile lui Ionut sunt: "<<'\n';
cout<<"Fizica : "<< aux[1]<<'\n';
cout<<"Engleza : "<<aux[2]<<'\n';
cout<<"Sport : "<<aux[3]<<'\n';
cout<<"Biologie : "<<aux[4]<<'\n';
return 0;
}
In acest moment se poate observa clasa Elev care ajuta la calcularea mediei generale si a mediilor (aritmetice numai) la diferite obiecte. Astfel, se disting cele 4 variabile cu acces
private precum si cele 5 metode
publice.
Explicatii:1.
Elev() – aceasta functie este specifica claselor, purtand numele de constructor. In general, obiectele au nevoie sa-si initializeze variabilele sau sa le aloce memorie dinamic in timpul procesului de creare pentru ca acestea sa devina operabile si pentru a evita prezenta unor valori random in interiorul acestora care ar putea genera bug-uri, erori sau destabilizarea intregului algoritm. Din aceste motive, clasa include o functie speciala, numita constructor, care este apelata automat cand un obiect al clasei este creat. Constructorul unei clase poarta acelasi nume ca si clasa, aici fiind
Elev. Foarte important in cazul functiilor constructor este faptul ca
NU AU TIP (nici macar void). In exemplul de fata, in momentul in care obiectul Ionut se creeaza in
main(), variabilele
media_generala,
nr_obiecte si
corigent iau valoarea 0.
2.
int este_corigent() – aceasta functie este inline, adica definita in interiorul clasei. Toate metodele unei clase se pot defini astfel dar aspectul practic ii forteaza pe multi programatori doar sa declare functia in interiorul clasei si sa o defineasca ulterior. Pentru o astfel de definire este utilizat operatorul de specificare de domeniu “
::”. Acesta indica faptul ca domeniul functiei respective este acelasi cu domeniul clasei din care face parte. Sintaxa definitiei unei functii membre a unei clase:
tip nume_clasa :: nume_functie_membra(...)
{
...
}
3. Restul functiilor sunt definite in exteriorul clasei folosind schema de mai sus. Asadar, functia
MG() va fi definita in urmatorul mod:
float Elev::MG()
{
float suma;
for(int i=1; i<=nr_obiecte; i++)
suma += medii_la_obiecte[i];
media_generala = suma / nr_obiecte;
return media_generala;
}
Utilizarea variabilelor si metodelor clasei se face ca in cazul struct-urilor din C:
nume_obiect.
VariabilaMembra = valoare , pentru accesul la o variabila membra
nume_obiect.
FunctieMembra() , pentru apelarea unei functii membre.
Exemplu in cazul de fata: Ionut.MG()Din nou, suntem nevoiti sa facem o observatie asupra unei greseli frecvent intalnite: incercarea accesarii unei variabile membre sau a unei metode cu drepturi de acces
private sau
protected exclusiv in exteriorul clasei
ESTE ILEGALA si, prin urmare, produce erori.
Exemplu: incercam sa accesam variabila membra corigent din
main():
if(Ionut.corigent) {...} ——>
GRESIT if(Ionut.este_corigent()) {...} ——>
CORECT Ar mai fi multe de spus in legatura cu supraincarcarea functiilor si a operatorilor, cu
relatia de prietenie dintre clase, dar acestea vor avea (i hope so

) cate un topic separat.
As usual, intrebari, nelamuriri, trageri de ureche
-> rise your hand here

. Cheers

!