Sunteți pe pagina 1din 78

PROGRAMAREA CALCULATOARELOR-

ALGORITMI
MODUL 2
CURS 9
CUPRINS
1. Moştenirea
• Noţiuni introductive
• Moştenirea simplă
• Moştenirea multiplă
• Constructori şi destructori pentru clasa derivată
2. Clase virtuale
3. Metode virtuale
4. Clase abstracte
5. Destructori virtuali

2
1. MOŞTENIREA
Noţiuni introductive

 În practică, majoritatea claselor nu sunt în întregime


unice, ci sunt variaţii ale unor clase existente

 Prin moştenire, orice Clasă derivată dintr-o Clasă de


bază “moşteneşte” toate atributele permise ale acesteia
din urmă.
Avantaj: este evitată în acest mod rescrierea inutilă a
unor secvenţe de cod deja create

 Clasa derivată conţine membrii clasei de bază la care


se adaugă membrii proprii 3
CLASE ȘI MOȘTENIREA
Relații între clase
 Intre clase putem avea următoarele relații:
 A) asociere
 B) dependență
 C) agregare
 D) moștenire

A) Asociere – relație între două sau mai multe clase care modelează
o interdependență între obiectele instanțiate din aceste clase;
 Se folosesc: numele asocierii, săgeți de navigare, roluri, indicatori
de multiplicitate într-o reprezentare UML;
Implementarea e realizată prin: pointeri la obiecte sau tablouri de
pointeri la obiecte, sau prin asocieri abstracte în clase distincte cu
atribute și comportament propriu, printr-o implementare sub forma
unui dicționar, care e o asociere multiplicativă de tipul n ... m
4
implementată cu tablouri de hashing.
• Asocierea este o relație între 2 clase bazată pe referință.
Clasa A va conține o referință la clasa B. Asocierea poate fi
reprezentată printr-o linie între aceste clase, săgeata
indicând direcția de navigare. Dacă săgeţile sunt prezente la
ambele capete, asocierea permite navigarea în ambele
sensuri.

class Asset { ... };


class Player {
Asset asset;
public Player(Asset purchasedAsset) { ... }
//Set the asset via Constructor or a setter
}; 5
B) Dependența
 Este deseori confundată cu asocierea. Dependenţa apare
când o referinţă la o clasă este primită ca parametru de o
anumită metodă. Dependenţa indică faptul că poate fi invocată
una dintre metodele ce implementează interfaţa clasei din care
este acea referinţă şi orice modificare a acelei clase poate
conduce la distrugerea clasei in care apare acea dependenţă.
 Dependenţa este reprezentată prin săgeată punctată, pornind
de la clasa dependentă spre cea de care e dependentă

class Operations { public void Roll() { ... }


};
class Player{
public void TakeTurn(Operations op)
{ op.Roll(); ... }
};
6
C) AGREGAREA
 Caracterizată prin proprietatea a avea (to have). E o relație între
două sau mai multe clase astfel încât un obiect al unei clase
agregate devine parte constituentă a unei clase care a generat
agregarea.
 Putem avea o agregare simplă sau una compusă ce impune ca
la un moment dat doar o instanță a clasei să fie folosită în
agregare.
 Agregarea simplă este la fel ca asocierea şi vazută deseori ca
o relaţie redundantă. O percepţie comună este că agegarea
simplă reprezintă relaţii de tipul one-to-many / many-to-many /
part-whole, care pot fi reprezentate şi prin asocieri (de aici si
redundanţa). In UML nu există o reprezentare diferită pentru ea,
unii developeri utilizează un romb gol pentru agregarea simplă.

7
class Asset { ... };
class Player {
List assets;
public void AddAsset(Asset newlyPurchasedAsset) {
assets.Add(newlyPurchasedAssest); ... }
...
};
8
AGREGAREA COMPUSĂ
 În acest caz într-o clasă se instantiază un obiect dintr-
o altă clasa. Când o clasă B este instanţiată într-o
clasă A, clasa A controlează crearea şi durata
existenţei instanţelor clasei B. Când instanţa clasei A
este distrusă, la fel va fi şi instanţa clasei B.
 O astfel de relaţie se reprezintă printr-o linie ce uneşte
cele 2 clase pe care este figurat un romb plin spre
clasa care deţine responsabilitatea creării obiectelor.
 O astfel de relaţie nu este implementată prin clase
imbricate. Clasa care este instanţiată în altă clasă
poate fi utilizată şi de alte părţi ale aplicaţiei.

