>> Inapoi <<



                                  --------
                                   Curs 6
                                  --------
                                  
    
=============================
 Clase derivate si mostenire
=============================
 Spunem ca clasa A este o clasa derivata din clasa B daca clasa A are
"trasaturile" clasei B, eventual, si altele noi. Clasa B se mai numeste
clasa de baza pentru A. 

 Exemplu: Clasa predefinita ifstream este o clasa derivata din clasa de baza
istream.
 Putem defini propriile noastre clase C++. O clasa derivata are toate
functiile si variabilele membru ale clasei de baza, dar poate avea si
functii membre suplimentare si/sau variabile membru suplimentare.
 Sintaxa generala pentru declararea clasei derivate A este:
 
  class A : lista_claselor_de_baza
   {
    //elemente specifice clasei (derivate) A
    ...
   };
   
 ==========
  Exemplu:
 ==========
 
1.  class A : B
     {
      //clasa A este o clasa derivata a clasei B. Clasa B este singura clasa
      //de baza a clasei A
     };
2.  class A: private B
     {
      //clasa A este o clasa derivata a clasei B. Clasa B este singura clasa
      //de baza a clasei A
     }
3.  class A: B, public C
     {
      ...
     };
 Pentru situatia 3 clasa A este o clasa derivata a claselor B si C (Se poate
defini derivare si pentru struct, dar nu si pentru union). Cuvintele
rezervate private si public din exemplul precedent se numesc modificatori de
protectie. Implicit, modificatorul de protectie este private la definirea
unei clase derivate si public la definirea unei structuri derivate. Deci,
cazurile 1 si 2 din exemplul precedent sunt practic identice.
 Deci, modificatorii de protectie utilizati in lista_claselor_de_baza
definesc protectia in clasa derivata a elementelor mostenite.

 -----------------------------------------------------------------------------
|  Accesul in   |    Modificatorul de    |   Accesul in clasa  | Accesul din  |
| clasa de baza |     protectie din      |      derivata a     |   exterior   |
|               | lista_claselor_de_baza | elementului mostenit|              |
 -----------------------------------------------------------------------------
|   private     |        private         |     inaccesibil     |  inaccesibil |
|   protected   |        private         |       private       |  inaccesibil |
|   public      |        private         |       private       |  inaccesibil |
 -----------------------------------------------------------------------------
|   private     |        public          |     inaccesibil     |  inaccesibil |
|   protected   |        public          |     protected       |  inaccesibil |
|   public      |        public          |     public          |   accesibil  |
 -----------------------------------------------------------------------------

 Pentru a oferi clasei derivate acces la un membru al clasei de baza, acesta
trebuie declarat protected sau public. Pentru respectarea principiului
incapsularii datelor, datele membre pentru care se ofera acces claselor
derivate se declara in clasa de baza cu atributul protected. De asemenea
pentru a conserva dreptul de acces in urma derivarii, se utilizeaza
modificatorul de acces public. Accesul poati fi stopat pe orice nivel al
ierarhiei de clase printr-un modificator de acces private.
 Este esential ca atributele de acces ale membrilor unei clase, precum si
modificatorii de acces ale claselor derivate sa fie stabilite astfel incat
sa permita dezvoltarea ierarhiei de clase fara a afecta incapsularea
datelor. Controlul accesului trebuie utilizat cu atentie pentru a conserva
avantajele de programarea orientata pe obiecte.


=======================================================
 Relatia dintre constructorii si destructorii claselor 
           de baza si ai clasei derivate
=======================================================

 Constructorii si destructorii sunt functii membru care nu se mostenesc. La
instantierea unui obiect al unei clase derivate se apeleaza atat
constructorii clasei derivate cat si cei ai claselor de baza. Intai se
apleleaza constructorii claselor de baza si la urma se apeleaza si
constructorul clasei derivate. Ordinea de apel a constructorilor claselor de
baza corespunde cu ordinea in care sunt indicate clasele respective in
"lista_claselor_de_baza" din definitia clasei derivate.
 La distrugerea unui obiect al clasei derivate, destructorii se apeleaza in
