Sunteți pe pagina 1din 9

Lucrarea nr.

PROPRIETĂŢI ALE MECANISMULUI DE MOŞTENIRE

Transmiterea parametrilor în mecanismul de moştenire

După cum se ştie, de fiecare dată când este creată o instanţă a unei clase derivate,
programul apelează constructorul pentru fiecare din clasele din care derivează clasa respectivă, pe
lângă constructorul clasei derivate înseşi. În multe cazuri, clasele de bază din care derivează clasa
conţin funcţii constructor care aşteaptă parametrii. Dacă o clasă derivată nu transmite parametrii
către funcţiile constructor ale claselor superioare din arborele ierarhic, va apărea o eroare şi
compilatorul nu va mai compila programul. Forma generală expandată a declaraţiei constructorului
este următoarea:

Derivata constructor(lista_argumente):baza1(lista_argumente1)
:baza2(lista_argumente2)
.............................
:bazaN(lista_argumenteN)
{

// corpul constructorului derivatei

În declaraţia precedentă, baza1 până la bazaN reprezintă clasele de bază pentru clasa
derivată, iar lista_argumente este formată din lista_argumente1, ..., lista_argumenteN, plus
argumentele necesare constructorului clasei Derivata, aşa cum se poate observa din următorul
exemplu simplu:

#include <iostream.h>
#include <stdlib.h>
class Baza {
protected:
int i;
public:
Baza(int x)
{
i=x;
cout<<"Construieste clasa Baza.\n";
}
~Baza(void)
{
cout<<"Distruge clasa Baza.\n";
}
};
class Derivata:public Baza {
int j;
public:
Derivata(int x,int y):Baza(y)
{
j=x;
cout<<"Construieste clasa Derivata.\n";
}
~Derivata(void)
{
cout<<"Distruge clasa Derivata.\n";
}
void arata(void)
{
cout<<i<<","<<j<<endl;
}
};
void main()
{
Derivata obiect(3,4);
obiect.arata();
system("pause");
}

Moştenirea pe mai multe niveluri

Aşa cum s-a vazut moştenirea multiplă constă în folosirea a două sau mai multe clase la
bază pentru a deriva o nouă clasă. Moştenirea pe mai multe nivele, are loc la derivarea unei clase
dintr-o clasă de bază care, la rândul ei este derivată din altă clasă. De exemplu clasa manager,
bazată pe clasa angajat, aceasta fiind la rândul ei bazată pe clasa personal.

#include <iostream.h>
#include <string.h>
#include <stdlib.h>

class personal
{
public:
personal(char *nume,int virsta);
void arata_personal(void);
private:
char nume[64];
int virsta;

};
class angajat:public personal
{
public:
angajat(char *nume, int virsta, char *telefon);
void arata_angajat(void);
private:
char telefon[64];

};
class manager:public angajat
{
public:
manager(char *nume, int virsta, char *telefon,char *birou);
void arata_manager(void);
private:
char birou[64];

};
personal::personal(char *nume,int virsta)
{
strcpy(personal::nume,nume);
personal::virsta=virsta;
}
void personal::arata_personal(void)
{
cout<<"Angajat:"<<nume<<endl;
cout<<"Virsta:"<<virsta<<endl;}
angajat::angajat(char *nume,int virsta, char *telefon):personal(nume,virsta)
{
strcpy(angajat::telefon,telefon);
}
void angajat::arata_angajat(void)
{
arata_personal();
cout<<"Telefon:"<<telefon<<endl;
}
manager::manager(char *nume, int virsta, char *telefon,char *birou):angajat(nume,virsta,telefon)
{
strcpy(manager::birou,birou);
}
void manager::arata_manager(void)
{
arata_angajat();
cout<<"Birou:"<<birou<<endl;
}
void main(void)
{
angajat muncitor ("Popescu Ion",41,"0742.0....");
manager sef("Ionescu Ion",50,"0743......","222");
muncitor.arata_angajat();
sef.arata_manager();
system("pause");
}

Conflictele de nume între clasele de bază şi cele derivate

Atunci când se derivează o nouă clasă din una sau mai multe clase de bază, este posibil ca
numele unui membru din clasa derivată să fie identic cu numele unui membru din una sau mai
multe clase de bază. Atunci când apare un astfel de conflict, C++ utilizează numele membrului din
clasa derivată, aşa cum arată următorul program:

#include <iostream.h>
#include <stdlib.h>
class Baza {
public:
void Afisare(void)
{
cout<<"Aceasta este clasa de baza!\n";
}
};
class Derivata:public Baza {
public:

void Afisare(void)
{
cout<<"Aceasta este clasa derivata!\n";
}
};
void main()
{
Derivata obiect;
obiect.Afisare();
system("pause");
}
La rularea acestui program pe ecran se va afişa mesajul: Aceasta este clasa derivata!
Pentru a accesa membrul cu acelaşi nume din clasa de bază trebuie utilizat operatorul de rezoluţie
globală :: care să indice clasa al cărei membru este apelat. Programul anterior devine:

#include <iostream.h>
#include <stdlib.h>
class Baza {
public:
void Afisare(void)
{
cout<<"Aceasta este clasa de baza!\n";
}
};
class Derivata:public Baza {
public:

void Afisare(void)
{
cout<<"Aceasta este clasa derivata!\n";
}
};
void main()
{
Derivata obiect;
obiect.Afisare();
obiect.Baza::Afisare();
system("pause");
}

Evitarea ambigutităţilor în clasele de bază

Atunci când programele derivează o clasă ce moşteneşte mai multe clase derivate anterior
dintr-o singură clasă de bază (vezi figura de mai jos), este posibil ca acea clasă să conţină membri
care, deşi unici în cadrul claselor părinţi, poartă acelaşi nume în cadrul clasei derivate.

public:i clasa baza

derivata1 public:i public:i derivata2

derivata1::i
derivata3
derivata2::i

În astfel de situaţii, ambiguitatea membrilor clasei va face compilatorul să genereze eroare,


aşa cum se întâmplă în următorul program:
#include <iostream.h>
#include <stdlib.h>
#include <string.h>
class Baza {
public:
int i;
};
class Derivata1:public Baza {
public:
int j;
};
class Derivata2:public Baza {
public:
int k;
};
class Derivata3:public Derivata1,public Derivata2 {
public:
int suma;
};
void main(void)
{
Derivata3 obiect;
obiect.i=10; //Aceasta instructiune provoaca eroare
obiect.j=20;
obiect.k=30;
obiect.suma=obiect.i+obiect.j+obiect.k;
cout<<obiect.suma<<endl;
system("pause");
}
Deoarece clasa Baza conţine membrul public i şi clasele Derivata1 şi Derivata2 provin din clasa
Baza, clasa Derivata3 conţine două instanţe ale membrului public i. Pentru a preveni ambiguitatea,
trebuie prevenită apariţia mai multor copii ale clasei de bază într-un obiect dat. Pentru aceasta,
clasa de bază trebuie declarată folosind cuvântul cheie virtual, aşa cum se poate vedea din
programul următor (modificarea programului anterior):

#include <iostream.h>
#include <stdlib.h>
#include <string.h>
class Baza {
public:
int i;
};
class Derivata1:virtual public Baza {
public:
int j;
};
class Derivata2:virtual public Baza {
public:
int k;
};
class Derivata3:public Derivata1,public Derivata2 {
public:
int suma;
};
void main(void)
{
Derivata3 obiect;
obiect.i=10;
obiect.j=20;
obiect.k=30;
obiect.suma=obiect.i+obiect.j+obiect.k;
cout<<obiect.suma<<endl;
system("pause");
}
Faptul că fiecare din cele două clase intermediare moştenesc clasa de bază în mod virtual
permite clasei derivate din final să moştenească ambele clase Derivata1 şi Derivata2 fără să mai
apară probleme.
Un alt exemplu de clasă virtuală este descris mai jos:
#include <iostream.h>
#include <stdlib.h>

class BAZA {
public:
int i;
};

class DERIVAT_1 : virtual public BAZA {


public:
int j;
};

class DERIVAT_2 : virtual public BAZA {


public:
int k;
};

class DERIVAT_3 : public DERIVAT_1, public DERIVAT_2 {


public:
int sum;
};

void main() {
DERIVAT_3 d3; d3.i = 33; cout<<d3.i<<endl;
DERIVAT_2 d2; d2.i = 22; cout<<d2.i<<endl;
DERIVAT_1 d1; d1.i = 11; cout<<d1.i<<endl;
BAZA b; b.i = 1; cout<<b.i<<endl;
system("pause");
}

Clasele FRIEND

• Cu ajutorul cuvântului cheie friend , o clasă poate informa C++ care sunt prientenii
săi, cu alte cuvinte ce clase pot accesa direct membrii săi privaţi.
• Membrii privaţi ai unei clase protejează datele acesteia.
• C++ vă permite restrângerea accesului unei clase prietene la o anumită mulţime de
funcţii.

Exemplele următoare arată utilizarea claselor friend:

#include <iostream.h>
#include <stdlib.h>
#include <string.h>

class Carte {
public:
Carte(char *titlu)
{strcpy(Carte::titlu,titlu);};
void arata_carte()
{cout<<titlu;};
friend class Cititor;
private:
char titlu[64];
};
class Cititor {
public:
Cititor(char *nume)
{strcpy(Cititor::nume,nume);};
void arata_cititor(class Carte carte)
{ cout<<"Cititor: "<<nume<<' '<<endl<<"Carte: "<<carte.titlu<<endl; };
private:
char nume[64];
};
void main()
{
Cititor cititor("Ionescu Ion");
Carte carte_preferata("Stapanul Inelelor");
cititor.arata_cititor(carte_preferata);
system("pause");
}
Dacă se doreşte ca doar anumiţi membrii ai unei clase să aibă acces la datele private ale altei
clase, atunci pot fi specificaţi doar acei membri de tip friend. De exemplu, dacă în exemplul anterior
dorim ca doar funcţia arata_carte() din clasa Cititor să aibă acces la datele private ale clasei Carte, se
poate utiliza următoarea instrucţiune în cadrul clasei Carte:

friend Cititor::arata_carte();

aşa cum se poate vedea din următorul program obţinut prin modificarea programului anterior:

#include <iostream.h>
#include <stdlib.h>
#include <string.h>

class Cititor {
public:
Cititor(char *nume)
{strcpy(Cititor::nume,nume);};
void arata_cititor(class Carte carte);
private:
char nume[64];
};
class Carte {
public:
Carte(char *titlu)
{strcpy(Carte::titlu,titlu);};
void arata_carte()
{cout<<titlu;};
friend Cititor::arata_cititor(Carte carte);
private:
char titlu[64];
};
void Cititor::arata_cititor(class Carte carte)
{
cout<<"Cititor: "<<nume<<' '<<endl<<"Carte: "<<carte.titlu<<endl;
};
void main()
{
Cititor cititor("Ionescu Ion");
Carte carte_preferata("Stapanul Inelelor");
cititor.arata_cititor(carte_preferata);
system("pause");
}

Un alt exemplu de utilizare a unei clase friend:

#include <iostream.h>
#include <string.h>
#include <stdlib.h>

class manager;
class angajat
{
public:
angajat(char *nume, int virsta, char *telefon);
void arata_angajat(void);
void modifica_angajat(char *nume, int virsta, char *telefon);
friend manager;
private:
char nume[64];
char telefon[64];
int virsta;
};

angajat::angajat(char *nume, int virsta, char *telefon)


{
strcpy(angajat::nume,nume);
strcpy(angajat::telefon,telefon);
angajat::virsta,virsta;
}
void angajat::arata_angajat(void)
{
cout<<"Nume:"<<nume<<endl;
cout<<"Telefon:"<<telefon<<endl;
cout<<"Virsta:"<<virsta<<endl;
}
void angajat::modifica_angajat(char *nume, int virsta, char *telefon)
{
strcpy(angajat::nume,nume);
strcpy(angajat::telefon,telefon);
angajat::virsta,virsta;
}

class manager
{
public:
void update_angajat(angajat *temp,char *nume, int virsta, char *telefon);
};

void manager::update_angajat(angajat *temp,char *nume, int virsta, char *telefon)


{
temp->modifica_angajat(nume, virsta,telefon);
}

void main(void)
{
angajat muncitor ("Popescu Ion",41,"0742.0....");
manager sef;
muncitor.arata_angajat();
sef.update_angajat(&muncitor,"Ionescu Ion",42,"0743.0.....");
muncitor.arata_angajat();
system("pause");
}

Teme:

1. Să se implementeze o clasă Punct care conţine 6 date de tip privat: 3 reprezintă coordonatele polare
ale punctului în spaţiu (unghiurile ϕ , θ şi distanţa ρ , aşa cum arată figura următoare) ,

iar 3 sunt coordonatele carteziene ale punctului (x, y şi z). Constructorul clasei va iniţializa cele trei
coordonate polare, iar clasa va avea o funcţie de calcul a coordonatelor carteziene ştiind că formulele
de calcul sunt următoarele:
⎧ x = ρ ⋅ cos(θ ) ⋅ cos(ϕ )

⎨ y = ρ ⋅ cos(θ ) ⋅ sin(ϕ )
⎪ z = ρ ⋅ sin(θ )

De asemenea, clasa va avea o funcţie de afişare a coordonatelor polare şi o funcţie de afişare a
coordonatelor carteziene. Să se creeze o clasă Segment care să conţină două date de tip Punct şi o
funcţie care să calculeze lungimea segmentului (egală cu distanţa dintre cele două puncte). Să se
creeze două segmente şi să se afişeze coordonatele polare şi carteziene ale capetelor segmentelor
precum şi lungimea segmentelor.

2. Să se implementeze o clasă Punct care să conţină două date de tip privat x şi y ce reprezintă
coordonatele carteziene în plan ale unui punct, o funcţie constructor care iniţializează cele două
coordonate şi o funcţie de afişare a coordonatelor. Să se creeze o clasă Triunghi de tip friend cu clasa
Punct, care să conţină trei elemente de tipul Punct ce reprezintă vârfurile unui triunghi. Să se creeze o
funcţie care să stabilească dacă triunghiul este echilateral, isoscel sau oarecare şi o funcţie care să
calculeze perimetrul triunghiului.

S-ar putea să vă placă și