Clase
Clasele reprezintă tipuri de data abstracte, asemănătoare structurilor, care încapsulează comportamentul şi datele asociate unei entităţi. Comportamentul este descris cu ajutorul metodelor (funcţii incluse), iar datele cu ajutorul atributelor. O instanţă (realizare concretă) a unei clase se numeşte obiect. Sintaxa utilizată pentru definirea claselor este asemănătoare cu cea pentru structuri:
class nume_clasa
{
//
atribute si metode
};
Obiectele pot fi create ca orice variabilă cu sintaxa: nume_clasa nume_obiect;.
Metodele unei
clase sunt similare funcţiilor obişnuite, cu două excepţii:
Accesul din
exterior la atributele şi metodele unei clase se face folosind operatorul
„.” (sau „->” în cazul
pointerilor).
Atributele şi metodele unei clase pot fi grupate din punctul de vedere al drepturilor de acces in trei categorii:
O clasă
poate oferi acces nerestricţionat la membrii proprii prin utilizarea
cuvântului cheie friend în una
din formele:friend prorotip_funcţie; sau friend nume_clasă;.
Controlul
accesului permite dezvoltatorului clasei limitarea modului de utilizare al
clasei şi ascunderea implementării interne a operaţiilor. Prin
aceasta cel care implementează clasa are posibilitatea de a modifica modul
în care sunt stocate intern datele sau sunt implementate operaţiile
fără a afecta aplicaţiile deja construite.
Modificatorii de
acces pot fi utilizaţi atât in cadrul claselor cât şi în cadrul
structurilor. Accesul este implicit public
pentru structuri şi private
pentru clase.
Metodele pot fi
definite atât în interiorul clasei (inline) cât şi în afara acestora,
folosind operatorul de rezoluţie „::”.
Exemplu – Clasa număr complex:
#include <iostream>
#include <cmath>
using namespace
std;
// definire clasa
class Complex
{
public: // sectiunea publica (accesibila din exterior)
// definire metode inline
double& getReal() { return
real; }
double& getImag() { return
imag; }
// declarare metoda definita in afara clasei
double Modul();
private: // sectiunea privata
// definire atribute
double real, imag;
};
// definire metoda in afara clasei
double Complex::Modul()
{
return sqrt(real*real + imag*imag);
}
void main()
{
// definire obiect
Complex c1;
// apelare metode pentru setarea valorii
c1.getReal() = 4;
c1.getImag() = 3;
cout <<
"Modulul este " << c1.Modul() << endl;
}
Constructorii
sunt funcţii speciale apelate automat de compilator la crearea unui nou
obiect. În cazul în care nu există nici un constructor definit de
către programator, compilatorul va sintetiza un constructor implicit
fără parametri.
Constructorii
sunt metode ale clasei cu următoarele caracteristici:
Rolul
constructorilor este de a iniţializa atributele obiectului. O clasă
poate avea mai mulţi constructori care să difere prin numărul şi/sau
tipul parametrilor.
Spre deosebire de
funcţiile obişnuite, constructorii pot avea o listă de
iniţializare pentru atributele clasei de forma:
constructor(param
constructor) : atribut_1(param), …, atribut_n(param) {…}
Exemple de constructori pentru clasa prezentată anterior:
class Complex
{
public: // sectiunea publica (accesibila din exterior)
// constructor implicit
Complex() { real
= 0; imag = 0; }
// constructor folosind lista de initializare
Complex(double r, double i) :
real(r), imag(i) { }
...................
};
// definire obiect
Complex c1(4, 3);
Constructorii de
copiere sunt constructori care primesc ca parametru o referinţă la un
obiect din aceeaşi clasă. În cazul în care nu există un asemenea
constructor, compilatorul va genera un constructor de copiere care va copia bit
cu bit atributele.
Exemplu de constructor de copiere pentru clasa complex:
class Complex
{
public: // sectiunea publica (accesibila din exterior)
.................................
// constructor de copiere definit in afara clasei
Complex(Complex&
c);
.................................
};
// definire constructor de copiere
Complex::Complex(Complex& c)
{
real = c.real;
imag = c.imag;
}
Constructorii de
copiere apelaţi în următoarele situaţii:
// apelare constructor de copiere
Complex c2 = c1;
// sau
Complex
c2(c1);
ATENŢIE: constructorii de copiere se apelează
numai la iniţializare, nu şi la atribuire; construcţia Complex c2; c2 = c1; nu va apela constructorul de
copiere.
void AfisareComplex(Complex c)
{
cout <<
"(" << c.getReal() << "," << c.getImag()
<< ")" << endl;
}
// trimitere ca parametru
AfisareComplex(c1);
Constructorii de
copiere sunt utili atunci când comportamentul implicit sintetizat de către
compilator nu este satisfăcător. Un astfel de caz sunt clasele care
conţin atribute pointeri. Constructorul de copiere implicit va copia doar
pointerul, nu şi zona de memorie adresată.
Destructorii sunt
metode speciale apelate de către compilator la ştergerea din memorie
a obiectului. Numele destructorilor este format din caracterul „~” şi numele clasei. Destructorii nu
au tip returnat şi nu pot primi parametri.
Exemplu:
// definire clasa
class Complex
{
public: // sectiunea
publica (accesibila din
exterior)
.................................
// destructor
~Complex()
{
// nu avem nimic
de facut
}
.................................
};
Destructorii de
folosesc pentru a elibera resursele alocate (dealocare zone de memorie alocate
dinamic, închidere fişiere, …) la distrugerea obiectului.
Clasa Complex (exemplul complet):
#include <iostream>
#include <cmath>
using namespace std;
// definire
clasa
class Complex
{
public: // sectiunea publica (accesibila din exterior)
// constructor implicit
Complex() { real = 0; imag = 0; }
// constructor folosind lista de
initializare
Complex(double r, double i) : real(r), imag(i) { }
// constructor de copiere definit in
afara clasei
Complex(Complex& c);
// definire metode inline
double& getReal() { return real; }
double& getImag() { return imag; }
// declarare metoda definita in
afara clasei
double Modul();
// destructor
~Complex()
{
// nu avem nimic de facut
}
private: // sectiunea privata
// definire atribute
double real, imag;
};
// definire
constructor de copiere
Complex::Complex(Complex&
c)
{
real = c.real;
imag = c.imag;
}
// definire
metoda in afara clasei
double Complex::Modul()
{
return sqrt(real*real +
imag*imag);
}
void AfisareComplex(Complex c)
{
cout << "(" << c.getReal() <<
"," << c.getImag() << ")" << endl;
}
void main()
{
// definire si initializare obiect
Complex c1(8, 10);
// apelare metode pentru setarea
valorii
c1.getReal() = 4;
c1.getImag() = 3;
// afisarea modulului
cout << "Modulul este " << c1.Modul()
<< endl;
// folosire constructor de copiere
Complex c2 = c1;
// trimitere ca parametru
AfisareComplex(c1);
}
Sa se
definească o clasa care sa încapsuleze principalele operaţii necesare
lucrului cu un vector alocat dinamic.
Vectorul are
dimensiune fixa specificata in constructor. Atributele necesare pentru
clasă vor fi un pointer către vectorul alocat dinamic şi
numărul de elemente al vectorului.
Operaţiile
implementate de clasă sunt:
Codul sursă complet, împreună cu un exemplu de utilizare:
#include <iostream>
using namespace
std;
class Vector
{
public:
// Constructor
Vector(int n);
// Constructor de copiere
Vector(Vector&
v);
// Destructor
~Vector();
// Intoarce dimensiunea vectorului
int getDim();
// Obtine o referinta la un element
// al vectorului
int& elem(int i);
private:
int *vector, dim;
};
// Construieste un vector nou
Vector::Vector(int n) :
dim(n)
{
// alocam memoria pentru vector
vector = new int[dim];
}
// Construieste un vector pe baza unui
// vector existent (pentru initializare
// sau trimitere de parametri prin
// valoare in functii)
Vector::Vector(Vector& v)
{
// construim noul vector
dim = v.dim;
vector = new int[dim];
// si copiem elementele
for (int i = 0; i
< dim; i++)
vector[i]
= v.vector[i];
}
// Elibereaza memoria alocata dinamic
Vector::~Vector()
{
// dezalocam memoria alocata
// in constructor
delete [] vector;
}
int Vector::getDim()
{
return dim;
}
int& Vector::elem(int
i)
{
return vector[i];
}
// Exemplu de functie pentru calculul mediei
double CalculMedie(Vector v)
{
int suma = 0;
for (int i = 0; i
< v.getDim(); i++)
suma +=
v.elem(i);
return (double)suma /
v.getDim();
}
void main()
{
// exemplu de utilizare clasa pentru calculul mediei
// citire dimensiune
int n;
cout <<
"n=";
cin >> n;
// creare vector si citire elemente
Vector v(n);
for (int i = 0; i
< n; i++)
{
cout
<< "Elementul " << i + 1 << ":";
cin
>> v.elem(i);
}
// calcul medie
cout <<
"Media este " << CalculMedie(v) << endl;
}
Dimensiunea
vectorului poate varia în timpul execuţiei programului. În acest caz vom
avea nevoie de un atribut suplimentar pentru a reţine zona de memorie
efectiv utilizată.
În plus
faţă de prima variantă au fost introduse următoarele
operaţii:
Codul sursă:
#include <iostream>
using namespace
std;
class Vector
{
public:
// Constructor
Vector(int n);
// Constructor de copiere
Vector(Vector&
v);
// Destructor
~Vector();
// Intoarce dimensiunea vectorului
int getDim();
// Operatii cu elemente
int& elem(int i);
void adaugaElement(int
val);
void stergeElemente(int
pozStart, int pozStop);
private:
// Rezerva memorie suplimentara pentru vector
void rezerva(int n);
int *vector, dimAlocata, dimUtilizata;
};
// Construieste un vector nou
Vector::Vector(int n = 0) :
dimUtilizata(n)
{
// alocam memoria pentru vector
dimAlocata = n;
vector = new int[dimAlocata];
}
// Construieste un vector pe baza unui
// vector existent (pentru initializare
// sau trimitere de parametri prin
// valoare in functii)
Vector::Vector(Vector& v)
{
// construim noul vector
dimUtilizata =
v.dimUtilizata;
dimAlocata =
v.dimAlocata;
vector = new int[dimAlocata];
// si copiem elementele
for (int i = 0; i
< dimUtilizata; i++)
vector[i]
= v.vector[i];
}
// Elibereaza memoria alocata dinamic
Vector::~Vector()
{
// dezalocam memoria alocata
// in constructor
delete [] vector;
}
int Vector::getDim()
{
return dimUtilizata;
}
int& Vector::elem(int
i)
{
return vector[i];
}
// Adauga un element la sfarsitul
// vectorului
void Vector::adaugaElement(int val)
{
// alocam memorie suplimentara
// daca este cazul
if (dimUtilizata == dimAlocata)
rezerva((int)(dimAlocata*1.2) + 1);
// si adaugam elementul
vector[dimUtilizata]
= val;
dimUtilizata++;
}
// Sterge elemente din vector de la
// pozitia pozStart (inclusiv) pana
// la pozitia pozStop (exclusiv)
void Vector::stergeElemente(int pozStart, int
pozStop)
{
// mutam elementele
for (int i = pozStop;
i < dimUtilizata; i++)
vector[i -
(pozStop - pozStart)] = vector[i];
// ajustam dimensiunea
dimUtilizata =
dimUtilizata - (pozStop - pozStart);
}
void Vector::rezerva(int n)
{
if (n > dimAlocata)
{
// alocam spatiu pentru
// noul vector
dimAlocata
= n;
int* temp = vector;
vector = new int[dimAlocata];
// copiem elementele semnificative
for (int i = 0; i
< dimUtilizata; i++)
vector[i]
= temp[i];
// stergem vechiul vector
delete [] temp;
}
}
// Exemplu de functie pentru calculul mediei
double CalculMedie(Vector v)
{
int suma = 0;
for (int i = 0; i
< v.getDim(); i++)
suma +=
v.elem(i);
return (double)suma /
v.getDim();
}
void main()
{
// exemplu de utilizare clasa
// citire dimensiune
int n;
cout <<
"n=";
cin >> n;
// creare vector si citire elemente
Vector v; // nu este necesar sa specificam dimensiunea
for (int i = 0; i
< n; i++)
{
cout
<< "Elementul " << i + 1 << ":";
int val;
cin
>> val;
// zona de memorie se va redimensiona automat
v.adaugaElement(val);
}
// calcul medie
cout <<
"Media este " << CalculMedie(v) << endl;
// stergere elemente
if (n > 2)
{
// stergem elementele 1 si 2
v.stergeElemente(1,
3);
// afisam vectorul
for (int j = 0; j
< v.getDim(); j++)
cout
<< v.elem(j) << " ";
cout
<< endl;
}
}