ordine inversa. Deci, intai se apeleaza destructorul clasei derivate si apoi
se apeleaza destructorii claselor de baza in ordine inversa fata de ordinea
de apel a constructorilor la instantierea obiectului respectiv.

 ==========
  Exemplu:
 ==========
 
#include 

class baza
 {
  public:
    baza() {cout << "Constructor al clasei de baza.\n";}
    ~baza() {cout << "Destructor al clasei de baza.\n";}
 };

class derivata : public baza
 {
  public:
    derivata() {cout << "Constructor al clasei derivate.\n";}
    ~derivata() {cout << "Destructor al clasei derivate.\n";}
 };

int main()
 {
  derivata();
  return 0;
 }

// La ecran va apare:
// Constructor al clasei de baza.
// Constructor al clasei derivate.
// Destructor al clasei derivate.
// Destructor al clasei de baza.

 La instantierea unui obiect se transmit valori parametrilor constructorilor
pentru initializarea datelor membru. In cazul unui obiect al unei clase
derivate, o parte din valori se folosesc pentru initializarea datelor membru
specifice clasei derivate, iar restul pentru initializarea datelor membru ale
claselor de baza.
 Constructorul clasei derivate contine parametri pentru toate valorile care se
utilizeaza la initializarea obiectelor. Transferul valorilor de initializare
pentru datele membru ale claselor de baza se defineste cu ajutorul antetului
constructorului clasei derivate.

 Fie A o clasa derivata din clasele de baza B1, B2, ..., Bn:
 
  class A: public B1(...), public B2(...), ..., public Bn
   {
    ...
    A(...); //prototip pentru constructor
    ...
   };
 
 Constructorul A are un antet la definirea sa de forma:
  A:: A(...) : B1(...), B2(...), ..., Bn(...)
  
 Constructorul A are o lista de parametri completa, adica pentru initializarea
tuturor detelor membru ale unui obiect de tip A.
 Expresia Bi(...) (i=1,2,...,n) din antetul constructorului A contine, in
paranteza, o lista de expresii care definesc valori initiale pentru datele
membru ale clasei Bi.
 Daca o clasa Bi nu are constructor, atunci pentru clasa respectiva nu va fi
prezenta o expresie de forma Bi(...) in antetul constructorului A. De
asemenea, expresia respectiva nu va fi prezenta in antetul lui A daca datele
membru ale clasei Bi se initializeaza cu valori implicite sau clasa Bi nu are
date membru.
 Expresia Bi(...) este un apel explicit al unui constructor al clasei Bi.
 Ordinea in care se scriu expresiile Bi(...) in antetul constructorului A este
arbitrara. Constructorii se apeleaza totdeauna in ordinea in care sunt scrise
clasele in "lista_de_clase_de_baza".
 O conditie esentiala pentru a se putea apela constructorii claselor de baza
prin intermediul clasei derivate este ca acestia sa aiba protectia de tip
protected sau public.
 Lista care defineste apelurile constructorilor claselor de baza nu este
prezenta in prototipurile constructorilor claselor derivate, ci numai in
antetele acestora.
 In general, clasele au constructori definiti de programatori. In cazul in
care o clasa nu are contructori, compilatorul genereaza automat un constructor
implicit. (fara parametri) pentru clasa respectiva.

 Teoretic, pot apare urmatoarele 4 cazuri:
1. Clasa derivata si clasele de baza ale ei nu au constructori definiti de
programator;
2. Cel putin o clasa dintre clasele de baza are constructori, iar clasa
derivata nu are constructori;
3. Clasa derivata are constructori, iar clasel de baza ale ei nu au
constructori definiti de programator;
4. Clasa derivat are constructori si cel putin o clasa de baza a ei are
constructori.

