Anda di halaman 1dari 11

1.

Prezentarea conceptului OOP de mostenire


2. Derivarea claselor in C++
3. Vizibilitatea membrilor in clasele derivate; drepturi de acces
4. Apelul constructorilor/destructorilor
5. Conversii de tip la atribuirea obiectelor
6. Mostenirea multipla
7. ...
Sa ne propunem un exemplu de aplicatie de gestionare a unor studenti (fiecare student avand
nume, note, medii, etc.) Intr-o prima forma, clasa urmatoare ar putea gestiona entitatea student:

/******************************************************************************/
class Student
{
int m_Id;
char *m_Nume;
int m_Note[100]; /* lista de note (prima valoare 0 => finalul listei) */

public:
Student (int id, char *nume, int note[]);
Student (const char *stdinfo); /* init std pe baza unui sir formatat */

void setNume (const char *nume);


float getMedie ();
void print ();
void addNota (int nota);
void modifyNota (int index, int nota);
//...
};

/******************************************************************************/
Student::Student (int id, char *nume, int note[]) : m_Id(id)
{
this->setNume (nume);

memset (m_Note, 0, sizeof (m_Note));


for (int i = 0; note[i] != 0; i++)
this->addNota (note[i]);
}

Student::Student (const char *stdinfo)


: m_Id (0), m_Nume (NULL)
{
memset (m_Note, 0, sizeof (m_Note));

char *linfo = new char [strlen (stdinfo) + 1];


strcpy (linfo, stdinfo);

int step = 0;
char *p = strtok (linfo, ",");
while (p != NULL)
{
switch (step) {
case 0:
m_Id = atoi (p); break;
case 1:
this->setNume (p); break;
default:
this->addNota (atoi(p));
};

p = strtok (NULL, ",");


step++;
}
}
void Student::setNume (const char *nume) {

if (nume == NULL)
return;

if (m_Nume != NULL)
delete[] m_Nume;

m_Nume = new char [strlen (nume) + 1];


memcpy (m_Nume, nume, strlen (nume) + 1);
}

float Student::getMedie () {

int i;
float s = 0;
for (i = 0; i < sizeof(m_Note) / sizeof(int) && m_Note[i] != 0; i++)
s = s + m_Note[i];

return (i > 0) ? (s / i): 0;


}

void Student::print () {
std::cout << "id: " << m_Id;
std::cout << "\nnume: " << m_Nume;
std::cout.precision (2);
std::cout << "\nmedie: " << std::fixed << this->getMedie() << std::endl;
}

void Student::addNota (int nota) {

int i = 0;
while (i < sizeof(m_Note)/sizeof(int) && m_Note[i] != 0) i++;

if (i < sizeof(m_Note) / sizeof(int))


m_Note[i] = nota;
}

void Student::modifyNota (int index, int nota) {


m_Note[index] = nota; /* @todo: aici, ar trebui sa verificam indexul */
}

