OOP ofera posibilitatea refolosirii
intr-un program a unei clase pe care am mai utilizat-o intr-un alt program,
economisind astfel timp si efort de programare. Sunt situatii in care o clasa
foloseste caracteristici ale unei clase existente, adaugand unul sau mai multe
date membre sau functii membre. In acest caz, C++ ofera posibilitatea de a crea
noul obiect pe baza caracteristicilor obiectului existent. Atunci cand o noua
clasa este creata pe baza uneia deja existente, noua clasa este o clasa
derivata.
Definitie:
Prin mostenire se intelege acea proprietate a claselor prin care o
clasa nou construita poate prelua datele si metodele unei clase mai vechi.
Clasei noi i se pot adauga date si metode. Acest mecanism mai este cunoscut sub
numele de „derivare a claselor”.
Avantajul
acestui procedeu este ca se poate prelua soft gata facut, care se dezvolta.
Persoana care dezvolta softul nu trebuie sa cunoasca detaliile de implementare
ale acestuia, ci doar documentatia de utilizare.
Daca
avem clasele X1, X2,…Xn, putem construi clasa
Xn+1 care are, pe langa datele si metodele celor n clase enumerate,
propriile date si metode. Clasele X1, X2,…Xn
se numesc clase de baza, iar
clasa Xn+1 – clasa derivata.
Declaratia unei clase
derivate:
class NumeClasaDerivata
: modificator_acces NumeClasaDeBaza
{
//date si
metode specifice clasei derivate
}
Obiectele din clasa derivata vor avea acces atat la membrii din clasa de
baza cat si la membrii din clasa derivata.
Atentie daca si in clasa de baza si in clasa derivata doi membri (camp de data
sau metoda) se numesc la fel, in clasa derivata daca se va dori sa se faca
referire la membrul din clasa de baza se va folosi operatorul de rezolutie
precedat de numele clasei de baza, altfel referirea va fi in mod implicit la
membrul din clasa curenta (clasa derivata).
class C1 //clasa de baza
{public:
tip1
camp1;
metoda1();
………………..
};
class C2: public C1 //clasa derivata
{public:
tip1 camp1;
metoda1();
…………………….
};
void main()
{C2 ob;
ob.camp1; //acceseaza campul din clasa C2
ob.C1::camp1; //acceseaza campul din clasa C1
ob.metoda1(); //acceseaza metoda din clasa C2
ob.C1::metoda1(); //acceseaza metoda din clasa C1
}
Exemplu: Fie clasa Persoana ce are ca date membre numele,
prenumele si anul nasterii unei persoane. Vrem sa cream clasa Student care are,
pe langa datele persoanei, si facultatea, specializarea si anul la care
studiaza. Derivam clasa Student din clasa Persoana.
#include<iostream.h>
#include<conio.h>
/*****************************************/
class persoana //clasa de baza
{public:
char nume[20], prenume[20];
int an;
void afisare()
{cout<<endl<<"datele
persoanei "<<endl;
cout<<nume<<" "<<prenume<<endl;
cout<<"Are "<<2005-an<<"
ani"<<endl;
}
void citire()
{
cout<<"numele ";
cin>>nume;
cout<<"prenumele ";
cin>>prenume;
}
};
/**************************************************************/
class student: public persoana //clasa derivata
{public:
char fact[20],spec[20];
int
an;
void citire()
{cout<<"facultatea ";
cin>>fact;
cout<<"specializarea ";
cin>>spec;
}
void afisare()
{cout<<endl<<"datele
studentului "<<endl;
cout<<"facultatea "<<fact<<endl;
cout<<"specializarea "<<spec<<endl;
cout<<"anul "<<an<<endl;}
};
void main()
{clrscr();
persoana p1;
p1.citire();
cout<<"anul nasterii ";
cin>>p1.an;
p1.afisare();
student s1;
cout<<endl<<"citire date student:
";
s1.persoana::citire();
cout<<"anul nasterii ";
cin>>s1.persoana::an;
s1.citire();
cout<<"in ce an este studentul? ";
cin>>s1.an;
cout<<"datele din ambele clase
"<<endl;
s1.persoana::afisare();
s1.afisare();
getch();
}
Observatie: Atunci cand
se deriveaza o clasa din alta clasa, membrii privati ai clasei de baza sunt
accesibili clasei derivate prin intermediul metodelor publice oferite de clasa
de baza. Din aceasta cauza, o clasa derivata nu poate accesa direct un membru
privat al clasei de baza prin utilizarea operatorului punct.
In ceea ce
priveste folosirea (compilarea si editarea de legaturi) clasei derivate in
sensul programarii, clasa de baza si cea derivata pot apare in acelati fisier
sursa, sau pot fi declarate in fisiere diferite.
Probleme propuse:
1. Sa se
defineasca clasa complex ca si clasa de baza apoi o clasa vector care sa
mosteneasca clasa complex si care sa permita prelucrarea unui sir de numere
complexe. Cerinte:
-se citesc n
numere complexe
-sa se afiseze
numerele in format complex
-sa se
determine suma acelor n numere complexe
-sa se
determine suma a doi vectori de numere complexe
-
Indiferent de tipul derivarii, functiile membre ale
clasei derivate nu au acces in zona private a clasei de baza.
-
Prin derivare public, membrii publici si protected
ai clasei de baza isi pastreaza caracterul de public, respectiv protected
(derivarea publica, extinde domeniile public si protected si pentru membrii
derivatei)
-
Prin derivare protected, membrii public si protected
ai clasei de baza devin protected pentru clasa derivata
-
Prin derivare private, membrii public si protected
ai clasei de baza devin membri private in clasa derivata
-
Daca nu se specifica tipul accesului mostenit prin
derivare, se considera implicit private.
Modificator acces la membrii clasei de baza |
Accesul in clasa derivata (noul acces) dobandit prin mostenire publica |
Accesul in clasa derivata (noul acces) dobandit prin mostenire protejata |
Accesul in clasa derivata (noul acces) dobandit prin mostenire privata |
private |
private |
private |
private |
public |
public |
protected |
private |
protected |
protected |
protected |
private |
Observatie: Prin
derivarea private, se opreste posibilitatea de a transmite catre un urmas
dreptul de acces la clasa de baza, in cazul mai multor niveluri de derivare
ierarhica. Pentru primul nivel de derivare, zonele public si protected sunt
totusi accesibile. Prin derivarea protected, membrii public si protected ai
clasei derivate pot fi transmisi mai departe si altor clase, prin derivare
succesiva.
Constructorii si destructorii sunt
functii membre care nu se mostenesc.
La instantierea unui obiect din clasa derivata se apeleaza mai intai
constructorii claselor de baza, in ordinea in care acestia apar in lista din
declararea clasei derivate. La distrugerea obiectelor, se apeleaza intai
destructorul clasei derivate, apoi destructorii claselor de baza.
Transmiterea argumentelor unei functii
constructor din clasa de baza se face folosind o forma extinsa a declaratiei
constructorului clasei derivate, care transmite argumentele unui sau
mai multor constructori din clasa de baza.
In general, clasele utilizeaza
constructori definiti de programator. In cazul in care acestia lipsesc,
compilatorul genereaza automat un constructor implicit pentru clasa respectiva.
Acelasi lucru se intampla si in cazul constructorilor de copiere.
La instantierea unui obiect din clasa
derivata, o parte din valorile primite ca parametri folosesc la initializarea
datelor membru ale claselor de baza, iar restul initializeaza datele membru
specifice clasei derivate.
Fie clasa de baza B si D o clasa derivata
a ei. Noua clasa D isi are proprii ei constructori, impliciti si/sau expliciti.
Constructorii clasei D sunt responsabili si cu initializarea corecta a datelor
mostenite. In mod normal, el apeleaza la constructorul lui B, chiar daca noi nu
cerem acest lucru, dupa care completeaza clasa cu datele si functiile specifice
clasei D.
In cazul destructorului, lucrurile stau
similar, numai ca ele se deruleaza in ordine inversa. La crearea unui obiect
derivat, ordinea de executie este:
- constructor de baza
- constructor derivat
iar
la eliberarea memoriei:
- destructor derivat
- destructor de baza
Exemplu: Fie clasa Angajat
care mosteneste clasa Persoana. Observati definirea constructorului clasei
derivate!!!
class
Persoana {
char nume[20];
int varsta;
long tel;
public:
Persoana(char c[20],int v,long t)
{strcpy(nume,c);
varsta=v;
tel=t;}
void afis()
{cout<<nume<<"
"<<varsta<<"
"<<tel<<endl;}
~Persoana()
{cout<<"am apelat
destructorul clasei Persoana"<<endl;}
};
class
Angajat : public Persoana {
public:
char functia[15];
long sal;
int vechime;
Angajat(char c[20],int v,long t,char
f[15],long s,int vech):Persoana(c,v,t)
{strcpy(functia,f);
sal=s;
vechime=vech;}
void afis_angajat()
{afis();
cout<<functia<<" "<<vechime<<" "<<sal<<endl;}
~Angajat()
{cout<<"am apelat
destructorul clasei Angajat"<<endl;}
};
void
main()
{Persoana
p1("Popescu",30,422078);
p1.afis();
Angajat a1("Ionescu
B.",33,319968,"profesor",4000000,9);
a1.afis_angajat();
}
La distrugerea obiectelor, se va afisa:
am apelat destructorul clasei Persoana – pentru p1
am apelat destructorul clasei Persoana – pentru a1
am apelat destructorul clasei Angajat – pentru a1
Daca clasa derivata nu are constructori
expliciti, valorile sunt cele puse de constructorul din clasa de baza. Exista
un constructor de copiere implicit, care sta la baza conversiilor unui Angajat
intr-o Persoana.
O clasa poate sa mosteneasca mai multe
clase de baza, ceea ce inseamna ca toti membrii claselor de baza vor fi
mosteniti de clasa derivata. In aceasta situatie apare mecanismul mostenirii
multiple.
Exercitiu:
Se implementeaza ierahia de clase din figura.
#include
<iostream.h>
class baza1 {
protected:
int x;
public:
baza1 (int xx)
{x=xx;cout<<"Constructor
cls. baza1\n";
cout<<x<<'\n';}
~baza1()
{cout<<"Destructor
baza1\n"<<x<<'\n';}
void aratax()
{cout<<"x="<<x<<'\n';}
};
class baza2 {
protected:
int y;
public:
baza2 (int yy)
{y=yy;
cout<<"Constructor baza2\n"<<y<<'\n';}
~baza2()
{cout<<"Destructor
baza2\n";}
void
aratay(){cout<<"y="<<y<<'\n';}
};
class derivat: public baza1, public baza2 {
public:
derivat(int xx, int yy):baza1(xx),
baza2(yy)
{cout<<"Constructor
derivat\n"; cout<<x<<' '<<y<<'\n';}
~derivat()
{cout<<"Destructor
derivat\n"; cout<<x<<' '<<y<<'\n';}
int arata(){cout<<x<<'
'<<y<<'\n';}
void seteaza(int xx, int yy){x=xx; y=yy;}
};
void main()
{
derivat
obiect(7,8);/*Constructor cls. baza1 7 Constructor baza2
8 Constructor
derivat 7 8 */
obiect.arata(); // 7 8
obiect.seteaza(1,2);
obiect.aratax(); // x=1
obiect.aratay(); // y=2
obiect.arata(); // 1 2
/*
Destructor derivat 1 2 Destructor
baza2 2 Destructor baza1 1 */
}
Asa
cum ilustreaza exemplul, la declararea obiectului obiect de tipul derivat s-au
apelat constructorii claselor de baza
(baza1 si baza2), in ordinea in care apar in declararea clasei derivate: mai
intai constructorul clasei baza1, in care x este data membru protejata
(accesibila din clasa derivat); apoi constructorul clasei baza2 , in care y
este data membru protejata (accesibila din clasa derivat); apoi constructorul
clasei derivat care le incorporeaza pe acestea intr-un singur obiect. Clasa
derivat nu are date membre, ci doar metode.
Dupa
iesirea din blocul in care a fost declarata variabila
obiect,
se apeleaza automat destructorii, in ordine
inversa
apelarii constructorilor.
Exercitii:
1. Sa se implementeze ierarhia de clase din
figura alaturata, cu
membrii pe care ii considerati necesari.
2. Concepeti o ierarhie de clase a figurilor
geometrice.
Ca date membre pot fi considerate
pozitia, dimensiunile
si atributele de desenare (culoare, tip
linie). Metodele
vor permite operatii de afisare,
deplasare, stergere,
modificarea atributelor figurii. Clasa de
baza va avea
proprietatile generale ale oricarei
figuri: coordonatele
pe ecran si vizibilitate.
3. Din clasa matrice, sa se deriveze clasa
c_matrice, care
reprezinta o matrice de complecsi.
4. Sa se defineasca o clasa
coada care sa permita adaugarea si listarea elementelor. Sa se defineasca o
noua clasa lista care sa permita si inserarea si stergerea unui element. Sa se
concateneze 2 liste (se va supraincarca operatorul +). Sa se defineasca o noua
clasa, lista_circulara, care sa mosteneasca clasa lista si care sa treaca o
lista liniara in lista circulara si afiseaza o lista circulara.
5. Sa se defineasca o clasa
vector care va permite: citirea, afisarea si ordonarea unui vector. Sa se
defineasca o clasa multime derivata din vector care sa permita in plus operatii
cu multimi. Sa se supraincarce operatorul ! astfel incat un vector sa contina
numai elemente distincte.
6. sa se defineasca o clasa
segment apoi o clasa triunghi derivata. Sa se calculeze aria si perimetrul triunghiului.
7. Sa se defineasca o clasa
matrice care sa permita citirea, afisarea si calculul sumei elementelor de pe o
linie. Sa se defineasca o noua clasa graf_neorientat care sa permita
prelucrarea grafurilor neorientate memorate prin matrice: parcurgere in
adancime si conexitate.
8. sa se defineasca clasa sir
care are ca date membru sirul si lungimea lui si care sa permita urmatoarele
prelucrari: concatenarea a doua siruri, stergerea unui caracter de pe pozitia
k, inserarea unui caracter pe pozitia k, stergerea caracterului c.
9. definiti o clasa fisier cara sa permita
prelucrarea fisierelor text. Ca date membru vom avea numele fisierului, iar ca
metode urmatoarele:
a)determinarea numarului de caractere printabile
b) compararea a doua fisiere
c) concatenarea a doua fisiere
10. definiti o clasa numar_mare
care sa permita operatii cu numere mari: citirea , suma si produsul a 2 numere
mari
11. sa se defineasca o clasa
care contine ca membri un numar si descompunerea sa in factori primi si o metoda
ce realizeaza descompunerea numarului in factori primi. Sa se determine
produsul a doua numere descompuse in factori primi. Sa se determine cmmmc si
cmmdc a doua numere exprimat in factori primi. Sa se determine cmmmc si cmmdc a
n numere.
12. sa se defineasca clasa
matrice patratica rara unde pt elementele nenule se cunosc coordonatele si
valoare retinuta. Sa se afiseze elementele de pe linia x, coloana y, suma a
doua matrici, diagonala principala, secundara.
13. sa se defineasca o clasa
polinom care sa permita operatii specifice