9
public class Piece { ... };
public class Player
{
Piece piece = new Piece(); /*Player owns the
responsibility of creating the Piece*/
...
};

10
 D) Moștenirea - caracterizată prin proprietatea a fi (to
be). E folosită pentru a modela similaritățile și diferențele
dintre clase. Expresia, e un fel de ( is_a_kind_of ) e
folosită la nivelul clasei și e un (is_a) la nivelul
obiectelor. Doar având moștenire considerăm că avem
POO.
 Avem mai multe tipuri de moștenire, nu toate disponibile
în orice limbaj OO.

11
TIPURI DE MOȘTENIRE
 -prin specializare, subclasa este o varietate specializată
a clasei de bază
-prin specificare, clasa de bază defineste doar
comportamentul (interfeţe sau clase abstracte) care este
implementat în subclase
-prin construcţie, subclasa foloseste comportamentul
clasei de bază nefiind un subtip al ei (Stiva construită cu
Vector)
-prin extindere, subclasa adaugă noi functionalităţi clasei
de bază, dar nu va modifica nici un element moştenit
-prin limitare, subclasa limitează comportamentul clasei
de bază, fiind utilizată destul de rar
-prin combinare, când de fapt avem moştenire multiplă,
bazată pe mai multe clase de bază. 12
 Clasa CPolygon conţine membri ce sunt comuni
celorlalte două clase
 Clasele CRectangle şi CTriangle vor conţine și membri
ce corespund unor caracteristici specifice
13
 Clasa de bază nu este afectată de crearea unei clase
derivate din ea şi nu trebuie recompilată

 O clasă derivată nu poate modifica definiţia clasei de bază

 Dintr-o clasă de bază pot fi derivate mai multe clase şi


fiecare clasă derivată poate fi folosită ca bază pentru alte
clase derivate:
 se pot obţine astfel ierarhii de clase

 O clasă poate moşteni proprietăţile mai multor clase de


bază (moştenire multiplă)
 Clasa de bază se mai numeşte şi superclasă, iar clasa
derivată se mai numeşte şi subclasă
14
Moştenirea simplă

 Declararea unei clase derivate se face astfel :

class Clasa_derivata : [specificator_de_acces] Clasa_baza


{
// corp clasa derivata
};

 Specificatorul de acces permite controlul asupra membrilor


clasei de bază
 Dacă nu este prezent se consideră că acesta este implicit
de tip private
15
 Ce moşteneşte o clasă derivată:
 toate datele membre ale clasei de bază ce nu sunt
private
 toate metodele (funcţiile) membre ordinare din clasa de
bază ce nu sunt private
 aceeaşi organizare iniţială a datelor ca în clasa de bază

 Ce nu moşteneşte o clasă derivată:


 constructorii clasei de bază
 destructorul clasei de bază
 membrii private ai clasei de bază nu sunt accesibili din
clasa derivată
 metodele membre ce supraîncarcă operatorul de
atribuire în clasa de bază
 funcţiile prietene ale clasei de bază
16
 Ce poate adăuga o clasă derivată:
 date membre noi
 metode membre noi
 constructori
 destructor
 funcţii prietene
 Specificatorul de acces dă măsura în care clasa derivată
poate accesa membrii moşteniţi din clasa de bază
 Tipuri de moştenire

Moştenire
Clasa de bază
public protected private

Membru public public protected private

Membru protected protected protected private


17
Membru private inaccesibil inaccesibil inaccesibil
 class Derivata : public Baza { ..... }
 membrii privaţi din Baza sunt inaccesibili în Derivata
 membrii protejaţi din Baza devin membri protejaţi în
Derivata
 membrii publici din Baza devin membri publici în
Derivata

 class Derivata : protected Baza { ..... }


 membrii privaţi din Baza sunt inaccesibili în Derivata
 membrii protejaţi şi cei publici din Baza devin membri
protejaţi în Derivata

 class Derivata : private Baza { ..... }


 membrii privaţi din Baza sunt inaccesibili în Derivata
 membrii protejaţi şi cei publici din Baza devin membri 18
privaţi în Derivata
 Moştenirea public

class Baza
{
protected:
int i, j;
public:
void initializare(int a, int b) {
i = a;
j = b;
}

void afiseaza( ) {
cout << i << “, “ << j << “\n”;
}
};//Baza
19
class Derivata : public Baza {

public:
int inmulteste( ) {
return i * j; // corect, i si j raman protected
}
};//Derivata