/******************************************************************************/
void main ()
{
/* Instantiem un std cu id=101, nume=Ionescu Vasile, notele 8,9,5,... */
Student S1 ("101,Ionescu Vasile,8,9,5,10,8,9");
S1.print();

std::cout << std::endl;


}
/******************************************************************************/
Daca rafinam putin problema: de exemplu in universitatea ATM exista studenti la buget
(militari) si studenti cu taxa (civili).
Ei au anumite caracteristici comune (cum ar fi informatiile gestionate de clasa Student, dar si
caracteristi diferite:

- studentii de la buget pot avea: un grad militar, alte informatii specifice…


- studentii de la taxa pot avea: o taxa platita pana acum, alte informatii specifice…

Daca ne propunem sa gestionam ambele categorii de studenti (buget, taxa), variantele ar putea
fi:

V1. Crearea a doua clase total diferite (StudentBuget si StudentTaxa) care sa gestioneze
cele doua categorii diferite de studenti.

class StudentBuget class StudentTaxa


{ {
typedef enum { int m_Id;
STD_FR,STD_CAP,STD_SG,STD_SGMAJ char *m_Nume;
} t_GRAD; int m_Note[100];
int m_TaxeAchitate[20];
int m_Id;
char *m_Nume; public:
int m_Note[100]; StudentTaxa(int id, char *nume,
t_GRAD m_Grad; int note[], int taxe[]);
StudentTaxa(const char *stdinfo);
public:
StudentBuget(int id, char *nume, void setNume (const char *nume);
int note[], t_GRAD grad); float getMedie ();
StudentBuget(const char *stdinfo); void print ();
void addNota (int nota);
void setNume (const char *nume); void modifyNota(int index, int nota);
float getMedie ();
void print (); void payTaxa (int val) {/*...*/}
void addNota (int nota); int getSumaAchitata() {/*...*/}
void modifyNota (int index, int nota); //...
};
void setGrad (t_GRAD grad)
{ m_Grad = grad; }
t_GRAD getGrad (){return m_Grad;}
//...
};

Avantaj:
- Exista doua clase diferite care gestionează cele două entitați diferite (std taxa - std buget)

Dezavantaj:
- Duplicarea codului: 80-90 % din codul celor doua clase este identic
V2. Adaugarea la nivelul clasei Student de mai sus a tuturor informatiilor specifice (extra
Student), cumulate pentru ambele categorii de studenti, precum si un tip (membru in
clasa Student) care sa mentioneze tipul studentului: BUGET sau TAXĂ.

class StudentAny
{
typedef enum {
STD_BUGET, STD_TAXA } t_STDTYPE;

typedef enum {
STD_FR, STD_CAP, STD_SG, STD_SGMAJ } t_GRAD;

int m_Id;
char* m_Nume;
int m_Note[100];

t_STDTYPE m_Tip;
t_GRAD m_Grad;
int m_TaxeAchitate[20];

public:
StudentAny (int id, char *nume, int note[],
t_STDTYPE tip, t_GRAD grad, int taxe[]);

StudentAny (const char *stdinfo);

void setNume (const char *nume);


float getMedie ();
void print ();
void addNota (int nota);
void modifyNota (int index, int nota);

void setGrad (t_GRAD grad) {/*...*/}


t_GRAD getGrad () {/*...*/}
//...
void payTaxa (int val) {/*...*/}
int getSumaAchitata() {/*...*/}
//...
};

Avantaj:
- codul comun nu este duplicat

Dezavantaj:
- amestecam entitatile (compromis) la un moment dat devine complicat de lucrat
V3. Utilizarea mostenirii OOP (derivarea claselor): din clasa Student se vor deriva doua clase
Specializate: una pe std de la buget, cealalta pe std de la taxa.

class Student
{
int m_Id;
char *m_Nume;
int m_Note[100];

public:
Student (int id, char *nume, int note[]);
Student (const char *stdinfo);

void setNume (const char *nume);


float getMedie ();
void print ();
void addNota (int nota);
void modifyNota (int index, int nota);
//...
};

typedef enum {
STD_FR,STD_CAP,STD_SG,STD_SGMAJ} t_GRAD;

class StudentBuget : public Student class StudentTaxa: public Student


{ {
t_GRAD m_Grad; int m_TaxeAchitate[20];

public: public:
StudentBuget(int id, char *nume, StudentTaxa(int id, char *nume,
int note[], t_GRAD grad); int note[], int taxe[]);
StudentBuget(const char *stdinfo); StudentTaxa(const char *stdinfo);

void setGrad (t_GRAD grad) {/*...*/} void payTaxa (int val) {/*...*/}
t_GRAD getGrad () {/*...*/} int getSumaAchitata() {/*...*/}
//... //...
}; };

Observatii:
- O combinatie intre cele doua versiuni de mai sus: codul comun il avem o singura data si
avem doua clase diferite specifice pentru cele doua entitati diferite
- Desi, crestem numarul de clase in cadul programului, insa este mult mai eficient:
o nu duplicam codul - avantajul lui V2
o distingem clar entitatile (fiecare clasa specializata se ocupa de entitatea ei) –
avantajul lui V1
Mostenirea claselor:
- mostenirea claselor: derivarea claselor
- clasa din care se face mostenirea (derivarea) : clasă de bază, clasă părinte, super-clasă, etc.
- clasa mostenita (derivata): clasă copil, clasă derivată, sub-clasă, clasă specializată
- dintr-o clasă de bază se pot face una sau mai multe derivari
- dintr-o clasă derivată (clasă copil): se poate deriva mai departe un (sub)copil
(clasă nepot pentru clasa de bază)

Proprietati ale claselor derivate:


- Mostenesc toti membrii clasei de bază (structura clasei + comportament), mai putin
metodele de tip constructori
- Constructorii clasei de bază vor participa (unul din ei) la initializarea obiectelor de tip
derivat.
- Metodele se mostenesc in mod implicit intocmai insa se pot suprascrie (methods overriding)
Metodele mostenite se redeclara in clasa derivata si apoi se reimplementează la nivelul clasei
de derivate.
Completam exemplul anterior;
- Implementam constructorii specifici claselor noi (StudentBuget si StudentTaxa)
- Restul metodelor (print(), setNume(), getMedie(), etc.) sunt “mostenite” din clasa parinte
(Student)

StudentBuget::StudentBuget (const char *stdinfo)


: Student(stdinfo)
{
}

StudentTaxa::StudentTaxa (const char *stdinfo)


: Student(stdinfo)
{
}

void main ()
{
Student S ("101,Ionescu Vasile,8,9,5,10,8,9");
StudentBuget SB ("102,Popescu Ion,7,9,5,10,8,9,10,4");
StudentTaxa ST ("103,Vasilescu Bogdan,8,9,10,4");

S.print();
SB.print();
ST.print();

std::cout << std::endl;


}

In constructorii claselor specifice vom “adauga” initializarile pentru campurile suplimentare:

StudentBuget::StudentBuget (const char *stdinfo)


: Student(stdinfo)
{
m_Grad = STD_FR;
}

StudentTaxa::StudentTaxa (const char *stdinfo)


: Student(stdinfo)
{
m_TaxeAchitate[0] = 0;
}
Obs: putem modifica constructorii de mai sus ca informatiile specifice sa fie “preluate” tot din
stringul stdinfo.
Pentru a fi corecti, la nivelul claselor derivate “suprascriem” metoda print ( ):

class StudentBuget : public Student


{
public:
typedef enum {
STD_FR,STD_CAP,STD_SG,STD_SGMAJ } t_GRAD;
private:
t_GRAD m_Grad;

public:
StudentBuget (int id, char *nume, int note[], t_GRAD grad);
StudentBuget (const char *stdinfo);

void setGrad (t_GRAD grad) {/*...*/}


t_GRAD getGrad () {/*...*/}

const char* getGradStr () const;


void print ();
//...
};

void StudentBuget::print()
{
std::cout << "id: " << m_Id;
std::cout << "\nnume: " << m_Nume;
std::cout.precision (2);
std::cout << "\nmedie: " << std::fixed << this->getMedie() << std::endl;
std::cout << "grad: " << this->getGradStr();
}
const char* StudentBuget::getGradStr () const
{
switch (m_Grad) {
case STD_FR: return "std.fr.";
case STD_CAP: return "std.cap.";
case STD_SG: return "std.sg.";
case STD_SGMAJ: return "std.sg.maj";
default: return "std";
};
}

In clasa Student, e obligatoriu sa schimbm membrii de tip private in protected (pentru


a-i putea accesa in clasele copil).
class Student
{
protected:
int m_Id;
char *m_Nume;
int m_Note[100];
public:
Student (int id, char *nume, int note[]);
Student (const char *stdinfo);
//...
};
Obs: specificatorul protected: in clasa de bază este la fel ca private insă cu access din
clasa copil.
Putem refolosi deja codul scris la nivelul clasei de bază Student:
void StudentBuget::print()
{
Student::print();/* apel metoda print originala (din clasa de baza) */
std::cout << "grad: " << this->getGradStr();
}

Derivarea poate fi de tip public sau de tip private.


Obs : ideea rămâne aceeaşi, însă există o serie de diferenŃe în anumite contexte.

class B { /*...*/ };
class D : public B // public derivation
{ /*...*/ };

class B { /*...*/ };
class D : B // private derivation
{ /*...*/ };

class B { /*...*/ };
class D : private B // private derivation
{ /*...*/ };

Accesul (vizibilitatea) din clasa derivată la membrii mosteniti din clasa de bază:

Tip derivare Daca in clasa de bază un membru este: În clasa derivată el este/devine:
private inaccesibil
public protected protected
public public

private inaccesibil
private protected private
public public

Anda mungkin juga menyukai