Rezolvare:
1. -> la instantierea obiectelor clasei derivate se aplica constructorii
impliciti generati de compilator.
2. -> toti constructorii claselor de baza definiti de programator trebuie sa
fie impliciti sau sa aiba toti parametrii impliciti. La instantierea
obiectelor clasei derivate nu se pot face initializari. La o astfel de
instantiere se aplica constructorul implicit al clasei derivate, generate de
compilator si constructorii claselor de baza definiti de programator,
folosindu-se valorile implicite ale parametrilor acestora.
3. -> constructorul clasei derivate realizeaza toate initializarile atat
pentru datele membru specifice clasei derivate cat si pentru datele membru ale
claselor de baza. Este necesar ca clasa derivata sa aiba acces la toate datele
membru ale claselor de baza care comporta initializari.
4. -> constructorul clasei derivate contine parametri pentru toate datelel
membru care comporta initializari. Valorile parametrilor corespunzatori
datelor membru specifice clasei derivate se utilizeaza pentru a initializa
datele respective in mod obisnuit (de obicei, in corpul constructorului clasei
derivate se initializeaza aceste date, iar celelalte valori se utilizeaza la
apelul explicit al constructorului claselor de baza.

 ==========
  Exemple:
 ==========
 
1. Clasa A este o clasa derivata a clasei de baza B. Nici una din clase nu
are constructor.
 La instantierea  A a; obiectul a se instantiaza fara a se face initializari.
La instantiere se aplica constructorii impliciti ai claselor generati de
compilator.

2. Clasa A este o clasa derivata a clasei de abza B. Clasa B are constructor
cu toti parametrii impliciti:

  class B
   {
    protected:
     double x,y;
    public:
     B(double p=0, double q=0)
      { x=p; y=q; }
   };
   
 Clasa A nu are constructor:
  class A : public B
   {
    protected:
     int z;
    public:
     functie();
     ...
   };
   
 La declaratia A a;, obiectul a se initializeaza apeland constructorul cu
parametrii impliciti al clasei B. Datele membre x si y se initializeaza cu 0,
iar data membru z nu este initializata.

3. Clasa A este o clase derivata a clasei B. Clasa B nu are constructor
definit de utilizator.

  class B
   {
    protected:
     double x,y;
    public:
     ... //nu exista constructori
   };
   
 Clasa A are un constructor. 
  class A : public B
   {
    protected:
     int z,u,v;
    public:
     A (double p=0, double q=0, int x1=1, int y1=2)
      {
       x=p; y=q; u=x1; v=y1;
       if (p inseamna a.x=0, a.y=0, a.u=1, a.v=2, a.z=1
  A b(3,4); -> inseamna b.x=3, b.y=4, a.u=1, b.v=2, b.z=0
  
4. Clasa A este o clasa derivata a clasei B si ambele clase au constructorii
definiti de programator.

  class B
   {
    protected: 
     double x,y;
    public:
     B(double xx=0, double yy=0)
      {
       x = xx; y = yy;
      }
     ...
   };
   
  class A: public B
   {
    protected:
     int z,u,v;
    public:
     A (double p=0, double q=0, int x1=1, int y1=2) : B(p,q)
      {
       u = x1; v=y1;
       if (p, fie independent de
un obiect al clasei pentru care este functie membru folosind operatorul
rezolutiei de domeniu:
   nume_clasa::nume_functie_membru_statica
  2. Functiile inline se declara folosind cuvantul rezervat inline. Aceasta
are in general cel mult 3 intructiuni care se inlocuiesc inca de la compilare
cu corpul functiei lor (echivalent cu #define din C).

   ==========
    Exemplu:
   ==========
   
// Fisierul "curs6-2.h"
// Acesta este fisierul de interfata care contine declaratia
// clasei de baza "figura" si a subclaselor "dreptunghi" si
// "cerc". Programul se refera la calculul polimorfic al ariilor
// de figuri

#ifndef CURS6-2_H
#define CURS6-2_H


//#include 
//#include 
//#include 

#define PI 3.14159

class figura
 {
  protected:
    int oriz, vert;
    char titlu[10];
 public:
   figura(char *nume, int i, int j);
   virtual double aria();
   void print();
 };

class dreptunghi: public figura
 {
  private:
    int h2, v2;
  public:
    dreptunghi(char *nume, int i1, int j1, int i2, int j2);
    double aria();
    void print();
 };

class cerc: public figura
 {
  private:
    int raza;
  public:
    cerc(char *nume, int i1, int j1, int r);
    double aria();
    void print();
 };


#endif // de la CURS6-2_HH

// Fisierul "curs6-2.cpp"
// Acesta este fisierul de implementare in care definim functiile
// membre ale clasei de baza "figura" si a subclaselor "dreptunghi"
// si "cerc".

#include "curs6-2.h"
#include 
#include 
#include 


// definim intai functiile membre ale clasei de baza "figura"

figura :: figura(char *nume, int i, int j)
 {
  oriz = i;
  vert = j;
  strcpy(titlu, nume);
 }

double figura :: aria()
 {
  cout << "caz implicit\n";
  return 0.0;
 }

void figura :: print()
 {
  cout << "Sunt figura numita " << titlu << endl;
 }


// in continuare, vom defini functiile membre ale clasei derivate
// "dreptunghi" din clasa de baza "figura"

dreptunghi :: dreptunghi(char *nume, int i1, int j1, int i2, int j2):
      figura(nume, i1, j1)
 {
  h2 = i2;
  v2 = j2;
 }

double dreptunghi :: aria()
 {
  return abs((oriz - h2) * (vert - v2));
 }

void dreptunghi :: print()
 {
  cout << "Sunt un dreptunghi numit " << titlu << endl;
 }


// in continuare, vom defini functiile membre ale clasei derivate
// "cerc" din clasa de baza "figura"

cerc :: cerc(char *nume, int i1, int j1, int r):
      figura(nume, i1, j1)
 {
  raza = r;
 }

double cerc :: aria()
 {
  return (PI * raza * raza);
 }

void cerc :: print()
 {
  cout << "Sunt un cerc numit " << titlu << endl;
 }

// Fisierul "main6-2.cpp"
// Acesta este fisierul de aplicatie pentru clasa figura, si
// subclasele dreptunghi si cerc.

#include "curs6-2.h"

#include 

void main()
 {
  cerc c("rotund", 3, 3, 10);
  dreptunghi d("cutie", 1, 1, 4, 4);
  figura *p = &c;
  cerc *q = &c;

  q -> print();
  cout << " aria = " << p -> aria() << endl;
  p = &d;
  p -> print();
  cout << " aria = " << p -> aria() << endl;
 }
 

=====================================
 Exercitii propuse spre implementare
=====================================

1. Scrieti un program C++ care implementeaza o structura de lista, clasele
derivate de stiva si coada. Implementarea se va face in fisiere separate si
va contine operatiile principale de push, pop, stergerea, inserarea, afisarea
structurilor precedente
 Folositi aceste biblioteci la problemele cunoscute:
  - sortare prin interclasare (lista)
  - parcurgerea grafurilor (in latime-coada, in adancime-stiva)
  - evaluarea expresiilor aritmetice (stive)
  
2. Implementati o ierarhie de clase pentru urmatoarele patrulatere:
paralelogram, dreptunghi, patrat si romb. Obiectele acestor clase sunt
patrulatere care au doua laturi paralele cu axa Ox. Clasele cu functii membru
care permit realizarea urmatoarelor operatii asupra obiectelor:
 - afisare pe ecranul grafic;
 - stergere de pe ecranul grafic;
 - deplasare pe ecran grafic;
 - calculul perimetrului si ariei figurii geometrice;
 
3. Folosind clasa Lista(de la ex. 1) definiti o clasa Polinom care sa
simuleze polinomul: a(n)*x^n+a(n-1)*x^(n-1)+...+a(0), a(i) din R. Definiti
operatiile +,-,* si evaluarea unui polinom intr-un punct. (citirea si
afisarea lor). De exemplu: 3*x^3+0*x^2+0*x+2 va fi afisat 3x^3+2.