int main(void)
{
Derivata obiect_derivat;
//obiect_derivat.i = 5; // gresit, i ramane protected
obiect_derivat.initializare(12, 17); // din Baza
obiect_derivat.afiseaza( ); // din Baza
cout<< “\n Produsul este: “ <<obiect_derivat.inmulteste( );
cin.get();
}
20
 Moştenirea protected
class Baza
{
int x;
protected:
int y;
public:
int z;
Baza(int x=1, int y=1 ) {
this->x=x;
this->y=y;
}
void arata( ) {
cout << "\n --------Clasa de baza------";
cout << "\n Valoarea variabilei private x: "<<x;
cout << "\n Valoarea variabilei publice y: "<<y;
} 21
};//Baza
class Derivata: protected Baza
{
public:
void do_this(void) {
//x = 5; // gresit, x este private in Baza
y = 7; // corect, y este protected in Baza, ramane protected
z = 15; // corect, z este public in Baza, devine protected
}
};

int main(void){
Baza ob1;
Derivata ob2;
ob1.arata( ); // public in Baza
ob2.do_this( );
//ob2.arata( ); // gresit, arata( ) devine protected in Derivata
cin.get(); 22

}
 Moştenirea private

class Baza
{
protected: int a, b;
public:
int setA(int a){ this->a=a;}
int setB(int b){this->b=b;}
int aduna() {
return a+b;
}
int scade() {
return a-b;
}
void afis_baza( ) {
cout << a << " " << b << "\n";
} 23
};
class Derivata : private Baza
{
public:
int inmulteste() {
return a+b;
}
};

Int main(void){
Baza obiect_baza;
obiect_baza.setA(1);
obiect_baza.setB(2);
Derivata obiect_derivat;
cout<< “\nProdusul este= “<<obiect_derivat.inmulteste(); // corect
//obiect_derivat.aduna(); // eroare, devine private
cin.get();
}
24
 Este posibil ca membrii clasei de bază să fie exceptaţi
individual de la modul de acces stabilit prin declaraţia
clasei derivate, astfel încât să-şi păstreze atributele din
clasa de bază:

class A { class B : private A


private : {
int x; …
void fx(void); public :
public : A::fy();
int y; protected :
void fy(void); A::z;
protected : };
int z;
void fz(void); 25
};
Moştenirea multiplă

class Clasa_derivata : lista_Clase_baza


{
// corp clasa
};

lista_Clase_baza: [specificator_acces] Clasa_bazai, …

 Fiecare clasă de bază poate fi prefixată la moștenire de


specificatorul private, public sau protected
 Dacă specificatorul de acces lipseşte se consideră implicit
că este unul private

26
Exemplul 1:
class Baza1 { class Baza2 {
protected: protected:
int x; int y;
public: public:
void afiseaza_x( ) { void afiseaza_y( ) {
cout << “valoarea lui x este: “ cout << “valoarea lui y
<< x <<”\n”; este: “ << y <<”\n”;
} }
void afis( ) { void afis( ) {
cout << “ y = “ << y <<”\n”;
cout << “ x = “ << x <<”\n”;
}
}
};//Baza2
};//Baza1

27
class Derivata: public Baza1, public Baza2 {
public:
void initializeaza(int i, int j) {
x = i;
y = j;
}
void afis( ) {
Baza1::afis( );
Baza2::afis( );
}
};
int main(void){
Derivata obiect_derivat;
obiect_derivat.initializeaza(100, 200);
obiect_derivat.afiseaza_x( ); // metoda mostenita din Baza1
obiect_derivat.afiseaza_y( ); // metoda mostenita din Baza2
obiect_derivat.afis(); // metoda din clasa Derivata
28
cin.get();
}
Observații:

 Clasa derivată poate conţine redefiniri (override) ale


unor metode din clasa de bază (cele virtuale):
 în clasa derivată pot exista metode cu acelaşi nume
ca și în clasa de bază, având sau nu aceeaşi
semnătură
 variantele din clasele de bază pot fi accesate din
clasa derivată utilizând calificarea completă:
Nume_clasă::nume_membru

29
 În cazul moştenirii multiple, dacă clasele de bază conţin
membri cu acelaşi nume, referirea acestora printr-un obiect
din clasa derivată poate conduce la ambiguităţi:
 pentru evitarea acestora se utilizează calificarea
completă
obiect_clasa_derivata.Nume_clasa_baza::nume_membru
 Exemplu : dacă în exemplul anterior în clasa derivata nu
