O clasa in C++ se declara astfel:
class DenumireClasa [:[virtual][public/protected/private] baza1[,...]]
{
private:
//membrii privati
protected:
//membrii protejati
public:
//membrii publici
}[lista_variabile];
O clasa se declara asemanator cu o structura, folosing cuvantul rezervat class
. In plus, fata de structul din C putem specifica o lista de clase mostenite (de baza) dupa caracterul :
. Pe langa membrii de tip date putem avea si functii membre.
In C++, in interiorul unei structuri pot aparea si membrii de tip functie, ceea ce nu este admis in C. Membrii unui tip struct
fara specificatori de acces in fata sunt automat publici, pe cand, in cazul clasei, ei sunt privati.
Fiecare membru al unei clase poate fi:
private
protected
public
Accesul membrilor este dat de specificatorul sau, pana cand se intalneste un alt specificator de acces. In Java, in fata fiecarui membru trebuie specificat de fiecare data tipul de acces, numiti modificatori de acces. Daca deasupra unui membru nu apare niciun specificator de acces acesta este implicit privat. Putem avea mai multe sectiuni date de diversi specificatori de acces. Oricare dintre specificatori poate lipsi atunci cand se declara o clasa.
Membrii private
sunt cunoscuti doar in interiorul clasei, adica atunci cand descriem, implentam clasa, deci nu sunt cunoscuti in exteriorul clasei.
Membrii protected
sunt cunoscuti in interiorul clasei si eventual in clase derivate; in exteriorul acestor clase nu sunt cunoscuti.
Membrii public
sunt cunoscuti in interiorul clasei si eventual in clase derivate si in exteriorul descrierii claselor, sub forma obiect.membru_public
sau DenumireClasa::membru_public static
.
De regula, campurile se declara fie private
fie protected
, iar metodele, o parte sunt private
sau protected
si restul (la care dam acces) sunt public
.
//Punct2D.h
class Punct2D
{
double m_x, m_y;
public:
void setX (int x);
void setY (int y);
double getX();
double getY();
double dist (Punct2D p2);
Punct2D::Punct2D();
Punct2D (double, double);
~Punct2D ();
};
Exista o conventie ca in fata datelor membre care nu sunt statice sa se puna prefixul m_
, iar pentru datele membre statice sa se puna prefixul s_
.
In limbajul C++, de regula, atunci cand descriem clasa pentru functiile membre nu se dau decat antetele. Descrierea se face, de regula, intr-un fisier cu extensia .h
, iar implementarea metodelor se face intr-un fisier cu extensia .cpp
.
In foarte multe dintre situatii, o clasa se implementeaza intr-un fisier avand numele clasei si extensia .h
, iar metodele se implementeaza intr-un fisier avand numele clasei si extensia .cpp
.
//Punct2D.cpp
void Punct2D::setX (double x)
{
m_x = x;
}
void Punct2D::setY (double x)
{
m_y = y;
}
void Punct2D::getX (double x)
{
return m_x;
}
void Punct2D::getY (double x)
{
return m_y;
}
double Punct2D::dist (Punct2D p2)
{
return sqrt((p2.m_x-m_x)*(p2.m_x-m_x) + (p2.m_y-m_y)*p2.m_y-m_y);
}
/*
* in functia dist:
* se calculeaaza distanta dintre obiectul curent si
* obiectul Punct2D primit ca argument
*/
Punct2D::Punct2D (double x, double y)
{
m_x = x;
m_y = y;
}
Punct2D::~Punct2D()
{}
Atunci cand implementam o clasa ne gandim ca o scriem pentru un obiect de tipul acelei clase. Acest obiect poarta denumirea de obiect curent. In implementarea functiei setX
, m_x
este campul obiectului curent.
//Source.cpp
void main()
{
double x,y;
cin >> x >> y;
Punct2D p1;
p1.m_x = x;
/*
* nu este posibil deoarece campul m_x este private
* desi p1.setX(x) este echivalent cu atribuirea
* daca m_x ar fi fost public, nu este niciodata
* recomandat ca un camp sa fie lasat public, iar
* pentru initializarea lui se scrie, daca este
* nevoie, un setter, adica o functie care face
* initializarea de camp.
*
* In momentul in care atribuirea se face pe loc
* nu avem control asupra ei,
* care poate altera mecanismul intern al obiectului
* si un eventual al unei functii poate duce la
* efecte nedorite, chiar terminarea executiei
* (exceptie netratata)
*/
p1.setX(x);
p1.setY(y);
cout << p1.dist(p2);
}
Atunci cand descriem o clasa avem acces la membrii privati si la cei protejati ai obiectelor instanta claselor respective. Acest lucru se observa la implementarea fucntiei dist
unde am putut accesa datele lui p2
sub forma p2.m_x
si respectiv p2.m_y
, fara a fi nevoie sa apelam la functiile getX
, respectiv getY
.
Constructorul este un membru special al clasei. Se implementeaza asemanator cu o functie membra. Atunci cand se creeaza un obiect se apeleaza automat unul dintre constructorii clasei. Identificarea constructorului apelat se face pe baza numarului de parametri si al tipurilor de date ale parametrilor.
Orice clasa C++ are cel putin doi constructori. In momentul in care nu redactam niciun constructor, la compilare se genereaza un constructor implicit fara parametri, care are corpul vid.
In cazul clasei Punct2D
a fost creat acest constructor fara parametri. Daca programatorul nu scrie constructori de copiere se creeaza automat constructorul implicit de copiere folosit in clonarea obiectelor.
Un constructor se recunoaste usor prin faptul ca poarta numele clasei si nu are tip returnat, nici macar void
.
In cazul clasei Punct2D
este creat automat constructorul Punct2D();
Un obiect instantei unei clase poate fi creat atat cu alocare statica de memorie, cat si cu alocare dinamica de memorie. In exemplul anterior, Punct2D p1
reprezinta creearea unui obiect cu alocare statica de memorie. Acest obiect se distruge automat la parasirea functiei in care a fost construit sau la parasirea aplicatiei, in cazul unui obiect creat global.
Scrierea Punct2D p1
;nu este echivalenta cu Punct2D p1();
Atunci cand se foloseste constructorul fara parametri, dupa obiect nu punem ()
. In cazul unui constructor cu parametri, dupa numele obiectului intre ()
se enumera valorile transmise constructorului.
Punct2D p(1,2);// instantierea se face folosind al doilea constructor
Pentru a instantia o clasa cu alocare de memorie folosim operatorul new
astfel:
Punct2D *p1 = new Punct2D; //constructor fara parametri
Punct2D *p2 = new Punct2D(1,2); //constructor cu parametri
Daca o clasa C++ poate avea mai multi constructori (oricati), clasa C++ nu are decat un singur destructor, ce este apelat inainte de eliberarea memoriei ocupate de obiect.
Destructorul clasei se recunoaste prin faptul ca poarta numele clasei precedat de semnul ~
, nu are parametri si nu are tip returnat. Daca nu redactam explicit destructorul clasei, se creeaza destructorul implicit care are corpul vid.
La parasirea functiei in care a fost creat un obiect cu alocare statica de memorie se apeleaza automat destructorul clasei (cand obiectul paraseste stiva de executie). Atunci cand un obiect a fost alocat cu alocare dinamica de memorie putem distruge acel obiect sub forma delete p
. Evident, daca nu distrugem un obiect creat cu alocare dinamica de memorie, la parasirea aplicatiei vom avea memory leak.
In scrierea Punct2D *p = new Punct2D[1,2];
se creeaza un vector de Punct2D. Pentru fiecare obiect din acest vector se apeleaza cate un constructor, apeland constructorul fara parametri (daca exista). Prin delete[]p;
se apeleaza destructorul primului obiect si se distruge primul obiect, apoi al doilea...