You are on page 1of 9

Laboratorul nr.

12
Motenirea. Clase derivate
Prin procedeul de derivare se urmrete obinerea unor clase noi, numite clase derivate, care motenesc proprietile unei clase deja definite, numit clas de baz. Clasele derivate conin toi membrii clasei de baz, la care se adaug noi membrii, date i funcii membre. Clasa de baz nu este afectat de derivare, ea putnd fi compilat anterior eventualelor derivri. Dintr-o clas de baz se poate deriva o clas care, la rndul su, s serveasc drept clas de baz pentru derivarea altora. Prin aceast succesiune se obine o ierarhie de clase. Pornind de la clase simple i generale, fiecare nivel al ierarhiei adaug noi proprieti, obinndu-se clase cu un grad sporit de complexitate, devenind din ce n ce mai specializate. Se pot defini clase derivate care au la baz mai multe clase, nglobnd proprietile tuturor claselor de baz, procedeu ce poart denumirea de motenire multipl. Declararea clasei derivate Dup cum s-a vzut n lucrrile de laborator anterioare, sintaxa general de declarare a unui tip de date class este de forma: class <nume_clasa> <: lista_clase_baza> {<lista_membri>} <lista_variabile>; lista_clase_baza cuprinde unul (n cazul unei derivri simple) sau mai multe (n cazul unei derivri multiple) nume de clase, nsoite de cte un specificator de acces. Specificatorul de acces controleaz drepturile de acces la membrii clasei de baz. Deci lista claselor de baz are sintaxa urmtoare: specificator_acces clasa_baza_1, specificator acces clasa_baza_2, Specificatorii de acces ce se pot utiliza sunt public i private. Valoarea implicit este private. Atributele de acces la membrii motenii de clasa derivat, n funcie de specificatorul de acces folosit, sunt cele prezentate n tabelul urmtor: Atributul din clasa de baz private protected public private protected public Modificatorul de acces Private Accesul motenit de clasa derivat inaccesibil private private inaccesibil protected public Accesul din exterior inaccesibil inaccesibil inaccesibil inaccesibil inaccesibil accesibil

Public

Pentru a avea acces la membrii clasei de baz din clasa derivat, este necesar ca acetia s fi fost declarai protected sau public. Pentru a pstra dreptul de acces la membrii clasei de baz, este necesar s se foloseasc acces public. La stabilirea specificatorilor de acces se au n vedere perspectivele de dezvoltare a ierarhiei de clase, fr a se nclca principiul ncapsulrii datelor. Motenirea este asemntoare cu procesul de includere a obiectelor n obiecte (procedeu ce poart denumirea de compunere), dar exist cteva elemente caracteristice motenirii: - codul poate fi comun mai multor clase; - clasele pot fi extinse, fr a recompila clasele originare; - funciile ce utilizeaz obiecte din clasa de baz pot utiliza i obiecte din clasele derivate din aceast clas. Pag. 1/9

Programare C/C++ - Laborator nr. 12 n exemplul urmtor se va deriva clasa cerc din clasa punct. #include <iostream.h> class punct { protected:

// se folosete specificatorul de acces protected pentru a permite // accesul la datele membre ale clasei punct din clasele derivate, // dar asigurndu-se n continuare protecia fa de funciile // externe acestora

float x, y; public : punct(float a=0, float b=0) {x=a; y=b;} void modific_punct(float dx, float dy) {x+=dx; y+=dy;} }; class cerc : public punct // n declaraie se specific clasa de baz punct cu acces public { float raza; public: cerc(float r=0) {raza=r;} void afisare() {cout<<"\nCentru cerc: x="<<x<<"\ty="<<y; // accesul la membrii clasei punct se face // direct datorit specificatorului // protected din clasa punct i a // specificatorului public ataat clasei de baz cout<<"\tRaza="<<raza; } void modific_cerc(float dx, float dy, float dr) { x+=dx; // accesul la membrii clasei punct se face direct y+=dy; raza+=dr;} }; void main() { cerc c1; c1.modific_cerc(1.1, 2.2, 3.3); c1.afisare(); c1.modific_punct(2.5, 4.6); c1.afisare(); } Clasa derivat preia toate proprietile clasei de baz (membrii x, y, funciile punct() i modific_punct()), la acestea adugnd membrul raza, funcia cerc(), funcia modific_cerc() i funcia afisare(). Este permis atribuirea obiectelor derivate unor obiecte ale clasei de baz, deci n funcia main() a exemplului anterior se poate include secvena: punct p1; p1=c1; // se creeaz un obiect punct cu x=0 i y=0 // p1.x preia valoarea c1.x, iar p1.y preia valoarea c1.y Pag. 2/9

// accesul la funciile membre clasei punct prin // obiecte de tip cerc este permis