avem functia afis(), printr-un obiect din clasa derivată
putem apela una din metodele afis() din clasele de bază.

obiect_derivat.afis( ); // ambiguitate

obiect_derivat.Baza1::afis( ); //corect
30
Exemplul 2:

class CPolygon
{
protected:
int width, height;
public:
void set_values (int a, int b) {
width=a; height=b;
}
};

class COutput
{
public:
void output (float i) {
cout << i << endl;
31
}
};
class CRectangle: public CPolygon, public COutput
{
public: int area ( ) { return (width * height); }
};

class CTriangle: public CPolygon, public COutput


{
public: float area ( ){ return (width * height / 2.); }
};

int main ( ) {
CRectangle re;
CTriangle trgl;
re.set_values (4,5); // metoda mostenita din CPolygon
trgl.set_values (4,5);
re.output (re.area( )); // metoda mostenita din COutput
trgl.output (trgl.area( ));
32
cin.get();
}
Constructori şi destructori pentru clasa derivată

 Regulile de funcţionare ale constructorilor şi destructorilor


rămân valabile pentru clasele derivate, cu unele aspecte
particulare

 Ce se întâmplă la crearea unui obiect din clasa derivată:


 se alocă spaţiu pentru întregul obiect (pentru datele
membre din clasa de bază și datele membre din clasa
derivată)
 se apelează constructorul clasei de bază pentru a
iniţializa datele membre moştenite din clasa de bază
 se apelează constructorul din clasa derivată pentru a
iniţializa datele membre adăugate în clasa derivată
33
 Ce se întâmplă la distrugerea unui obiect din clasa
derivată:
 se apelează destructorul clasei derivate
 se apelează destructorul clasei de bază
 este eliberat spaţiul alocat pentru întregul obiect

34
class A {
// corp clasa
};
class B : public A {
// corp clasa
};
class C : public B {
// corp clasa
};

 În cazul instanţierii unui obiect din clasa C, ordinea


creării obiectelor este:
A::A( ) -> B::B( ) -> C::C( );

 Ordinea distrugerii obiectelor este:


35
C::~C( ) -> B::~B( ) -> A::~A( );
 !!! În cazul în care în clasa de bază există doar
constructor cu parametri, atunci trebuie să avem
constructor explicit și în clasa derivată, care trebuie să
specifice apelul constructorului clasei de bază în lista de
iniţializare
 De regulă, constructorul clasei derivate conţine în lista de
parametri şi acei parametri care vor fi transmişi
constructorului clasei de bază:
Clasa_derivata ::Clasa_derivata(lista_parametri) :
Clasa_baza(parametri);
 Exemplu:
B::B(int x, int y) : A(y) {
// corp constructor
36
}
Exemplu
// clasa de baza
class Pozitie {
protected :
int x, y;
public :
Pozitie (int=0, int=0);
void afisare( );
void deplasare(int, int);
};
Pozitie::Pozitie(int abs, int ord) {
x = abs; y=ord;
cout << "Constructor \"Pozitie\", ";
afisare( );
}
void Pozitie::afisare( ) {
cout << "coordonate: x = " << x << ", y = " << y << "\n";
}
void Pozitie::deplasare(int dx, int dy) { 37
x += dx; y+=dy;
}
// clasa derivata
class Punct: public Pozitie {
int vizibil;
char culoare;
public:
Punct(int=0, int=0, char='A');
void arata( ) {
vizibil = 1;
}
void ascunde( ) {
vizibil = 0;
}
void coloreaza(char c) {
culoare = c;
}
void deplasare(int, int);
38
void afisare( );
};
// constructor
Punct::Punct(int abs, int ord, char c):Pozitie(abs, ord)
{
vizibil = 0;
culoare = c;
cout << "Constructor \"Punct\", ";
afisare( );
}

// redefinire functie de afisare in clasa derivata


void Punct::afisare( ) {
cout << "Pozitie: x = " << x << ", y = " << y;
cout << ", culoare: " << culoare;
if(vizibil)
cout << ", vizibil \n";
else
39
cout << ", invizibil \n";
}
// redefinire functie de deplasare in clasa derivata
void Punct::deplasare(int dx, int dy)
{
if(vizibil) {
cout << "Deplasare: ";
afisare( );
}
x += dx;
y += dy;
if(vizibil) {
cout << "la coordonatele: ";
Pozitie::afisare( ); //metoda din clasa Pozitie
}
} 40
// program ce utilizeaza clasa derivata
void main(void)
{
Punct p0(1, 1, 'V');
Punct p1(p0); //copy-constructor
p1.arata( );
p1.deplasare(10,10);
}

