| Ad_Infinitum |
| Administrator |

 |
 |
| Data inscrierii: 23/Iun/2007 |
| Mesaje: 212 |
|
|
|
 |
 |
 |
|
Clase ºi obiecte
Un tip de date într-un limbaj de programare este o reprezentare a unui concept. De exemplu, tipul float din C++, împreunã cu operaþiile definite asupra acestuia (+, -, *, etc.) reprezintã o versiune a conceptului matematic de numere reale. Pentru alte concepte, care nu au o reprezentare directã prin tipurile predefinite ale limbajului, se pot defini noi feluri de date care sã specifice aceste concepte. Un program care defineºte modele de date strâns corelate cu conceptele conþinute în aplicaþie este mai concis, mai uºor de înþeles ºi de modificat.
O clasã este un tip de date definit de utilizator. O declarare a unei clase defineºte un tip nou care reuneºte date ºi funcþii. Acest tip nou poate fi folosit pentru a declara obiecte de acest tip, deci un obiect este un exemplar (o instanþã) a unei clase.
Forma generalã de declaraþie a unei clase care nu moºteneºte nici o altã clasã este urmãtoarea:
class nume_clasa {
date ºi funcþii membre private
specificator_de_acces
date ºi funcþii membre
specificator_de_acces
date ºi funcþii membre
…………………………………………….
specificator_de_acces
date ºi funcþii membre
} lista_obiecte;
Cuvântul-cheie class introduce declaraþia clasei (tipului de date) cu numele nume_clasa. Dacã este urmatã de corpul clasei (cuprins între acolade), aceastã declaraþie este totodatã o definiþie. Dacã declaraþia conþine numai cuvântul-cheie class ºi numele clasei, atunci aceasta este doar o declaraþie de nume de clasã.
Corpul clasei conþine definiþii de date membre ale clasei ºi definiþii sau declaraþii (prototipuri) de funcþii membre ale clasei, despãrþite printr-unul sau mai mulþi specificatori de acces. Un specificator_acces poate fi unul din cuvintele-cheie din C++:
public private protected
Specificatorii private ºi protected asigurã o protecþie de acces la datele sau funcþiile membre ale clasei respective, iar specificatorul public permite accesul la acestea ºi din afara clasei. Efectul unui specificator de acces dureazã pânã la urmãtorul specificator de acces. Implicit, dacã nu se declarã nici un specificator de acces, datele sau funcþiile membre sunt de tip private. De aceea, toate datele sau funcþiile declarate de la începutul blocului clasei pânã la inițialul specificator de acces sunt de tip private. Într-o declaraþie de clasã se poate altera specificatorul de acces ori de câte ori se doreºte: unele declaraþii sunt trecute public, dupã care se poate reveni la declaraþii private, etc. Diferenþa între tipurile de acces private ºi protected constã în modul în care sunt moºtenite drepturile de acces în clase derivate ºi va fi detaliat în secþiunea care se ocupã cu clasele derivate (secþiunea 5).
Declararea unor obiecte de tipul definit de clasã prin lista_obiecte este opþionalã.
Date ºi funcþii membre ale clasei
Datele declarate într-o clasã se numesc date membre ºi, de obicei, ele sunt protejate (private sau protected), dar existã ºi situaþii în care sunt declarate public. Nu se pot declara auto, extern sau register datele membre ale unei clase.
Funcþiile definite într-o clasã se numesc funcþii membre (sau metode ale clasei) ºi de obicei ele sunt de tip public, dar pot fi ºi protejate.
În exemplul urmãtor se considerã definiþia unui tip de date pentru reprezentarea numerelor complexe, clasa Complex.
Exemplul 2.1
#include <iostream.h>
class Complex{
double re;
double im;
public:
void init(){
re = 0;
im = 0;
}
void set(double x, dublu y){
re = x;
im = y;
}
void display(){
cout << re <<” “ << im << endl;
}
};
void main(){
Complex c1;
c1.init();
c1.display(); // afiseaza 0 0
c1.set(7.2, 9.3);
c1.display(); // afiseaza 7.2 9.3
}
Clasa Complex conþine douã date membre private, re ºi im de tip double ºi trei funcþii membre public, init(), set() ºi display().
În funcþia main() a programului de mai sus s-a declarat un obiect cu numele c1 de tipul (clasa) Complex. Pentru un obiect dintr-o clasã datã se poate apela absolut orice funcþie membrã a clasei, folosind operatorul punct de acces la un membru (al unei structuri, uniuni sau clase); deci se poate scrie: c1.init(), acest lucru însemnând apelul funcþiei init() pentru obiectul c1 din clasa Complex.
2.1.1Domeniul clasei. Operatorul de rezoluþie
O clasã defineºte un tip de date al cãrui nume este numele clasei, precum ºi un domeniu al clasei. În acelaºi timp, o clasã care nu este o clasã localã sau o clasã internã altei clase (acestea sunt descrise în subsecþiunea 2.8), are un domeniu de definiþie (este cunoscutã în acest domeniu) care pornește de la prima poziþie dupã încheierea corpului clasei ºi se întinde pânã la sfârºitul fiºierului în care este introdusã definiþia ei ºi al fiºierelor care îl includ pe acesta.
Datele ºi funcþiile membre ale clasei care nu sunt declarate public au, în mod implicit, ca domeniu de definiþie, domeniul clasei respective, adicã sunt faimoase ºi pot fi folosite numai din funcþiile membre ale clasei.
Datele ºi funcþiile membre publice ale clasei au ca domeniu de definiþie întreg domeniul de definiþie al clasei, deci pot fi folosite în acest domeniu.
Funcþiile unei clase pot fi definite în interiorul clasei, aºa cum sunt funcþiile init()set()ºi display() ale clasei Complex, sau pot fi declarate în interiorul clasei (prin declararea prototipului) ºi definite în exteriorul ei.
Pentru definirea unei funcþii în afara clasei (dar, bineînþeles în domeniul ei de definiþie) numele funcþiei trebuie sã fie calificat (însoþit) de numele clasei respective prin operatorul de rezoluþie (: . Sintaxa de definire a unei funcþii în exteriorul clasei este urmãtoarea:
tip_returnat nume_clasa::nume_functie(lista_argumente){
// trupul functiei
}
În domeniul de definiþie al unei clase se pot crea obiecte (instanþe) ale clasei. Fiecare obiect conþine câte o copie individualã a fiecãrei variabile a clasei respective (dacã nu este de tip static; acest caz va fi descris într-o subsecþiune urmãtoare) ºi pentru fiecare obiect se poate apela orice funcþie membrã publicã a clasei.
Accesul la datele membre publice sau apelul funcþiilor membre publice ale unui obiect se poate face utilizând un operator de selecþie membru: operatorul punct (.) dacã se cunoaºte obiectul, sau operatorul -> dacã se cunoaºte pointerul la obiect.
Datele ºi funcþiile membre protejate ale clasei (private sau protected) au ca domeniu de definiþie domeniul clasei respective ºi de aceea nu pot fi accesate decât prin funcþiile membre ale clasei.
Mai trebuie remarcat încã un înfățișare referitor la domeniul de definiþie al claselor.
Domeniul în care pot fi definite obiecte de tipul unei clasei este domeniul în care este definitã clasa. Înainte de definiþia clasei nu se pot defini sau declara obiecte din acea clasã. Acest lucru înseamnã, implicit, cã nici în trunchiul unei clase nu se pot declara obiecte de tipul aceleiaºi clase (deoarece nu s-a completat definiþia clasei).
Numele unei clase se poate declara (sau redeclara) folosind construcþia:
class nume_clasa;
Într-un domeniu în care a fost declarat numele unei clase se pot declara pointeri la clasa respectivã. De asemenea, în corpul unei clasei se pot declara pointeri sau referinþe la aceeaºi clasã.
În exemplul urmãtor sunt prezentate comentat mai multe situaþii referitoare la declaraþii ºi definiþii de clase, obiecte, date ºi funcþii membre ale acestora. Erorile sunt specificate chiar cu mesajul construit de compilator.
Exemplul 2.2
#include <iostream.h>
………………………………….
void K::set(int x){ // error:'K':is not a class
// or namespace name
a = x;
}
class K; // declaratie nume clasa K
K* pob1; // corect, pointer la K
K pb2; // error: 'pb2' uses undefined class 'K'
class K{
int a; // a este penis privat al clasei
K k; //error:'k':uses'K'which is being defined
K* pobk; // corect, pointer la K
public:
int b; // b este membru public al clasei
K(){}
K(K& r); // corect, contine o referinta la K
int get();// declaratie (prototip) functie membra
void set(int x);
};
// corect: definitii functii membre in
// domeniul de definitie al clasei
int K::get(){
return a;
}
void K::set(int x){
a = x;
}
K pob3; // corect, clasa K este definita
void fk(){
K ob4;
K ob5;
K* pob5 = &ob5;
ob4.b = 5; // corect, b este membru public
pob5->b = 6;
ob4.a = 2; // error:'a' : cannot access
// private member in class 'K'
ob4.set(2); // corect, functie publica
pob5->get();
}
2.1.2Pointerul this. Funcþii membre de tip const
Orice funcþie membrã apelatã pentru un obiect al unei clase primeºte un argument ascuns, pointerul la obiectul pentru care a fost invocatã funcþia, numit pointerul this. Într-o clasã X pointerul constant this este declarat implicit astfel:
X* const this;
Deoarece this este cuvânt cheie, el nu poate fi declarat explicit. De asemenea, fiind declarat implicit pointer constant, el nu poate fi modificat, dar poate fi folosit explicit.
În exemplul de mai sus, funcþiei init() i se oferă implicit pointerul la obiectul c1, cu numele this. Prin intermediul acestui pointer funcþia acceseazã datele membre ale obiectului c1 (instanþã a clasei Complex). Asignãrile din funcþia init() {re = 0; im = 0;} sunt asignãri ale datelor membre ale obiectului c1 accesate prin intermediul pointerului la acesta (cu numele this), primit ca argument implicit la apelul funcþiei. Acest lucru s-ar putea scrie mai explicit astfel:
void init(){
this->re = x;
this->im = y;
}
Dar, odatã acest mecanism stabilit ºi cunoscut, nu mai este nevoie sã fie scris de fiecare datã, deci nu se va întâlni niciodatã o astfel de utilizare a pointerului this. În schimb, pointerul this este întrebuințat în funcþii membre care manevreazã pointeri.
În general, la apelul unei funcþii în care un argument este de tip pointer la constantã sau referinþã la constantã, se interzice modificarea obiectului indicat sau referit astfel. Dar, pentru funcþiile membre ale claselor, folosirea unui pointer sau referinþã la constantã necesitã condiþii suplimentare faþã de folosirea acestora în funcþii nemembre. De exemplu:
#include <iostream.h>
class U{
int u;
public:
int get() {return u;}
void set(int x) {u = x;}
};
void fu1(const U* pu, int i){
pu->set(i);
pu->get();
}
void fu2(const U& r, int i){
r.set(i);
r.get();
}
void main(){
U ob;
fu1(&ob,2);
fu2(ob,3);
}
La compilarea acestui program se obþine de patru ori urmãtorul mesaj de eroare de compilare: 'get' : cannot convert 'this' pointer from 'const class U *' to 'class U *const'. Specificatorul const pentru argumentul formal de tip pointer (respectiv referinþã) la clasa U ale celor douã funcþii fu1() ºi fu2(), transformã tipul pointerului this transmis implicit acestor funcþii membre nestatice în “pointer constant la constantã”, adicã el are forma: const U* const this ºi se interzice accesul funcþiilor membre (în acest caz, funcþiile get() ºi set()) la obiectul indicat.
Dar, intenþia cu care se utilizeazã argumente de tip pointer (sau referinþã) la constantã în apelul funcþiilor este de a interzice modificarea obiectului, nu de a interzice accesul perfect la acesta. Suportul oferit de limbajul C++ pentru rezolvarea acestei probleme este de a declara de tip const acele funcþii care au dreptul de acces la un obiect indicat prin pointer sau referinþã la constantã.
Pentru clasa U, se poate declara de tip const funcþia get() ºi atunci obiectul poate fi accesat numai pentru citire atunci îndată ce este transmis ca argument pointer (sau referinþã) la constantã. Programul modificat aratã astfel:
#include <iostream.h>
class U{
int u;
public:
int get() const {return u;}
void set(int x) {u = x;}
};
void fu1(const U* ps, U* pd){
int i = ps->get(); // corect, get()const{….}
// poate accesa ob. const U* ps
ps->set(i); // eroare, set() {….} nu poate
// accesa obiectul const U* ps
pd->set(i); // corect, set() {…} poate
// accesa obiectul U* pd
i = pd->get(); // corect get()const{….}
// poate accesa obiectul U* pd
}
void main(){
U ob1, ob2;
fu1(&ob1,&ob2);
}
Se poate observa cã funcþia membrã get()const {...} poate accesa atât un obiect dat prin pointer la constantã (ps), cât ºi un obiect dat prin pointer normal (pd), în timp ce funcþia membrã set() {…} nu poate accesa decât obiecte date prin pointer natural (în sensul cã nu este pointer la constantã).
2.1.3Funcþii membre inline
Îm programarea folosind clase, se obiºnuieºte sã fie definite ºi apelate multe funcþii mici (cu numãr redus de instrucþiuni), ºi acest lucru poate realizează un cost ridicat de execuþie, datoritã operaþiilor necesare pentru rezervarea spaþiului în stivã necesar funcþiei, apoi pentru transferul argumentelor ºi returnarea unei valori. De multe ori este posibil ca aceste operaþii implicate în apelul unei funcþii sã depãºeascã timpul de execuþie util al funcþiei. Acestã problemã se rezolvã prin intermediul funcþiilor inline.
În general, o funcþie declaratã inline se schimbã la compilare cu corpul ei, ºi se spune cã apelul funcþiei se realizeazã prin expandare. În felul acesta se eliminã operaþiile suplimentare de apel ºi revenire din funcþie. Dezavantajul funcþiilor inline este acela cã produc creºterea dimensiunlor programului compilat, de aceea se recomandã a fi utilizate pentru funcþii de mărimi mici (maximum 3-4 instrucþiuni). În plus, mai existã ºi unele restrucþii privind funcþiile inline: ele nu pot fi declarate funcþii externe, deci nu pot fi utilizate decât în modulul de program în care au fost definite ºi nu sunt admise instrucþini ciclice (while, for, do-while). Atributul inline poate fi neglijat de compilator dacã funcþia nu poate fi tratatã astfel.
O funcþie membrã a unei clase definitã (nu doar declaratã) în interiorul clasei este implicit funcþie inline. Acest lucru înseamnã cã, de exemplu, funcþia init() din clasa Complex este implicit inline. O funcþie membrã definitã în afara clasei este implicit o funcþie normalã (nu este inline). Dar ºi o astfel de funcþie poate fi declaratã explicit inline . De exemplu, funcþia set() din clasa K:
inline void K::set(int x){
a = x;
}
Aceastã posibilitate de definire a funcþiilor inline în implementarea claselor face ca multiple apeluri de funcþii sã nu producã un cost suplimentar, aºa cum, în mod eronat, se considerã uneori. În toate instrucþiunile programului din Exemplul 2.1 descris mai sus, apelurile de funcþii se executã prin expandare ºi deci nu produc cost suplimentar.
2.1.4Pointeri la date ºi funcþii membre
Este posibil accesul la un membru al unei clase printr-un pointer care memoreazã adresa acelui membru. Un pointer la un membru poate fi obþinut prin infuzarea operatorului adresã & numelui acestui membru calificat cu numele clasei; de exemplu &X::m, pentru membrul m al clasei X. Se vor preciza modurile de definire ºi exploatare a pointerilor la membrii claselor în exemplul urmãtor.
Exemplul 2.3
Se defineºte o clasã W se acceseazã prin pointeri datele ºi funcþiile membre ale clasei W astfel:
#include <iostream.h>
class W{
int a;
public:
int b;
void seta(int x){a = x;}
void setb(int x){b = x;}
int geta(){return a;}
int getb(){return b;}
};
void main (){
W ob1, ob2;
ob1.setb(5);
int W::*pdm; // pdm este un pointer la o data
// membra de tip int a clasei W
W* pw = &ob2;
pw->setb(6);
pdm = &W::a; // eroare, a este privat
pdm = &W::b; // pdm indica data b a clasei W
cout << ob1.*pdm << “ ” ;
cout << pw->*pdm << endl; // afiseaza 5 6
void (W::*pfm)(int);// pfm este pointer la o
// functie membra a clasei W cu
// argument int si return void
pfm = &W::seta; // pfm indicã functia seta
(ob1.*pfm)(7); // ob1.a = 7
(pw->*pfm)(10); // ob2.a = 10
pfm = &W::setb; // pfm indicã functia setb
(ob1.*pfm)(8); // ob1.b = 8
(pw->*pfm)(11); // ob2.b = 11
cout << ob1.geta() << " ";
cout << ob2.getb() << endl; // afiseaza 7 8
cout << ob1.geta() << " ";
cout << ob2.getb() << endl; // afiseaza 10 11
}
Prin declaraþia int W::*pdm; pointerul pdm este definit ca un tip de “pointer la o datã de tip întreg a clasei W”. Un pointer definit ca tip printr-o astfel de declaraþie, poate indica (poate primi adresa) oricare datã membrã de tip întreg a clasei W care nu este protejatã. Din acest moment, pointerul poate fi folosit prin operatorul de selecþie membru .* pentru un obiect din clasa respectivã (ob1.*pdm), sau prin operatorul de selecþie ->* pentru un pointer la un obiect din clasa respectivã (pw->*pdm).
Prin declaraþia void (W::*pfm)(int); se defineºte pointerul pdf ca un tip de “pointer la o funcþie membrã a clasei W care are un argument de apel de tip int ºi returneazã un void”. Dupã aceastã definiþie, pointerul pfm poate primi adresa oricãrei funcþii care îndeplineºte condiþia datã (este o funcþie membrã a clasei W, are un argument de apel de tip int ºi returneazã un void). De exemplu, poate primi adresa funcþiei seta() prin asignarea: pfm = &W::seta; Din acest moment, funcþia seta() poate fi apelatã prin pointerul sãu pfm întrebuințând operatorul de selecþie .* pentru un obiect din clasa W ((ob1.*pfm)(7); ) sau operatorul de selecþie ->* pentru un pointer la un obiect din clasa W ((pw->*pfm)(10);).
2.1.5Încapsularea datelor
Aparent, accesul la datele sau funcþiile membre ale unei clase s-ar putea rezolva modest prin declararea de tip public a datelor care se doresc a fi accesate din orice punct al domeniului de definiþie al clasei. Într-adevãr, urmãtoarea implementare este posibilã:
class Complex{
public:
double re;
double im;
//…………
};
void fc1(){
Complex c1;
c1.re = 5.6; // nu apare eroare de compilare
c1.im = 7.9;
}
Dar o astfel de implementare nu respectã principiul încapsulãrii datelor ºi este recomandat sã fie evitatã.
Problema ce înseamnã o clasã bine definitã are mai multe aspecte. Din punct de vedere al dreptului de acces la membrii clasei, o clasã bine definitã îngăduie încapsularea (sau ascunderea informaþiilor), prin care un obiect poate ascunde celor care-l folosesc “secretele” sale, adicã modul de implementare, prin interzicerea accesului la datele ºi funcþiile private sau protected.
În general, un obiect (instanþã a unei clase) are o stare, datã de totalitatea variabilelor sale, ºi o comportare, reprezentatã de funcþiile pe care le poate executa. Dispoziția unui obiect variazã în cursul existenþei acestuia ºi depinde de desfãºurarea în timp a tuturor funcþiilor pe care le-a executat.
Încapsularea prevede o barierã explicitã în calea accesului la starea unui obiect. Conform acestui principiu al încapsulãrii, asupra unui obiect se poate acþiona numai prin funcþiile pe care acesta le pune la dispoziþie în interfaþã ºi care sunt de tip public.
Încapsularea este procesul de separare a elementelor unui tip de date abstract (clasã) în douã pãrþi: structura, datã de implementarea acestuia, ºi comportarea sa, accesatã prin interfaþã. Implementarea constã din definirea datelor ºi a funcþiilor membru, iar interfaþa constã din declaraþiile funcþiilor membru de tip public.
Ascunderea informaþiilor este conceputã în C++ pentru prevenirea accidentelor nu a fraudelor. Nici un limbaj de programare nu poate interzice unei persoane sã “vadã” implementarea unei clase, dar poate interzice unei funcþii din program sã citeascã date la care nu are dreptul de acces. (Un sisteme de operare poate, totuºi, sã interzicã accesul de citire la unele fiºiere, deci asccunderea sã fie realã, chiar pentru persoane, nu numai pentru funcþii din program.)
Revenind la modul în care se pot accesa datele membre ale unei clase, se poate remarca cã, în general, respectând principiul încapsulãrii, datele membre sunt declarate private sau protected ºi nu pot fi accesate direct (pentru citire sau scriere) din funcþii nemembru ale clasei care nu sunt de tip friend (sau nu aparþin unei clase friend a clasei respective). Pentru citirea sau modificarea unora dintre datele membre protejate în clasa respectivã se pot prevedea funcþii membre de tip public, care pot fi apelate din orice punct al domeniului de definiþie al clasei ºi fac parte din interfaþa clasei.
De exemplu, pentru clasa Complex, o implementare care respectã principiul încapsulãrii, dar, în acelaºi timp permite accesul la datele private ale clasei poate arãta astfel:
#include <iostream.h>
class Complex{
double re;
double im;
public:
void init(){
re = 0;
im = 0;
}
void set(double x, double y){
re = x;
im = y;
}
void setre(double x){re = x;}
void setim(double y) {im = y;}
double getre(){return re;}
double getim(){return im;}
void display();
};
inline void Complex::display(){
cout << re << “ ” << im << endl;
}
void main(){
Complex c1;
c1.set(7.2, 9.3);
c1.display(); // afiseaza 7.2 9.3
c1.setre(1.3);
c1.setim(2.8);
c1.display(); // afiseaza 1.3 2.8
}
Datele membre ale clasei (re ºi im) sunt date de tip private, iar accesul la acestea este posibil prin intermediul funcþiilor membre publice set(), setre(), setim(), etc.
Într-o astfel de implementare, în care majoritatea funcþiilor sunt inline (este posibil în acest caz simplu, cu funcþii de dimensiuni mici) nu apar decât apeluri de funcþii prin expandate, deci implementarea este atât elegantã cât ºi eficientã.
2.1.6Date ºi funcþii membre de tip static
O datã membrã a unei clase poate fi declaratã static în declaraþia clasei. Va exista o singurã copie a unei date de tip static, care nu aparþine nici unuia dintre obiectele (instanþele) clasei, dar este partajatã de toate acestea. Declaraþia unei date de tip static într-o clasã este doar o declaraþie, nu o definiþie ºi este necesarã definiþia acesteia în altã parte în program (în afara clasei). Aceasta se face redeclarând variabila de tip static folosind operatorul de rezoluþie pentru clasa cãreia îi aparþine, ºi fãrã specificatorul static.
O variabilã membru de tip static a unei clase existã înainte de a fi realizat un obiect din clasa respectivã ºi este iniþializatã cu 0. Cea mai frecventã utilizare a datelor membre statice este de a asigura accesul la o resursã comunã mai multor obiecte, deci pot înlocui variabilele globale.
Exemplul 2.4
Se considerã o clasã S care conþine o variabilã normalã v ºi o variabilã staticã s. Data de tip static este declaratã în clasa S ºi definitã în afara acesteia, folosind operatorul de rezoluþie. Se poate urmãri evoluþia diferitã a celor douã variabile v ºi s prin crearea a douã obiecte x ºi y de tip S, ºi prin aplelul funcþiilor incs() ºi incv() pentru obiectul x astfel:
class S{
int v;
static int s; // declaratia var. statice s
public:
S() { v = 0;}
int gets() {return s;}
int getv() {return v;}
void incs() {s++;}
void incv() {v++;}
};
int S::s; // definitia var. statice s a clasei S
void main (){
cout << “Inainte de incrementare\n”;
cout <<“x.s: “<<x.gets()<<“y.s: “<<y.gets()<< endl;
cout <<“x.v: “<<x.getv()<<“y.v: “<<y.getv()<< endl;
x.incs();
x.incv();
cout << “Dupa incrementare\n”;
cout <<“x.s: “<<x.gets()<<“y.s: “<<y.gets()<< endl;
cout <<“x.v: “<<x.getv()<<“y.v: “<<y.getv()<< endl;
}
La execuþia acestui program se afiºeazã conþinutul variabilelor s ºi v pentru obiectele x ºi y. Mesajele afiºate sunt urmãtoarele:
Inainte de incrementare
x.s: 0 y.s: 0
x.v: 0 y.v: 0
Dupa incrementare
x.s: 1 y.s: 1
x.v: 1 y.v: 0
Diferenþa între comportarea unei date membre de tip static faþã de o datã normalã este evidentã: dupã incrementarea variabilei s pentru obiectul x, cele două obiecte x ºi y vãd aceeaºi valoare a variabilei statice s.
Funcþiile membre ale unei clase pot fi, de asemenea, declarate de tip static. La fel ca ºi datele membre statice, o funcþie membrã de tip static se declarã în interiorul clasei ºi se defineºte în afara acesteia, folosind operatorul de rezoluþie pentru clasa respectivã. O funcþie membrã staticã are vizibilitatea limitatã la fiºierul în care a fost definitã ºi este independentã de instanþele (obiectele) clasei. Fiind independentã de obiectele clasei, o funcþie staticã nu primeºte pointerul this, chiar dacã apelul se face pentru un obiect al clasei respective.
Fie, de exemplu clasa Task care conþine o datã membrã staticã ºi o funcþie membrã staticã:
class Task{
static Task *chain; // declaratie data statica
public:
static void schedule(int);// decl. functie statica
};
Task *Task::chain=0; // definirea datei statice
void Task::schedule(int p){ // definire func. statica
Task::chain = 0;
}
void fs2(){
Task T1;
T1.schedule(4);
Task::schedule(2);
}
Apelul unei funcþii statice se poate face fie ca funcþie membrã a unui obiect din clasa respectivã, aºa cum apare în întâiul apel din funcþia fs2(). În aceastã situaþie se foloseºte doar tipul obiectului T1 pentru apel, nu obiectul în sine, ºi nici pointerul acestuia nu este transmis implicit (ca un pointer this) funcþiei schedule(). Compilatorul chiar dã un mesaj de atenþionare (warning): 'T1' : unreferenced local variable.
Altã restricþie referitoare la funcþiile membre statice este aceea cã ele nu pot avea acces decât la datele statice ale clasei ºi la datele ºi funcþiile globale ale programului.
Se poate remarca faptul cã specificatorul static are în C++, ca ºi în C, douã semnificaþii: aceea de vizibilitate restricþionatã la nivelul fiºierului în care sunt definite ºi aceea de alocare staticã, adicã obiectele existã ºi-ºi menþin valorile lor de-a lungul execuþiei întregului program.
Clase, structuri ºi uniuni
În C++ structurile au o funcþionaliate foarte apropiatã de cea a claselor: ele definesc soiuri de date noi, încuviințează gruparea de date ºi funcþii, permit moºtenirea. De fapt, singura diferenþã între clase ºi structuri în C++ este aceea cã, implicit, toþi membrii unei structuri sunt de tip public.
Se poate verifica uºor acest lucru, chiar pe exemplul din aceastã secþiune. Dacã se înlocuieºte cuvântul cheie class cu cuvântul cheie struct ºi se introduce specificatorul de acces private care sã modifice tipul implicit de acces la date, se obþine acelaºi program, cu aceeaºi funcþionare:
#include <iostream.h>
struct Complex{
private:
double re;
double im;
public:
void init(){
re = 0;
im = 0;
}
void set(double x, double y){
re = x;
im = y;
}
void Setre(double x){re = x;}
void Setim(double y) {im = y;}
double getre(){return re;}
double getim(){return im;}
void display();
};
inline void Complex::display(){
cout << re << “ ” << im << endl;
}
void main(){
Complex c1;
c1.set(7.2, 9.3);
c1.display(); // afiseaza 7.2 9.3
}
Se poate observa cã, spre deosebire de C, în C++ obiectele de tip structurã pot fi declarate folosind doar numele structurii, fãrã sã mai fie nevoie sã fie precedat de cuvântul-cheie struct.
Aceastã dublã posibilitate de definire a unor tipuri noi de date (prin clase ºi prin structuri) provine din modul în care a evoluat limbajul C++, pornind de la C. Structurile au fost pãstrate în C++ în primul rând pentru translatarea directã a programelor existente, din C în C++. Dacã structurile tot trebuie sã existe în C++, atunci adãugarea trãsãturilor suplimentare care sunt proprii claselor (funcþii membru, derivare, moºtenire, etc) este o problemã simplu de rezolvat la nivelul proiectãrii compilatoarelor, iar structurile C++ au devenit astfel mai puternice.
În sfârºit, existenþa în momentul de faþã a douã cuvinte-cheie pentru definirea tipurilor noi de date, permite evoluarea liberã a conceptului class, în timp ce conceptul struct poate fi pãstrat în continuare pentru asigurarea compatibilitãþii cu programele C deja existente.
Chiar dacã se poate folosi o structurã acolo unde se doreºte definirea unui tip de date abstract (clasã), o practicã corectã de scriere a programelor este consideratã aceea în care clasele sunt utilizate pentru definirea tipurilor de date noi, iar structurile sunt utilizate atunci când se doreºte o structurã de tip C.
Ca ºi structurile, uniunile (union) în C++ definesc tipuri noi ºi pot conþine atât date cât ºi funcþii membru, care sunt implicit publice. În acelaºi timp, o uniune C++ pãstreazã toate capacitãþile din C, printre care cea mai importantã este aceea cã toate datele împart aceleaºi locaþii de memorie.
Existã mai multe restricþii în utilizarea uniunilor în C++. În primul rând uniunile nu pot fi folosite în mecanismul de derivare a tipurilor de date, nici ca tipuri de bazã, nici ca tipuri derivate ºi deci, nu pot avea funcþii membru de tip virtual (acestea sunt legate de derivare ºi vor fi studiate în secþiunea 5). Deºi o uniune poate avea constructori, nu sunt admise date membre care au un constructor. De asemenea, nu pot fi membri variabile de tip static. |
|