Programare C/C++ - Laborator nr. 12 Prin operaia de atribuire se preiau numai membrii clasei de baz. Regula de compatibilitate se poate extinde i la pointeri i referine de obiecte. punct * p_p; cerc * p_c; p_p=p_c; Constructori i destructori pentru clasa derivat La crearea unui obiect se apeleaz ntotdeauna constructorul clasei respective, iar declaraia obiectului trebuie s specifice valorile iniiale cerute de constructor. Distrugerea obiectului este precedat de apelul funciei destructor a clasei respective. Aceste reguli sunt valabile i n cazul claselor derivate. Pentru crearea unui obiect al unei clase derivate, se creeaz iniial un obiect al clasei de baz prin apelul constructorului acesteia, apoi se adaug elementele specifice clasei derivate prin apelul constructorului clasei derivate. Declaraia obiectului derivat trebuie s conin valorile de iniializare, att pentru elementele specifice, ct i pentru obiectul clasei de baz. Deci la definirea constructorului clasei derivate, este necesar s se specifice care sunt parametrii ce sunt preluai de constructorul clasei de baz. Aceast specificare se ataeaz la antetul funciei constructor a clasei derivate. #include <iostream.h> class punct {protected: float x, y; public : punct(float a, float b) { x=a; y=b; cout<<"\nConstructor punct: this="<<this; cout<<"\tx="<<x<<"\ty="<<y; } ~punct() { cout<<"\nDestructor punct: this="<<this; cout<<"\tx="<<x<<"\ty="<<y<<endl; } }; class cerc:public punct { float raza; public: cerc(float a, float b, float r):punct(a, b) { // se declar clasa cerc derivat din clasa punct

// n antetul constructorului se specific efectiv // care sunt parametrii transferai constructorului punct

raza=r; cout<<"\nConstructor cerc: this="<<this; cout<<"\traza="<<r<<endl;

} ~cerc() { cout<<"\nDestructor cerc: this="<<this; cout<<"\traza="<<raza; } }; Pag. 3/9

Programare C/C++ - Laborator nr. 12 void main() { cerc c1(1.1, 2.2, 10), c2(5, 10, 15); } La definirea constructorului clasei cerc, s-a specificat care dintre parametrii transmii sunt preluai de constructorul clasei punct. S-a inclus afiarea de mesaje, astfel nct s se poat urmri ordinea n care se fac apelurile funciilor. Se poate observa c, la crearea unui obiect cerc, se apeleaz nti constructorul punct i apoi constructorul cerc. De asemenea, se observ c adresa obiectului este unic, ea fiind asociat tuturor elementelor care alctuiesc un obiect cerc, deci ambii constructori, att punct(), ct i cerc() vor afia aceeai valoare. La eliminarea obiectelor cerc, apelul destructorilor se face n ordine invers, deci nti se apeleaz destructorul ~cerc() i apoi ~punct(). n situaia n care clasele de baz au definit constructor implicit sau constructor cu parametri implicii, nu se impune specificarea parametrilor care se transfer ctre obiectul clasei de baz. Constructorul de copiere pentru o clas derivat n ceea ce privete utilizarea constructorului de copiere pentru o clas derivat, se pot distinge mai multe situaii. Dac ambele clase, att clasa derivat ct i clasa de baz, nu au definit constructor de copiere, se apeleaz constructorul implicit creat de compilator. Copierea se face membru cu membru. n cazul n care clasa de baz are constructorul de copiere definit, dar clasa derivat nu, pentru clasa derivat compilatorul creeaz un constructor implicit care apeleaz constructorul de copiere al clasei de baz. Pentru clasa punct din exemplul anterior se poate include definirea constructorului de copiere: punct(punct &p) { x=p.x; y=p.y; cout<<"\nConstructor de copiere punct:"; cout<<"\tx="<<x<<"\ty="<<y; } n cazul n care se definete constructor de copiere pentru clasa derivat, acestuia i revine n totalitate sarcina transferrii valorilor corespunztoare membrilor ce aparin clasei de baz. Deci, pentru clasa cerc se include constructorul de copiere cu prototipul: cerc(cerc &); iar antetul este de forma: cerc::cerc(cerc &c) : punct(c.x, c.y) cerc::cerc(cerc &c) : punct(c.x, c.y) { raza=c.raza; cout<<\nConstructor de copiere cerc: cout<<\traza=<<raza; } Se poate observa c, dei este definit constructor de copiere pentru clasa de baz, pentru crearea obiectului punct se apeleaz constructorul cu parametri, nu cel de copiere. Pag. 4/9