41
Copy-constructorul în clasa derivată

 Constructorul de copiere al clasei derivate este


răspunzător de iniţializarea corectă a datelor moştenite
de la clasa de bază
 De aceea se recomandă utilizarea unui astfel de
constructor de copiere:

Clasa_deriv ::Clasa_deriv(const Clasa_deriv &nume_par) :


Clasa_baza(parametri);

42
În cazul moştenirii multiple:
 ordinea în care sunt apelaţi constructorii claselor de
bază este aceeaşi cu ordinea specificării claselor de
bază în declaraţia clasei derivate
 această ordine poate diferi de ordinea specificată în lista de
iniţializare din constructorul clasei derivate
 În general, ordinea operaţiilor în cazul creării obiectelor
derivate este următoarea:
 se apelează constructorii claselor de bază în ordinea
precizată în declaraţia clasei derivate
 se apelează constructorii altor obiecte membre din
clasa derivată, în ordinea declarării lor în clasa
derivată
 se apelează constructorii clasei derivate
 La distrugerea obiectelor derivate, operaţiile se
desfăşoară în ordine inversă 43
2. CLASE VIRTUALE
 Considerăm următoarea situaţie:
class BB {
protected : int x;

};
class B1 : public BB {
// corp clasa
};
class B2 : public BB {
// corp clasa
};
class D : public B1, public B2 {
// corp clasa
};

 Un obiect al ultimei clase derivate D conţine duplicate ale


datelor membre ale primei clase de bază BB 44
 Pentru a distinge între datele membre se poate folosi
operatorul de rezoluţie (::) şi calificarea completă astfel:
BB::B1::x
BB::B2::x
 Dacă o instanţă a clasei D ar vrea să acceseze
variabila x printr-o sintaxă de genul:
D obiect;
obiect.x = valoare;
Apare o ambiguitate:
 compilatorul nu poate decide prin care din clasele B1
sau B2 să acceseze variabila din clasa de bază
 Putem folosi sintaxa object.B1 :: x = 1; dacă
accesăm x prin B1, dar sintaxa e considerată
45
complicată
 Există posibilitatea includerii unui singur exemplar al clasei
de bază primare prin declararea acesteia ca fiind clasă
virtuală în declaraţiile claselor B1 si B2:
class BB {
protected : int x;

};

class B1 : virtual public BB {


// corp clasa
};

class B2 : virtual public BB {


// corp clasa
};

class D : public B1, public B2 {


46
// corp clasa
};
 În acest caz, un obiect al clasei D va conţine numai un
obiect al clasei de bază comune BB, iar clasele B1 şi B2
vor partaja acest obiect comun

 Un obiect al unei clase derivate dintr-o clasă de bază


virtuală nu conţine direct obiectul clasei de bază comune,
ci conţine de fapt un pointer către acesta

47
 Dacă într-o ierarhie de clase, unele instanţe ale clasei de
bază sunt declarate virtuale, iar altele sunt declarate non-
virtuale, atunci obiectele claselor derivate vor conţine câte
un obiect al clasei de bază pentru fiecare instanţă non-
virtuală şi un singur obiect al clasei de bază pentru toate
instanţele virtuale

 Specificatorul virtual deşi apare în declaraţiile claselor B1


şi B2 are efect numai asupra claselor derivate din acestea

48
 Constructorul clasei derivate D trebuie să precizeze
transferul de date către constructorul clasei de bază BB
pentru crearea unei copii unice a obiectului BB:
D::D(lista_param) : B1(lista_param), B2(lista_param),
BB(lista_param) {…}

 Indiferent de poziţia într-o ierarhie de clase, un obiect al


unei clase de bază virtuale este întotdeauna construit
înaintea obiectelor non-virtuale din aceeaşi ierarhie

 Dacă într-o ierarhie de clase, o clasă de bază este


declarată virtuală folosind specificatori de acces diferiţi,
atunci se va propaga specificatorul cel mai permisiv (de
exemplu dintre specificatorii private şi public se va
propaga specificatorul public)
49
EXEMPLU CU CONTAINER STRING :
#include <iostream>
#include <string>
using namespace std;

