Documente Academic
Documente Profesional
Documente Cultură
extindere
organizarea de resurse soft ca şi o
colecţie de obiecte, distincte şi
se bazează pe orientarea pe obiecte discrete, care înglobează atât structuri
de date cât şi funcţiile de prelucrare
ale acestora.
toate informaţiile
stare memorate
în interiorul ei.
Obiectul
- o entitate individualizată prin nume
- conţine o mulţime de date descriu proprietăţile şi nivelul acestora
funcţii definesc comportamentul.
- obiectele pot fi clasificate în mulţimi. O mulţime de obiecte de acelaşi fel constituie o clasă de
obiecte, descrisă prin modelul comun al obiectelor sale.
Clasa
- grup de obiecte care fac parte din ea
- o clasă este o descriere a proprietăţilor şi comportamentului unui tip obiect; o clasă poate fi
considerată ca un tip special de dată, iar obiectele sale ca variabile de acest tip.
- pentru identificarea claselor unui sistem trebuie luate în consideraţie următoarele aspecte:
¤ Există informaţii care trebuiesc stocate sau analizate? Dacă da, ele sunt posibile candidate
pentru o clasă.
¤ Există sisteme externe cu care va comunica sistemul construit? Dacă da, este normal să fie
luate în considerare la modelarea sistemului.
¤Există biblioteci sau componente din proiecte anterioare care pot fi folosite? În caz afirmativ
acestea conţin clase candidate.
¤ Sunt device-uri în sistem care vor trebui gestionate? Orice device conectat la sistem
înseamnă o clasă candidat care sa-l gestioneze.
¤ Există părţi organizaţionale în sistem? Va fi nevoie de clase în care să fie implementate.
Membrii
Funcţii sau metode
Aşa cum variabilele în programe sunt de tip global şi local sau după modul de alocare sunt statice şi
dinamice, analog, pentru a defini o anumită disciplină în manipularea claselor se asociază grupurilor
de elemente din clasă un specificator de control al cărui câmp de acţiune este anulat de un altul.
Specificatorii de control sunt:
• public – ceea ce înseamnă că elementul respectiv este accesibil oricărei clase externe;
• private – membrul este accesibil numai de funcţii din interiorul clasei sau de către funcţii prietene
(friend) ale clasei;
• protected – similar cu private dar accesul se extinde pentru funcţiile membre şi prietene derivate
din clasa respectivă.
Obs:
- Efectul unui specificator de acces dureaza pâna la urmatorul specificator de acces.
- Implicit, daca nu se declara nici un specificator de acces, datele sau functiile membre sunt de tip
private.
- O funcţie membră a unei clase are acces la toate datele membre din clasa respectivă, indiferent de
specificatorul de acces.
- La proiectarea unei clase se au în vedere următoarele aspecte:
• funcţiile membru să acopere întreaga gamă de prelucrări;
• între variabilele şi funcţiile membre să fie o concordanţă perfectă pentru a nu apare erori în
execuţia programelor ce folosesc clase.
- Întrucât o clasă este un tip de dată, declararea unui obiect se face asemănător oricărei declaraţii de
dată:
nume_clasă nume_obiect;
- Declararea unui obiect mai este numită şi instanţierea clasei, în sensul că se creează o instanţă a
acelei clase, o entitate concretă din mulţimea descrisă de clasa respectivă.
- Pentru a pune în evidenţă faptul că un membru aparţine unui obiect se utilizează formularea:
nume_obiect. nume_membru.
- O clasă conţine deci:
- o parte protejata/privata care asigura implementarea clasei;
- o parte publica care reprezintă interfaţa clasei respective cu celelalte secţiuni din program
are ca efect
Declararea obiectelor alocarea de spaţiu în memorie, la fel ca în cazul declarării
oricărei variabile.
nu este iniţializat
Pentru rezolvarea problemei iniţializării obiectelor există posibilitatea utilizării unor metode speciale,
numite constructori.
Problema încheierii ciclului de viaţă al obiectelor este rezolvată prin utilizarea unor metode speciale
numite destructori.
Constructorii si destructorii se declara si se definesc similar cu celelalte functii membre, dar prezinta
o serie de caracteristici specifice:
*) numele lor coincide cu numele clasei careia ii apartin; destructorii se disting de constructori prin
faptul ca numele lor este precedat de caracterul ~
*)nu pot returna nici un rezultat
*)nu se pot utiliza pointeri catre constructori sau destructori
*)constructorii pot avea parametri, destructorii insa nu. Un constructor fara parametri poarta
denumirea de constructor implicit.
*)in cazul in care o clasa nu dispune de constructori sau destructori, compilatorul de C++ genereaza
automat un constructor respectiv destructor implicit.
d) cu conversie de tip;
e) de copiere:
- definit de utilizator;
Obs: În cazul în care clasa prezintă constructori expliciţi valorile pentru iniţializare sunt transmise
acestuia la declararea obiectului, asemănător listei de parametri reali la apelul unei funcţii:
nume_clasă nume_obiect(lista_valori);
Ex1:
#include<math.h>
#include<iostream.h>
#include<conio.h>
class complex
{public:
float x,y,m; //datele clasei
void display();
float modul(); //metoda clasei
complex() //constructor fara parametri definit de catre programator
{cout<<endl<<"mesaj de la constructor"<<endl;}
};
Testand programul se afiseaza pe ecran de
float complex::modul()
trei ori mesajul: mesaj de la constructor,
{return sqrt(x*x+y*y);}
corespunzator fiecarei declarari a celor trei
instante ale clasei complex.
void complex::display()
{cout<<x<<"+"<<y<<"*i";}
complex q;
void main()
{complex q1, q2;
getch(); clrscr();
}
Urmatorul exemplu contine in plus inca un constructor definit de catre programator, constructor
ce are un parametru.
Ex2
#include<math.h>
#include<iostream.h>
#include<conio.h>
class complex
{public:
float x,y,m; //datele clasei
void display();
float modul(); //metoda clasei
complex()
{cout<<endl<<"mesaj de la constructor"<<endl;}
complex(float yy);
};
complex::complex(float xx)
{x=xx;
y=0;
}
float complex::modul()
{return sqrt(x*x+y*y);
}
void complex::display()
{cout<<endl<<x<<"+"<<y<<"*i";
cout<<endl;
}
complex q;
void main()
{cout<<"Obiectul global q=";
q.display();
complex q1;
cout<<"Obiectul local q1, retine valori reziduale";
q1.display();
complex q2(6.7); //se apleleaza constructorul cu un parametru
q2.display();
getch();
clrscr();
}
Urmatorul exemplu permite prin intermediul mecanismului de supraincarcare definirea unui
constructor cu doi parametri:
Ex3:
#include<math.h>
#include<iostream.h>
#include<conio.h>
class complex
{public:
float x,y,m; //datele clasei
void display();
float modul(); //metoda clasei
complex()
{cout<<endl<<"mesaj de la constructor"<<endl;}
complex(float yy);
complex(float xx, float yy);
};
complex::complex(float xx)
{x=xx;
y=0;
}
float complex::modul()
{return sqrt(x*x+y*y);
}
void complex::display()
{cout<<endl<<x<<"+"<<y<<"*i";
cout<<endl;
}
complex q;
void main()
{cout<<"Obiectul global q=";
q.display();
complex q1;
cout<<"Obiectul local q1, retine valori reziduale";
q1.display();
complex q2(6.7);
cout<<"q2=";
q2.display();
complex q3(1.2,1.3);
cout<<"q3=";
q3.display();
getch();
clrscr();
}
Obs:
Ca orice alta functie în limbajul C++, constructorii pot avea parametri impliciti , fiind numiti
constructori cu parametri impliciti.
Varianta constructorului cu parametri impliciti poate fi adoptata în toate cazurile în care
constructorul nu necesita argumente.
Daca toti parametrii unui constructor sunt impliciti, apelul constructorului are forma unei simple
declaratii .
Ex 4: Pentru clasa complex s-a definit un constructor cu parametri impliciti; din acest motiv s-a
putut face declaratia "complex q;"
#include<math.h>
#include<iostream.h>
#include<conio.h>
class complex
{public:
float x,y,m; //datele clasei
void display();
float modul(); //metoda clasei
float complex::modul()
{return sqrt(x*x+y*y);
}
void complex::display()
{cout<<endl<<x<<"+"<<y<<"*i";
cout<<endl;
}
void main()
{cout<<"Obiectul global q=";
q.display();
complex q1; //obiect neinitializat ; apelul nu se face in mod explicit
cout<<"Obiectul local q1 ";
q1.display(); La apelul explicit al constructorului:
complex q2(6.7); // obiect initializatt. complexq4=complex() evaluarea expresiei complex()
cout<<"q2="; conduce la:
q2.display(); - Crearea unui obiect temporar de tip complex (obiect cu o
complex q3(1.2,1.3); // obiect initializat. adresa precisa, dar inaccesibil);
cout<<"q3="; - Apelul constructorului pentru acest obiect temporar;
q3.display(); - Copierea acestui obiect temporar în q4.
complex q4=complex(); // constructorul este apelat în mod explicit.
q4.display();
getch();
clrscr();}
Constructori de copiere
Functia principala a unui constructor este aceea de a initializa datele membre ale obiectului
creat, folosind pentru aceasta operatie valorile primite ca argumente.
O alta forma de initializare care se poate face la crearea unui obiect este prin copierea datelor
unui alt obiect de acelasi tip.
Pentru o clasa, se poate defini un contructor de copiere, care sa permita copierea obiectelor.
constructorul de copiere pentru clasa nume_clasa, are, de obicei, prototipul:
Ex 5:
#include<math.h>
#include<iostream.h>
#include<conio.h>
class complex
{public:
float x,y,m; //datele clasei
void display();
float modul(); //metoda clasei
float complex::modul()
{return sqrt(x*x+y*y);
}
void complex::display()
{cout<<endl<<x<<"+"<<y<<"*i";
cout<<endl;
}
void main()
{complex q1(1.2,1.3);
cout<<"q1=";
q1.display();
complex q2=q1; //se apeleaza constructorul de copiere
cout<<"q2=";
q2.display();
complex q3=complex(q1); //se apeleaza constructorul de copiere
cout<<"q3=";
q3.display();
getch();
clrscr();
}
Destructori
Destructorii sunt metode ale claselor care actioneaza în sens invers, complementar, fata de
constructori.
Constructorii sunt folositi pentru alocarea memoriei, initializarea datelor membru sau alte
operatii (cum ar fi, incrementarea unui contor pentru instantele clasei).
Destructorul este apelat automat, la iesirea din blocul în care este recunoscut acel obiect.
Proprietatile destructorilor :
*)Destructorul nu are parametri si nu returneaza nici o valoare (antetul nu contine cuvântul cheie
void, iar în corpul destructorului nu apare instructiunea return;);
class complex
{public:
float x,y,m; //datele clasei
void display();
float modul(); //metoda clasei
complex(complex &ob)
{ cout<<endl<<"operatie de copiere ";
x=ob.x;
y=ob.y;
}
~complex ()
{cout<<endl<<"am apelat destructorul pentru"<<endl; //destructor
display();}
};
float complex::modul()
{return sqrt(x*x+y*y);
}
void complex::display()
{cout<<endl<<x<<"+"<<y<<"*i";
cout<<endl;
}
void main()
{complex q1(1.1,1.1);
cout<<"q1=";
q1.display();
complex q2(2.2,2.2);
cout<<"q2=";
q2.display();
complex q3(3.3,3.3);
cout<<"q3=";
q3.display();
// getch();
}
Funcţii inline alternativă la utilizarea excesivă a macro-definiţiilor din limbajul C.
Compilatorul înlocuieşte orice apel al unei funcţii inline cu codul acesteia. În felul acesta se elimină
operaţiile suplimentare de apel şi revenire din funcţie.
Utilizarea funcţiilor inline măreşte viteza de execuţie a programului prin evitarea operaţiilor pe stivă,
ce au loc în cazul apelurilor de funcţii obişnuite, dar plăteşte drept preţ pentru aceasta creşterea
dimensiunii codului executabil.
Declaraţia unei funcţii inline este formată din declaraţia unei funcţii obişnuite precedată de cuvântul
cheie inline:
Sintaxa pentru definiţia claselor permite declararea implicită a unei funcţii inline. O funcţie membră
a unei clase definită (nu doar declarată) în interiorul clasei este implicit funcţie inline:
Alt exemplu:
Metoda “tipareste” este declarată explicit, ea fiind doar declarată în cadrul clasei, iar în locul unde
este definită este prefixată de cuvântul cheie “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:
Fie exemplul următor:
În acest exemplu, funcţia set( ) din clasa Complex poate fi declaratã explicit inline :
Obs:
1) Metodele care nu sunt membru al unei clase nu pot fi declarate inline decât explicit.
2)Funcţiile inline 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 pot conţine instrucţiuni ciclice (while, for, do-while).
structura comportarea
1. folosirea claselor pentru unirea structurile de date şi a funcţiilor destinate manipulării lor;
2. folosirea secţiunilor private şi publice, care fac posibilă separarea mecanismului intern de interfaţa
clasei;
Obs:
1) Î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 nemembre ale clasei care
nu sunt de tip friend (sau nu aparţin unei clase friend a clasei respective).
2) 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:
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.
Cuvântul cheie “static” poate fi utilizat în prefixarea membrilor unei clase. Odată declarat “static”,
membrul în cauză are proprietăţi diferite, datorită faptului că membrii statici nu aparţin unui anumit
obiect, ci sunt comuni tuturor instanţierilor unei clase.
Cea mai frecventă utilizare a datelor membre statice este de a asigura accesul la o variabilă comună
mai multor obiecte, deci pot înlocui variabilele globale.
Datele membru statice, care sunt utilizate de către toate obiectele clasei, se definesc prin
specificatorul static astfel:
Exemplul 1:
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 apelul funcţiilor incs() şi incv() pentru obiectul x astfel:
Exemplul 2:
În următorul exemplu, se numără câte Puncte sunt utlizate la un moment dat în program, utilizând ca
dată comună pentru toate punctele ( întreaga clasă) Nr_Ob:
O funcţie membră de tip static se declară în interiorul clasei şi se defineşte în interiorul clasei sau în
afara acesteia, folosind operatorul de rezoluţie.
O funcţie membră statică are vizibilitatea limitată la fişierul în care a fost definită şi este
independentă de instanţele (obiectele) clasei.
Exemplu:
Fie, de exemplu clasa Task care conţine o dată membră statică şi o funcţie membră statică:
Apelul unei funcţii statice se poate face fie ca funcţie membră a unui obiect din clasa respectivă, aşa
cum apare în primul apel din funcţia fs2(), fie prin specificarea clasei căreia îi aparţine, folosind
operatorul de rezoluţie.
Obs:
Specificatorul static are în C++ două semnificaţii: aceea de vizibilitate restricţionată la nivelul
fişierului în care sunt definite variabilele sau funcţiile şi aceea de alocare statică, adică obiectele
există şi-şi menţin valorile lor de-a lungul execuţiei întregului program.
Funcţii şi Clase Prietene (Friend)
O funcţie este prietenă cu o clasă dacă are acces la datele membru private ale acelei clase. O
funcţie prietenă poate fi o funcţie globală sau chiar o funcţie membru a altei clase.
O clasă este prietenă cu o altă clasă dacă ea are acces la datele membru ale acesteia.
O funcţie, respectiv o clasă prietenă se declară utilizând cuvântul friend astfel:
a) friend Tip_funcţie Nume_funcţie ( Listă_parametri_formali ); // Funcţie friend globală
b) friend Tip_funcţie Nume_clasă::Nume_funcţie(Listă_par._formali);// Funcţie friend membru
c) friend Nume_clasă; // Clasă friend
Obs:
1) O funcţie de tip friend este în esenţă o funcţie standard, care nu este membră a clasei ci are numai
acces la membrii de tip privat ai acelei clase. Orice functie poate fi prietenă a unei clase, indiferent
de natura acesteia.
2) Declaraţia unei funcţii prietene se poate face oriunde în cadrul declaraţiei clasei şi oferă funcţiei
posibilitatea de a avea acces la oricare dintre membri.
4) Modificatorii de acces nu au nicio influenţă asupra funcţiilor prieten, de aceea ele pot fi specificate
oriunde în cadrul descrierii clasei. Deci declararea unei funcţii prietene se poate face oriunde în
cadrul declaraţiei clasei (atât in partea privata cat si in cea publica) si oferă funcţiei privilegiul de a
avea acces la oricare dintre membri.
Exemplul 1:
În acest exemplu funcţia Afişează va fi scoasă în afara clasei Complex şi va fi declarată ca funcţie
prieten.
#include <iostream.h>
class X
{
int u,v;
public:
X(int k, int q){u=k;v=q;}
int dif(){return u*u-v*v;}
};
void main()
{
X z(6,3);
int p=z.dif();
cout<<"p="<<p;
}
Se obţine pentru p valoarea 27, folosind funcţia membră, dif( ).
La acelaşi rezultat se va ajunge folosind funcţia friend dif( ) a clasei X, care nu mai este
funcţie membră a acestei clase.
#include <iostream.h>
class X
{
int u,v;
public:
X(int k, int q){u=k;v=q;}
friend int dif(X w);
};
int dif(X w){return w.u*w.u-w.v*w.v;}
void main()
{
X z(6,3);
cout<<dif(z);
}
Obs : Există două situaţii în declararea funcţiilor friend ale unei clase:
a) se specifică faptul că funcţia membră a unei clase este funcţie friend a unei alte clase. Mai
exact, se declară funcţia membră din prima clasă ca fiind de tip friend în a doua clasă.
Exemplu:
class Y;
// se predeclară clasa care conţine funcţii prietene ale unei alte clase X
class X
{
……………
void F1(Y &a); // funcţie membră a clasei X
…………….
};
class Y
{
………………
friend void X::F1(Y &a); //funcţie friend pentru clasa Y
………………..
};
b) se poate declara o întreagă clasă prietenă , în care toate funcţiile membre ale sale sunt funcţii
prietene ale unei alte clase.
Exemplu:
class Y;
class X
{
………………..
void F1(Y &a);
………………..
};
class Y
{
………………..
friend X;
// se declară clasa X ca prietenă a clasei Y(toate funcţile membre
// ale clasei X sunt funcţii friend pentru clasa Y)
………………..
};
Exemplul 3:
Sa se defineasca tipul abstract de date complex care sa aiba functii membru pentru:
- modul, argument
- accesul la partea reala si imaginara a obiectelor de tip complex
- afisarea obiectelor complexe
- constructori pentru initializare si copiere
Un numar complex se poate ridica la o putere intreaga folosind formula lui MOIVRE. Aceasta se
exprima prin relatia:
((r*cos(a)+i*sin(a)))n = rn * (cos(n*a)+i*sin(n*a))
unde:
#include <iostream.h>
Din cadrul functiei duplicate, care
class CRectangle { este prietena cu CRectangle, nu este
int width, height; posibil accesul asupra membrilor
public: width si height ale diverselor
void set_values (int, int); obiecte de tip CRectangle.
int area (void) {return (width * height);}
friend CRectangle duplicate (CRectangle);
}; De observat ca nici in declaratia lui
duplicate() nici in utilizarea lui
void CRectangle::set_values (int a, int b) { ulterioara din main() nu s-a
width = a;
height = b;
considerat ca functia duplicate() ar
} fi membra a clasei CRectangle.
int main () {
CRectangle rt, rtb;
rt.set_values (2,3);
rtb = duplicate (rt);
cout << rtb.area();
}
int main () {
CSquare sqr;
CRectangle rt;
sqr.set_side(4);
rt.convert(sqr);
cout << rt.area();
return 0;
}
Exemplul 5:
Obs:
Relatia de prietenie nu este tranzitiva. Daca o clasa A este prietena cu o clasa B, iar clasa B este
prietena cu o clasa C, aceasata nu inseama ca A este prietena cu C.
Relaţia permite constituirea unei noi clase, numită derivată (sau fiu) pornind de
la clase existente, denumite de bază (sau părinte).
O clasă care asigură proprietăţi comune mai multor clase se defineşte ca o clasă
de bază.
O clasă derivată moşteneşte de la una sau mai multe clase de bază toate
caracteristicile acestora, cărora le adaugă alte caracteristici noi, specifice ei.
Se spune că o clasă D moşteneşte o clasă A, dacă obiectele din clasa D conţin
toate atributele clasei A şi au acces la toate metodele acestei clase.
Dacă D moşteneşte A, atunci obiectele din D vor avea toate atributele şi acces la
toate metodele lui A, dar în plus: - D poate defini noi atribute şi metode;
- D poate redefini metode ale clasei de bază;
- metodele noi şi cele redefinite au acces la toate
atributele dobândite sau nou definite.
Deci prin derivare sunt construite clasele din aproape în aproape, organizându-
se pe niveluri de agregare.
Clasele agregate de pe nivelul k preiau operanzii şi funcţiile membre ale
claselor de pe nivelul k-1, care intră prin derivare în componenţa lor, precum şi
proprietăţile acestora.
În limbajul C++ este permisă moştenirea multiplă. Pentru a defini o clasă fiu ca
fiind derivată dintr-o clasă părinte (sau mai multe clase părinte), se procedează astfel:
2) O clasă derivată are acces la membrii clasei părinte care au fost definiţi ca fiind
publici sau protejaţi şi nu are acces la membrii privaţi. Prin derivare se construiesc
ierarhii de clase, deci din clasa fiu se pot deriva alte clase noi.
Exemplul 1:
Fie clasa Punct care implementează entitatea punct geometric. Aceasta are
atributele x şi y, care reprezintă coordonatele punctului în plan. Este inclusă o singură
metodă, care desenează punctul pe ecran.
class punct { int x,y;
public:
void deseneaza();
};
void punct::deseneaza();
{ //corpul nu este descris in acest exemplu
}
Fie clasa Cerc care implementează entitatea geometrică cerc. Aceasta este
descrisă prin coordonatele centrului cercului şi raza sa. Ca urmare clasa Cerc poate fi
derivată din clasa Punct, adăugând un nou atribut (raza) şi o nouă metodă, pentru
desenarea cercului.
Clasa Cerc moşteneşte clasa Punct, deci un obiect de tipul Cerc va avea ca
membri coordonatele x,y moştenite şi ca atribut propriu Raza.
Exemplul 2:
Se consideră un program care descrie organizarea personalului unei instituţii
fără folosirea claselor derivate. O clasă numită Angajat deţine date şi funcţii
referitoare la un angajat al instituţiei:
class Angajat{
char *nume;
float salariu;
public:
Angajat();
Angajat(char *n, float sal);
Angajat(Angajat& r);
void display();
};
Angajat::display(){
cout << nume << “ ” << salariu << endl;
}
Moştenirea multiplă: Este posibilă moştenirea din mai multe clase de bază. De
exemplu:
Evident, specificatorii de acces pot să difere (pot fi oricare din public, private,
protected).
O clasă de bază se numeşte bază directă dacă ea este menţionată în lista de
clase de bază.
O clasă de bază se numeşte bază indirectă dacă nu este bază directă dar este
clasă de bază pentru una din clasele menţionate în lista claselor de bază.
Într-o clasă derivată se moştenesc atât membrii bazelor directe cât şi membrii
bazelor indirecte.
De exemplu, se completează programul de descriere a organizării personalului
unei instituţii şi pentru alte categorii de personal (personal temporar, consultant,
secretară, director), prin adăugarea câte unei clase pentru fiecare categorie de personal.
Modul în care aceste clase se derivează din anumite clase de bază evidenţiază
relaţiile de ierarhie între categoriile pe care le reprezintă:
class Personal { /* … */ };
class Angajat : public Personal{ /* …*/ };
class Administrator : public Angajat { /* … */ };
class Director : public Administrator { /* … */ };
class Secretara : public Angajat { /* … */ };
class Temporar : public Personal { /* … */};
class Consultant : public Temporar, public Administrator
{ /* … */ };
Clasa (nodul) din vârful ierarhiei reprezintă categoria cea mai generală a
ierarhiei, iar toate celelalte moştenesc, direct sau indirect, din această clasă de bază,
fiind clase mai specializate.
Controlul accesului la membrii clasei de bază
Dacă specificatorul de acces din declaraţia unei clase derivate este public, atunci:
• Datele de tip public ale clasei de bază sunt moştenite ca date de tip public în
clasa derivată şi deci pot fi accesate din orice punct al domeniului de definiţie al clasei
derivate.
• Datele de tip protected în clasa de bază sunt moştenite protected în clasa
derivată, deci pot fi accesate numai de funcţiile membre şi friend ale clasei derivate.
Exemplu:
Diverse situaţii de acces la membrii clasei de bază din clasa derivată atunci când
specificatorul de acces este public:
class Base {
int a;
protected:
int b;
public:
int c;
void seta(int x){a = x; cout << "seta din baza\n";}
void setb(int y){b = y; cout << "setb din baza\n";}
void setc(int z){c = z; cout << "setc din baza\n";}
};
class Derived : public Base {
int d;
public:
void seta(int x) {
a = x; // eroare, a este private
}
void setb(int y) {
b = y;
cout << "setb din derivata\n";
}
void setc(int z) {
c = z;
}
};
}
Dacă specificatorul de acces din declaraţia clasei derivate este protected, atunci
toţi membrii de tip public şi protected din clasa de bază devin membri protected în
clasa derivată.
Bineînţeles, membrii de tip private în clasa de bază nu pot fi accesaţi din clasa
derivată.
Dacă specificatorul de acces din declaraţia clasei derivate este private, atunci
toţi membrii de tip public şi protected din clasa de bază devin membri de tip private în
clasa derivată şi pot fi accesaţi numai din funcţiile membre şi friend ale clasei derivate.
Bineînţeles, membrii de tip private în clasa de bază nu pot fi accesaţi din clasa
derivată.
Moştenirea multiplă:
O clasă poate avea mai multe clase de bază directe, dacă acestea sunt specificate
în declaraţia clasei.
În exemplul următor este prezentată clasa Derived care moşteneşte două clase
de bază, Base1 şi Base2.
class Base1 {
protected:
int x;
public:
Base1(int i) {
cout << "Constructor baza 1\n"; x = i;
}
~Base1() { cout <<"Destructor baza 1\n"; }
int getx(){return x;}
};
class Base2{
protected:
int y;
public:
Base2(int j){
cout << "Constructor baza 2\n"; y = j;
}
int gety() { return y;}
~Base2() { cout <<"Destructor baza 2\n"; }
};
class Derived : public Base1, public Base2 {
int d;
public:
Derived (int i, int j);
~Derived(){ cout << "Destructor derivata\n"; }
};
Derived::Derived(int i, int j): Base1(i), Base2(j){
cout << "Constructor derivata\n";
}
void fbm(){ În funcţia fbm() este creat
Derived obd(3,4); obiectul obd de clasă
cout << obd.getx() << " "<< obd.gety() << endl; Derived.
}
Exemplul 1:
int unu (int i)
In acest caz functia unu ( )
{return i;}
este redefinita folosind un
int unu (int i, int j)
numar diferit de parametrii
{return i+j;}
Exemplul 2:
int doi (int i) In acest caz functia doi ( )
{return i;} este supraincarcata folosind
int doi (float i) tipuri diferite de parametrii.
{return floor(i);}
Obs:
1) Nu pot fi supraincarcate doua functii care difera doar prin tipul rezultatului returnat
Deci urmatoarea "supraincarcare" a functiilor este gresita:
int trei (int i); float trei (int i);
Exemplu:
Considerăm ierarhia Persoana. Presupunem că dorim să adăugăm o metodă
semneaza(). Un student va semna adaugând prefixul "Student" iar un profesor va
semna adaugând prefixul "Profesor". Urmează ca fiecare dintre cele trei clase să aibă
propria sa metodă semneaza():
class Persoana
{
...
void semneaza();
};
class Student : public Persoana
{
...
void semneaza();
};
class Profesor : public Persoana
{
...
void semneaza();
};
void Persoana::semneaza()
{
cout << getNume() << endl;
}
Supraincarcarea operatorilor
Prin supraincarcarea operatorilor se extinde tipul obiectelor asupra carora se pot aplica
acesti operatori.
Se pot supraincarca aproape toti operatorii in C++.
Operatorii care pot fi supradefiniti sunt:
Următorii operatori nu se supraîncarcă:
• ([]) operatorul de selectare a unei componente membre într-o structură de tip articol;
• (*) operatorul de deferire a unei componente din clasă;
• (::) operatorul de selecţie a funcţiei membru dintr-o clasă; operatorul de tratare variabile globale;
• (?:) operatorul condiţional.
Alte restrictii la supraincarcarea operatorilor:
- Se pot supraincarca doar operatorii existenti; nu se pot crea noi operatori.
- Nu se poate modifica numarul de operanzi ai operatorilor limbajului (operatorii unari
nu pot fi supraincarcati ca operatori binari, si invers).
- Nu se poate modifica precedenta si asociativitatea operatorilor.
class complex
{public:
float x,y;
void display()
{cout<<x<<"+"<<y<<"*i";
cout<<endl;
}
complex operator+(complex z)
{complex s;
s.x=x+z.x;
s.y=y+z.y;
return s;
}
};
void main()
{clrscr();
complex a,b,aux;
cout<<"primul numar "<<endl;
cout<<"partea reala ";
cin>>a.x;
cout<<"partea imaginara ";
cin>>a.y;
cout<<"al doilea numar "<<endl;
cout<<"partea reala ";
cin>>b.x;
cout<<"partea imaginara ";
cin>>b.y;
aux=a+b;
cout<<endl<<"Suma celor doua numere "<<endl;
aux.display();
cout<<endl;
cout<<"primul numar"<<endl;
a.display();
cout<<endl<<"al doilea numar "<<endl;
b.display();
a=a+b;
cout<<endl<<"primul numar trece in suma celor doua:"<<endl;
a.display();
cout<<"cel de al doilea numar ramane neschimbat "<<endl;
b.display();
getch();
}
Exercitii propuse:
Sa se supraincarce operatorii: -, *, / pentru clasa complex
complex operator!()
{complex c;
c.x=x;
c.y=-y;
return c;
}
Exemplu:
class B1
{
//...
}
class B2
{
//...
}
class B3
{
//...
}
class D1 : B1
{
B2 b1;
B3 b2;
};
Din punct de vedere funcţional există totuşi diferenţe, cum ar fi, de exemplu, modul de
apel al metodelor claselor membre.
Obs2. Orice friend (funcţie sau clasă) a unei clase derivate are exact aceleaşi drepturi
şi posibilităţi de a accesa membrii clasei de bază ca oricare alt membru al clasei
derivate.
Alt exemplu moştenire- derivare clase:
Obs1. Cuvântul cheie const folosit dupa lista de parametri ai unei funcţii declară
funcţia membru ca şi funcţie “read-only” – funcţia respectivă nu modifică obiectul
pentru care este apelată.
Această tehnică este folosită de obicei atunci când se redefineşte o funcţie membru
într-o clasă derivată. Versiunea din clasa derivată apelează versiunea din clasa de bază
şi apoi efectuează celelalte operaţii necesare.