Programare C/C++ - Laborator nr. 12 Lista ce specific parametrii ce se transmit ctre obiectul clasei de baz poate lipsi n situaia n care pentru clasa de baz este definit constructor implicit sau constructor cu parametri implicii. Redefinirea funciilor membre Clasa derivat are acces la toi membrii cu acces protected sau public ai clasei de baz. Astfel, dac se definete pentru clasa punct o funcie membr de afiare: punct::afisare() { cout<<\nPunct: x=<<x<< , y=<<y; } Funcia de afiare membr a clasei punct este motenit de clasa cerc, dar, avnd n vedere faptul c o funcie de afiare pentru clasa cerc ar trebui s ofere mai mult informaie, este necesar definirea unei noi funcii de afiare, membr a clasei cerc. Este permis supradefinirea funciilor membre clasei de baz cu funcii membre ale clasei derivate. Clasa cerc se completeaz cu funcia membr: void afisare(). void cerc::afisare() { cout<<\nCerc:; cout<<\ncentru: x=<<x<<\ty=<<y; cout<<\nraza: <<raza; } Noile definiii din clasa derivat nu substituie definiiile din clasa de baz, ci se adaug acestora. Compatibilitatea ntre o clas derivat i clasa de baz. Conversii de tip Deoarece clasa derivat motenete proprietile clasei de baz, ntre tipul clas derivat i tipul clas de baz se admite o anumit compatibilitate. Compatibilitatea este valabil numai pentru clase derivate cu acces public la clasa de baz i numai n sensul de la clasa derivat spre cea de baz, nu i invers. Compatibilitatea se manifest sub forma unor conversii implicite de tip: - dintr-un obiect derivat ntr-un obiect de baz; - dintr-un pointer sau referin la un obiect din clasa derivat ntr-un pointer sau referin la un obiect al clasei de baz. De exemplu, constructorul de copiere al clasei cerc se poate rescrie sub forma: cerc::cerc(cerc & c): punct( c ) { raza=c.raza; afisare(); } Prin definiia folosit pentru constructorul de copiere se determin apelul constructorului de copiere al clasei punct n urma conversiei implicite referin cerc -> referin punct. Atribuirea p=c se face n urma conversiei implicite a obiectului de tip cerc c, n tipul punct. O atribuire de forma c=p nu este permis, ea fiind semnalat cu mesaj de eroare. Situaii similare apar la transferul parametrilor la apelul funciilor prin valoare, referin sau pointer la clas. Nu sunt permise conversii inverse. De exemplu atribuirile: Pag. 5/9

Programare C/C++ - Laborator nr. 12 p_c=&p; c=p; cerc &r_c=p; // conversie pointer punct->pointer cerc // conversie punct->cerc // conversie referina punct->referinta cerc

nu sunt permise, ele conducnd la erori la compilarea programului. Dac ntr-o aplicaie, o conversie de la clasa de baz la clasa derivat este necesar, ea se poate defini prin supradefinirea operatorului de atribuire sau a operatorului cast. Supradefinirea operatorilor n clasele derivate Funciile operator membre ale clasei de baz sunt motenite de clasele derivate i pot fi supradefinite n clasele derivate, ca toate celelalte funcii membre. O excepie o constituie operatorul de atribuire, care se supune urmtoarelor reguli: - Dac operatorul este supradefinit n clasa derivat, funcia operator=() preia sarcina efecturii atribuirilor pentru toi membrii, i pentru cei ai clasei de baz, chiar dac este supradefinit operatorul n clasa de baz; - Dac nu este supradefinit funcia operator=() n clasa derivat, dar este definit n cea de baz, atribuirea pentru membrii clasei de baz se face apelndu-se funcia operator=(), pentru ceilali membri fcndu-se atribuirea membru cu membru; - Dac nu este supradefinit operatorul de atribuire n nici una din clase, atribuirea se face membru cu membru. Crearea unei ierarhii de clase Clasele derivate pot fi utilizate, la rndul lor drept clase de baz pentru a obine noi clase derivate, crendu-se astfel ierarhii de clase. Se pornete de la o clas relativ simpl. La fiecare derivare se adaug noi proprieti obinndu-se clase tot mai complexe. Fiecare clas derivat motenete proprietile tuturor claselor utilizate n mod succesiv drept clase de baz. Motenirea multipl Conceptul de motenire permite crearea de clase noi care motenesc proprietile mai multor clase de baz. Motenirea multipl aduce mai mult flexibilitate n construirea claselor, rezultatul fiind obinerea unor structuri de clase complexe. Sintaxa folosit pentru declararea unei clase derivate D este: class D: specif_acces clasa_baza1, specif_acces clasa_baza2, {} n lista claselor de baz, pentru fiecare clas de baz, se include specificatorul de acces. Principiile prezentate la derivarea simpl i la crearea ierarhiilor simple de clase sunt valabile i n cazul derivrii multiple. Prin clasa strpoz se vor crea obiecte ce conin un ir de caractere mpreun cu poziia de afiare. Aceast clas se obine prin derivarea din clasele pozitie i string, accesul ctre acestea fiind public. #include <iostream.h> #include <string.h> #include <conio.h> class pozitie { protected : int x, y; public: pozitie(int=0, int=0); // declarare clas pozitie