class Person {
private:
string ptype;
string name;
public:
Person( const string & t, const string & n ) : ptype( t ), name( n ){ }
virtual ~Person( ) { }
const string & getName( ) {
return name;}
const string getPtype( ) {
return ptype; }
};//Person class
50
class Student : virtual public Person {
private:
int hours;
public:
Student( const string& n, int h ) : Person( "Student", n ), hours(h) { }
int getCreditHours( ) {
return hours;
}
}; //Student class

class Employee : virtual public Person {


private:
int hours;
public:
Employee( const string& n, int h ) : Person( "Employee", n), hours(h) { }
int getVacationHours( ) {
return hours;
}
}; //Employee class 51
class StudentEmployee : public Student, public Employee
{
public:
StudentEmployee( const string& n, int ch, int vh ) :
Employee( "ignored", vh ),
Student( "ignored", ch ),
Person( "StudentEmployee", n ) { }
};//StudentEmployee class
int main( ) {
StudentEmployee se("Popescu", 120, 15 );
cout << "Name: " << se.getName( ) << endl;
cout << "Type: " << se.getPtype( ) << endl;
cout << "Credits: " << se.getCreditHours( ) << endl;
cout << "Vacation: " << se.getVacationHours( ) << endl;
cin.get();
}//main

Obs: constructorii se apeleaza in ordinea Person, Student, Employee 52


//Exemplu cu tablouri de caractere:
class Person {
private:
char ptype[20];
char name[20];
public:
Person( const char t[], const char n[] )
{
strcpy(ptype, t);
strcpy(name, n);
}
~Person( )
{}
const char* getName( ) {
return name;
}
const char* getPtype( ) {
return ptype;
53
}
};
class Student : virtual public Person
{
private:
int hours;
public:
Student( const char n[], int h ) : Person( "Student", n ), hours(h)
{}

int getCreditHours( ) {
return hours;
}
};

54
class Employee : virtual public Person
{
private:
int hours;
public:
Employee( const char n[], int h ) : Person( "Employee", n), hours(h)
{}

int getVacationHours( ) {
return hours;
}
};

55
class StudentEmployee : public Student, public Employee
{
public:
StudentEmployee( const char n[], int ch, int vh ) :
Person( "StudentEmployee", n ),
Student( "ignored", ch ),
Employee( "ignored", vh )
{}
};

int main( ){
StudentEmployee se( “Popescu", 120, 15 );

cout << "Name: " << se.getName( ) << endl;


cout << "Type: " << se.getPtype( ) << endl;
cout << "Credits: " << se.getCreditHours( ) << endl;
cout << "Vacation: " << se.getVacationHours( ) << endl;
cin.get()ș 56
}
3. METODE VIRTUALE
 O metodă virtuală este acea metodă care este definită cu
specificatorul virtual în clasa de bază şi apoi poate fi
redefinită în clasele derivate:
 au implementări diferite în clasele derivate, dar aceiași
semnătură

 Redefinirea din clasele derivate are prioritate faţă de


definirea iniţială din clasa de bază, în cazul apelării
acesteia prin intermediul unui obiect instanţiat din clasa
derivată
 De obicei, o funcţie virtuală defineşte o clasă generală de
acţiuni, iar varianta redefinită introduce o metodă specifică
57
Observații:
- În cazul utilizării metodelor virtuale, prototipul
funcţiilor din clasele derivate trebuie să fie exact
acelaşi ca cel al funcţiei originale (semnătura)
 dacă lista de parametri diferă şi metoda originală
este declarată ca fiind virtuală, mecanismul de
funcţie virtuală este ignorat

 Când sunt utilizate obişnuit (apel prin nume obiect),


metodele virtuale se comportă la fel ca alte metode
membre ale clasei
 Comportamentul specific apare atunci când sunt
apelate printr-un pointer spre clasa de bază, care poate
referi și obiecte din clasa derivată.
 Un pointer al clasei de bază poate fi folosit pentru a
indica spre orice obiect dintr-o clasă derivată din
aceasta (upcasting)
58
UPCASTING/DOWNCASTING
 Un pointer către o clasă de bază poate fi utilizat pentru a
indica spre un obiect dintr-o clasă derivată din acea clasă de
bază, fără a specifica ceva suplimentar – proces numit
upcasting. Tipul obiectului decide care metoda va fi apelată.
 Un obiect dintr-o clasă derivată poate fi asignat unui obiect
din clasa de bază, fără a specifica nimic în plus – upcasting
 Un pointer la o clasă derivată poate fi asociat unui obiect din
clasa de bază, folosind un cast explicit spre pointer al clasei
derivate – proces numit downcasting
cout<<“\n Downcasting1: \n;
Punct *pdown; // pointer spre clasa derivata
pdown=(Punct*)&pp0; // obiect din clasa de baza
59
pdown-> afisare(); // metoda din clasa de baza
class Baza {
public:
virtual void functie( ) {
printf("\n Functia din clasa de baza\n");
}
};

class Derivat1: public Baza {


public:
void functie( ) {
printf("\n Functia din prima clasa derivata\n");
}
};

class Derivat2: public Derivat1 {


public:
void functie( ) {
printf("\n Functia din a doua clasa derivata\n");
} 60
};
int main(void) {
Baza *p, ob_baza;
Derivat1 ob_derivat1;
Derivat2 ob_derivat2;
p = &ob_baza;
p->functie( );
p = &ob_derivat1;
p->functie( );
p = &ob_derivat2;
p->functie( ); cin.get();
}

61
EXEMPLU:
#include <iostream>
using namespace std;

class CPolygon {
protected:
int width, height;
public:
void set_values (int a, int b) {
width=a; height=b; }
int getWidth() { return width;}
int getHeight() { return height; }
virtual float area(void) {return 0; }
};//CPolygon class

class CRectangle: public CPolygon {


public:
float area (void) {
return (width * height); } 62
};//CRectangle
class CTriangle: public CPolygon {
public:
float area (void) { return (width * height / 2.); }
};//CTriangle
int main (void) {
CRectangle rect;
CTriangle trgl;
CPolygon poly;
//upcasting
CPolygon * ppoly1 = &rect, *ppoly2 = &trgl, *ppoly3 = &poly;
ppoly1->set_values (1,2); / / met. din CPolygon
ppoly2->set_values (3,4); // met. din CPolygon
ppoly3->set_values (5,6); // met. din CPolygon
cout << "Arie dreptunghi: " << ppoly1->area( ) << endl; //CRectangle
cout << "Arie triunghi: "<< ppoly2->area( ) << endl; // CTriangle
63
cout << "Arie poligon (init): " << ppoly3->area( ) << endl; // CPolygon
//upcasting
poly=trgl;
cout<< “Valori obiect poligon dupa upcasting:
width="<<poly.getWidth()<<" height="<<poly.getHeight()<<endl;
cout << "Arie poligon (trgl): " << poly.area( ) << endl;

// poly preia valorile atributelor din trgl, dar se apeleaza


// metoda area() din clasa de baza CPolygon
cin.get();
}

64
 Când un astfel de pointer indică spre un obiect derivat ce
conţine o metodă virtuală, compilatorul C++ determină
care versiune a metodei va fi apelată, în funcţie de tipul
obiectului spre care indică acel pointer

 Varianta cu care se va lucra se stabileşte la execuție,


motiv pentru care se foloseşte denumirea de “legătură
dinamică” (dynamic binding), spre deosebire de “legătura
statică” în care varianta cu care se lucrează este stabilită
în etapa de compilare

 Astfel se pot executa versiuni diferite ale funcţiei virtuale


în funcţie de tipul obiectului (polimorfism dinamic)
65
 Alte caracteristici:
 o metodă rămâne virtuală indiferent de câte ori este
moştenită
 dacă o clasă derivată nu redefineşte o metodă
virtuală, atunci un obiect dintr-o clasă derivată va
folosi metoda din clasa de bază
 metodele statice nu pot fi funcţii virtuale
 constructorii nu pot fi funcţii virtuale
 destructorii pot fi funcţii virtuale

66
4. CLASE ABSTRACTE
 Sunt clase care nu sunt utilizate direct, ci furnizează un
schelet pentru alte clase ce vor fi derivate din acestea
 De obicei, toate metodele membre ale unei clase abstracte
sunt virtuale şi au implementări vide urmând să fie redefinite
în clasele derivate

 O metodă virtuală ce nu are implementare se numeşte


metoda pur virtuală (funcţie virtuală pură):
 doar defineşte o interfaţă pentru obiectele claselor derivate
 O metoda pur virtuală face clasa în care apare să fie o
clasă abstractă
 Metoda pur virtuală trebuie definită în clasele derivate sau
redeclarată ca metoda pur virtuală în clasele derivate
67
 Declarare:
virtual tipReturnat numeMetoda( ) = 0;

Observație: O clasă abstractă nu poate fi instanţiată (nu se


pot declara obiecte având acest tip), în schimb se pot
declara pointeri la o clasă abstractă

 Metodele pur virtuale sunt destinate moştenirii:


 definesc o interfaţă pentru o anumită caracteristică
ce urmează a fi implementată într-o clasă derivată

68
class CPolygon {
protected:
int width, height;
public:
void set_values (int a, int b) {
width=a; height=b;
}

virtual float area (void) =0;

void print_area (void) {


cout << this->area( ) << endl;
}
}; 69
class CRectangle: public CPolygon {
public:
float area (void) {
return (width * height);
}
};

class CTriangle: public CPolygon {


public:
float area (void) {
return (width * height / 2.);
}
};
70
int main ( ) {
CRectangle rect;
CTriangle trgl;
CPolygon * ppoly1 = &rect;
CPolygon * ppoly2 = &trgl;
ppoly1->set_values (4,5);
ppoly2->set_values (4,5);
cout << "Apel direct pentru metoda pur virtuala\n";
cout << "\tArie dreptunghi: " << ppoly1->area( ) << endl;
cout << "\tArie triunghi: " << ppoly2->area( ) << endl;
cout << "Apel indirect pentru metoda pur virtuala\n";
cout << "\tArie dreptunghi: " ;
ppoly1->print_area( );
cout << "\tArie trunghi: " ;
ppoly2->print_area( );
71
cin.get();
}
5. Destructori virtuali

 În cazul utilizării pointerilor către clasa de bază care


referă obiecte din clasa derivată, pot să apară probleme
la eliminarea unor obiecte (cele derivate)
 Scopul declarării destructorilor virtuali este acela de a
se asigura apelul tuturor destructorilor implicaţi într-o
ierarhie, în ordinea corectă:
 destructorii claselor derivate vor deveni la rândul lor
virtuali

 Destructorii virtuali vin în contradicţie cu:


 scopul metodelor virtuale (de a fi moştenite şi
redefinite în clasele derivate)
 caracteristicile destructorilor (nu pot fi moşteniţi, ca
urmare nu pot fi ascunşi sau redefiniţi în clase 72
derivate)
Exemplul 1:
class Base {
// membri comuni
public:
Base( ) { cout<<"Constructor: Base"<<endl;}
~Base( ) { cout<<"Destructor : Base"<<endl;}
// virtual ~Base( ) { cout<<"Destructor : Base"<<endl;}
};

class Derived: public Base {


// membri specifici
public:
Derived( ) { cout<<"Constructor: Derived"<<endl;}
~Derived( ) { cout<<"Destructor : Derived"<<endl;}
};

void main( )
{
Base *Var = new Derived( );
// alte prelucrari 73
delete Var;
}
74
Exemplul 2:
enum Color {Co_red, Co_green, Co_blue};

// clasa de baza abstracta


class Shape {
protected:
int xorig;
int yorig;
Color co;
public:
Shape(int x, int y, Color c) : xorig(x), yorig(y), co(c) { }
virtual ~Shape( ) { } // destructor virtual
virtual void draw( ) = 0; // functie virtuala pura
};
75
// clasa linie (intre origine si un punct destinatie)
class Line : public Shape {
int xdest;
int ydest;
public:
Line(int x, int y, Color c, int xd, int yd) :
xdest(xd), ydest(yd), Shape(x, y, c) { }
~Line( ) {
cout << "~Linie\n";
} // destructor virtual
void draw( ) {
cout << "Linie" << "(";
cout << xorig << ", " << yorig << ", " << int(co);
cout << ", " << xdest << ", " << ydest;
cout << ")\n";
} 76
};
// Clasa cerc : cerc cu centru si raza
class Circle : public Shape {
int raza;
public:
Circle(int x, int y, Color c, int r) : raza(r), Shape(x, y, c) { }
~Circle( ) {
cout << "~Cerc\n";
} // destructor virtual
void draw( ) {
cout << "Cerc" << "(";
cout << xorig << ", " << yorig << ", " << int(co);
cout << ", " << raza;
cout << ")\n";
}
};
77
int main(void)
{
const int N = 4;
int i;
Shape* sptrs[N];
sptrs[0] = new Line(1, 1, Co_blue, 4, 5);
sptrs[1] = new Line(3, 2, Co_red, 9, 75);
sptrs[2] = new Circle(5, 5, Co_green, 3);
sptrs[3] = new Circle(3, 3, Co_red, 10);
for (i = 0; i < N; i++)
sptrs[i]->draw( );
for (i = 0; i < N; i++)
delete sptrs[i];
78
cin.get();
}

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