Pag. 6/9

Programare C/C++ - Laborator nr. 12 pozitie(pozitie&); ~pozitie(); void afisare(); void deplasare(int,int); }; pozitie::pozitie(int abs, int ord) { x=abs; y=ord; cout<<"\nConstructor - pozitie"; afisare(); } pozitie::pozitie(pozitie &p) { x=p.x; y=p.y; cout<<"\nConstructor copiere-pozitie"; afisare(); } pozitie::~pozitie() { cout<<"\nDestructor - pozitie"; afisare(); } void pozitie::afisare() { cout<<"\npozitie: x="<<x<<" y="<<y; } void pozitie::deplasare(int dx, int dy) { x+=dx; y+=dy; } class string // declarare clas string { protected: int ncar; // lungimea sirului char *str; public: string(int n=0) { ncar=n;str=new char[ncar+1]; str[0]='\0'; cout<<"Constructor 1- string" ; afisare(); } string(char * s) { ncar=strlen(s); str=new char[ncar+1]; strcpy(str,s); cout<<"\nConstructor 2 - string"; afisare(); } string(string & s) { ncar=s.ncar; str=new char[ncar+1]; strcpy(str, s.str); cout<<"\nConstructor de copiere-string"; afisare(); } Pag. 7/9

Programare C/C++ - Laborator nr. 12 ~string() { cout<<"\nDestructor- string"; afisare(); delete str; } void afisare() { cout<<"\nstring:"<<str<<"\n"; } string operator+(string &s) { string temp(ncar+s.ncar); strcat(temp.str, str); strcat(temp.str, s.str); return temp; } }; class strpoz : public pozitie, public string // declararea clasei strpoz, derivat din { // pozitie i string cu acces public ctre acestea char culoare; public: // n antetul constructorului se specific parametrii preluai de constructorii claselor de baz strpoz(int abs, int ord, int n=0, char c='A') : pozitie(abs, ord), string(n) { culoare=c; cout<<"\nConstructor 1 - strpoz"; afisare(); } strpoz(int abs, int ord, char *s, char c='A'):pozitie(abs, ord), string(s) { culoare=c; cout<<"\nConstructor 2 - strpoz"; afisare(); } strpoz(strpoz &sp) : pozitie(sp), string(sp) // parametrul preluat de constructorii // claselor de baz este chiar sp pentru // care se face conversia implicit ctre // clasa de baz respectiv { culoare=sp.culoare; cout<<"\nConstructor copiere - strpoz"; afisare(); } ~strpoz() { cout<<"\nDestructor -strpoz"; afisare(); } void coloreaza(char c) { culoare=c; } void afisare() { cout<<"\nstrpoz:"<<str; cout<<"\nx="<<x<<" y="<<y; cout<<"\nculoare:"<<culoare<<"\n"; } strpoz operator+(strpoz &sp) { strpoz temp(x+sp.x, y+sp.y, ncar+sp.ncar); Pag. 8/9

Programare C/C++ - Laborator nr. 12 strcat(temp.str, str); strcat(temp.str, sp.str); return temp; } }; void main() { strpoz sp1(5, 5, "TEXT"); strpoz sp2(sp1); string s1("aaa"), s2("bbb"); string s3=s1+s2; s3.afisare(); strpoz sp3=sp1+sp2; sp3.afisare(); } n declaraia clasei strpoz se specific derivarea cu acces public din clasele pozitie i string. Definiia fiecrui constructor al clasei strpoz specific transferul datelor ctre constructorii claselor de baz. Constructorii i destructorii claselor de baz sunt apelai automat la crearea, respectiv distrugerea obiectelor derivate. Ordinea apelrii constructorilor este dat de ordinea din lista claselor de baz din definiia clasei derivate, constructorul clasei derivate fiind apelat ultimul. Destructorii sunt apelai n ordine invers. Clasa derivat conine toi membrii claselor de baz, dar i poate accesa doar pe cei declarai cu acces public sau protected. Clasele derivate pot supradefini funcii existente n clasele de baz. Membrii i funciile omonime pot fi accesate folosind operatorul de rezoluie. De exemplu, pentru clasa strpoz se poate defini funcia membr de afiare astfel: void strpoz::afisare() {cout<<\nStrpoz: pozitie::afisare(); string::afisare(); cout<<\culoare:<<c<<endl; } iar n funcia main() se poate include linia de program: sp3.pozitie::afisare();

Pag. 9/9

You might also like