Documente Academic
Documente Profesional
Documente Cultură
Poo PDF
Poo PDF
PROGRAMARE ORIENTAT PE
OBIECTE
ndrumtor de laborator
Cuvnt nainte
Dorin Berian
Cuprins
Laboratorul 1:
Completri aduse de limbajul C++ fa de limbajul C 7
Laboratorul 2:
ncapsularea prin intermediul claselor . 13
Laboratorul 3:
Pointeri la metode. Funcii inline. Membri statici ... 17
Laboratorul 4:
Constructori i destructori ... 23
Laboratorul 5:
Funcii i clase prietene ... 29
Laboratorul 6:
Motenirea (derivarea) claselor ... 33
Laboratorul 7:
Metode virtuale. Utilizarea listelor eterogene ..................................... 41
Laboratorul 8:
Motenire multipl ... 49
Laboratorul 9:
abloane n C++ .. 55
Bibliografie .......... 61
Laborator 1 POO:
1. Conceptele POO
2. Intrri i ieiri
Limbajul C++ furnizeaz obiectele cin i cout, n plus fa de funciile scanf i printf din
limbajul C. Pe lng alte avantaje, obiectele cin i cout nu necesit specificarea formatelor.
Exemplu:
#include <iostream.h>
void main(void)
{
7
int a;
float b;
char c[20];
cout <<"Tastati un intreg : "<< endl;
cin >> a;
cout << "Tastati un numar real : " << endl;
cin >> b;
cout << "Tastati un sir de caractere : " << endl;
cin >> c;
cout << "Ati tastat " << a << ", " << b << ", " << c << ".";
cout << endl;
}
Suprancrcarea funciilor
Limbajul C++ permite utilizarea mai multor funcii care au acelai nume, caracteristic
numit suprancrcarea funciilor. Identificarea lor se face prin numrul de parametri i tipul lor.
Exemplu:
Dac se apeleaz suma (3,5), se va apela funcia corespunztoare tipului int, iar dac se
apeleaz suma (2.3, 9), se va apela funcia care are parametrii de tipul float. La apelul funciei
suma (2.3, 9), tipul valorii 9 va fi convertit automat de C++ n float (nu e nevoie de typecasting).
ntr-o funcie se pot declara valori implicite pentru unul sau mai muli parametri. Atunci
cnd este apelat funcia, se poate omite specificarea valorii pentru acei parametri formali care au
declarate valori implicite. Valorile implicite se specific o singur dat n definiie (de obicei n
prototip). Argumentele cu valori implicite trebuie s fie amplasate la sfritul listei.
Exemplu:
8
4. Operatorii new i delete
Limbajul C++ introduce doi noi operatori pentru alocarea dinamic de memorie, care
nlocuiesc familiile de funcii "free" i "malloc" i derivatele acestora.
Astfel, pentru alocarea dinamic de memorie se folosete operatorul new, iar pentru
eliberarea memoriei se folosete operatorul delete.
Operatorul "new" returneaz un pointer la zona de memorie alocat dinamic (dac alocarea
se face cu succes) i NULL dac alocarea de memorie nu se poate efectua.
Exemplu:
struct sistem
{
char nume[20];
float disc;
int memorie;
int consum;
};
void main(void)
{
x = new sistem;
x->disc = 850;
x->memorie = 16;
x->consum = 80;
delete x;
}
Exemplu:
9
Considernd un obiect x, prin "&x" se nelege referin la obiectul x.
Exemplu:
int n = 3; int n = 3;
ex (n); ex (n);
cout << n; - se va afia 3 cout << n; - se va afia 7
Referina nu este un pointer ctre obiectul referit, este un alt nume al obiectului!
Exemplu:
b) Plasarea declaraiilor
Limbajul C impune gruparea declaraiilor locale la nceputul unui bloc. C++ elimin acest
inconvenient, permind declaraii n interiorul blocului, de exemplu imediat nainte de utilizare.
Domeniul unei astfel de declaraii este cuprins intre poziia declaraiei i sfritul blocului.
void main(void)
{
int i;
cin >> i;
int j = 5*i-1; //declaratie i initializare de valoare
cout << j;
}
10
Limbajul C++ introduce operatorul de rezoluie (::), care permite accesul la un obiect (sau
variabil) dintr-un bloc n care acesta nu este vizibil, datorit unei alte declaraii.
void afiseaza(void)
{
char s[20] = variabila locala;
cout << ::s; //afiseaza variabila global
cout << s; //afiseaza variabila locala
}
d) Funcii inline
C++ ofer posibilitatea declarrii funciilor inline, care combin avantajele funciilor
propriu-zise cu cele ale macrodefiniiilor. Astfel, la fiecare apelare, corpul funciei declarate inline
este inserat n codul programului de ctre compilator.
Fa de macrodefiniii (care presupun o substitutie de text ntr-o faz preliminar
compilrii), pentru funciile inline compilatorul insereaz codul obiect al funciei la fiecare apel.
Avantajul creterii de viteza se pltete prin creterea dimensiunii codului. Aadar, funciile inline
trebuie s fie scurte.
Exemplu:
Aplicaia 1
Aplicaia 2
#include <iostream.h>
11
void main(void)
{
funcie(456, 4.5, 1.4, "apel 1");
funcie(456, 4.5, 1.4);
funcie(456, 4.5);
funcie(456.5);
}
Aplicaia 3
S se realizeze un program care calculeaz produsul a dou numere reale i a dou numere
complexe, specificate prin parte reala i parte imaginar. Funciile de calcul al produselor vor avea
acelai nume i parametri diferii.
ntrebri:
1. Ce este ncapsularea?
2. Ce este motenirea?
3. Ce este polimorfismul?
4. Care sunt funciile de intrare/ieire n C++?
5. Unde trebuiesc plasate argumentele cu valori implicite?
6. Ce nseamn suprancarcarea funciilor n Limbajul C++?
7. Care sunt operatorii de alocare i dezalocare de memorie n limbajul C++?
8. Ce este o referin ?
9. Referina este un pointer?
10. Ce este operatorul de rezoluie?
11. Unde se pot plas declaraiile de variabile n cadrul Limbajului C++?
12
Laborator 2 POO:
Scurt prezentare: Acest laborator prezint noiunile de clas i obiect, precum i aspecte
referitoare la:
- definirea unei clase;
- variabile i funcii membre;
- declararea obiectelor;
1. folosirea claselor pentru unirea structurile de date i a funciilor destinate manipulrii lor;
2. folosirea seciunilor private i publice, care fac posibil separarea mecanismului intern de
interfaa clasei;
O clas reprezint un tip de date definit de utilizator, care se comport ntocmai ca un tip
predefinit de date. Pe lng variabilele folosite pentru descrierea datelor, se descriu i metodele
(funciile) folosite pentru manipularea lor.
Instana unei clase reprezint un obiect - este o variabil declarat ca fiind de tipul clasei
definite.
Variabilele declarate n cadrul unei clase se numesc variabile membru, iar funciile declarate
n cadrul unei clase se numesc metode sau functii membru. Metodele pot accesa toate variabilele
declarate n cadrul clasei, private sau publice.
Membrii unei clase reprezint totalitatea metodelor i a variabilelor membre ale clasei.
specificator_clasa Nume_clasa
{
[ [ private : ] lista_membri_1]
[ [ public : ] lista_membri_2]
};
13
Numele clasei (Nume_clasa) poate fi orice nume, n afara cuvintelor rezervate limbajului C++.
Se recomand folosirea de nume ct mai sugestive pentru clasele folosite, precum i ca denumirea
claselor s nceap cu liter mare. (ex: class Elevi)
Folosind specificatorii de clas struct sau union se descriu structuri de date care au
aceleai proprieti ca i n limbajul C (neobiectual), cu cteva modificri :
se pot ataa funcii membru;
pot fi compuse din trei seciuni - privat, public i protejat (folosind specificatorii de acces
private, public i protected);
Diferena principal ntre specificatorii class, struct i union este urmtoarea: pentru o
clas declarat folosind specificatorul class, datele membre sunt considerate implicit de tip private,
pn la prima folosire a unuia din specificatorii de acces public sau protected. Pentru o clas
declarat folosind specificatorul struct sau union, datele membre sunt implicit de tip public, pn
la prima folosire a unuia din specificatorii private sau protected. Specificatorul protected se
folosete doar dac este folosit motenirea.
Descrierea propriu-zis a clasei const din cele doua liste de membrii, prefixate de cuvintele
cheie private i/sau public.
Membrii aparinnd seciunii public pot fi accesai din orice punct al domeniului de
existen al respectivei clase, iar cei care aparin seciunii private (att date ct i funcii) nu pot fi
accesai dect de ctre metodele clasei respective. Utilizatorul clasei nu va avea acces la ei dect prin
intermediul metodelor declarate n seciunea public (metodelor publice).
Definirea metodelor care aparin unei clase se face prefixnd numele metodei cu numele clasei,
urmat de ::. Simbolul :: se numete scope acces operator (operator de rezoluie sau operator
de acces) i este utilizat n operaii de modificare a domeniului de vizibilitate.
Exemplu:
class Stiva
{
int varf;
int st[30];
public:
void init (void);
};
n stnga lui :: nu poate fi dect un nume de clasa sau nimic, n cel de-al doilea caz
prefixarea variabilei folosindu-se pentru accesarea unei variabile globale (vezi laboratorul 1).
n lipsa numelui clasei n faa funciei membru nu s-ar putea face distincia ntre metode care
poart nume identice i aparin de clase diferite.
14
Exemplu:
class Stiva
{
public:
void init (void);
};
class Elevi
{
public:
void init (void);
};
Accesarea membrilor unui obiect se face folosind operatorul . Dac obiectul este accesat
indirect, prin intermediul unui pointer, se foloseste operatorul "->"
Dup cum s-a mai spus, variabilele membru private nu pot fi accesate dect de metode care
aparin clasei respective.
Aplicaia 1
S se scrie o aplicaie care implementeaz o stiv cu ajutorul unui tablou. Se vor implementa
funciile de adugare n stiv, scoatere din stiv, afiare a stivei (toate elementele).
Aplicaia 2
Clasa Lemon
private:
total numar lamai (se foloseste cate una la fiecare limonada)
15
total numar cuburi de zahar (cate 2 la fiecare limonada indulcita)
suma incasari (se incrementeaza cu pretul corespunzator)
public:
initializare (se specifica numarul de lingurite de zahar si de lamai disponibile)
bea o limonada indulcita (verificare: mai este zahar, mai este lamaie ?)
bea o limonada neindulcita (verificare: mai este lamaie?)
afisare total incasari
ntrebri:
1. Ce este ncapsularea?
2. Ce este o clasa?
3. Ce este un obiect?
4. Ce este o funcie membra?
5. Care este diferena ntre clase i structuri?
6. Pentru ce este utilizat "scope acces operator"?
7. Variabilele membru private pot fi accesate i n afara clasei respective?
16
Laborator 3 POO:
Beneficii:
- utilizarea pointerilor la metode aduce o flexibilitate sporit n conceperea programelor;
- utilizarea funciilor inline crete viteza de execuie a programelor;
- utilizarea membrilor statici ajut la reducerea numrului de variabile globale;
1. Pointeri la metode
Dei o funcie nu este o variabil, ea are o localizare n memorie, care poate fi atribuit
unui pointer. Adresa funciei respective este punctul de intrare n funcie; ea poate fi obinut
utilizndu-se numele funciei, fr nici un argument (similar cu obinerea adresei unei matrici).
Un pointer la metod desemneaz adresa unei funcii membru m. Metodele unei clase
au implicit un parametru de tip pointer la obiect, care se transmite ascuns. Din acest motiv,
parametrul n cauz nu apare in lista de parametri a funciei desemnate de pointer.
Exemplu:
class clasa
{
int contor;
public:
void init (int nr = 0)
{
contor = nr;
}
int increment (void)
{
return contor++;
}
};
/*
tipul "pointerLaMetoda" este un pointer la o metoda a clasei
"clasa", metoda care nu are parametri si care returneaza "int"
*/
void main(void)
{
clasa c1, *pc1 = &c1;
17
pointerLaMetoda pM = &(clasa :: increment);
c1.init (1);
pc1->init(2);
int i = (c1.*pM)();
i = (pc1->*pM)();
}
Dup cum se observ n acest exemplu, pointerul poate pointa ctre orice metod a
clasei clasa.
2. Funcii inline
Funciile inline sunt eficiente n cazurile n care transmiterea parametrilor prin stiv
(operaie lent) este mai costisitoare (ca i timp de execuie) dect efectuarea operaiilor din
corpul funciei.
Exemplu:
class coordonate_3D
{
int X,Y,Z;
// ATENTIE:
// prin definirea metodei in interiorul declararii clasei,
// functia membru devine implicit "inline" !!
18
Definirea unei funcii n cadrul unei clase se mai numete i declarare inline implicit.
Metoda tipareste este declarat explicit, ea fiind doar declarat n cadrul clasei, iar n locul
unde este definit este prefixat de cuvntul cheie inline.
Metodele care nu sunt membru al unei clase nu pot fi declarate inline dect explicit. Este
interzis folosirea n cadrul funciilor inline a structurilor repetitive (for, while, do while),
i a funciilor recursive.
3. Membri statici
Principala utilizare a variabilelor membru statice const n eliminarea n ct mai mare
msur a variabilelor globale utilizate ntr-un program.
Cuvntul cheie static poate fi utilizat n prefixarea membrilor unei clase. Odat declarat
static, membrul n cauz are proprieti diferite, datorit faptului c membrii statici nu
aparin unui anumit obiect, ci sunt comuni tuturor instanierilor unei clase.
Pentru o variabil membru static se rezerv o singur zon de memorie, care este
comun tuturor instanierilor unei clase (obiectelor).
Variabilele membru statice pot fi prefixate doar de numele clasei, urmat de operatorul de
de rezoluie ::. Apelul metodelor statice se face exact ca i accesarea variabilelor membru
statice. Deoarece metodele statice nu sunt apelate de un obiect anume, nu li se transmite pointer-
ul ascuns this.
Dac ntr-o metod static se folosesc variabile membru nestatice, e nevoie s se
furnizeze un parametru explicit de genul obiect, pointer la obiect sau referin la obiect.
Toi membrii statici sunt doar declarai n cadrul clasei, ei urmnd a fi obligatoriu
iniializai.
Exemplu
class exemplu
{
int i;
public:
static int contor; // variabila membru statica
static inc (void) {i++;} // metoda statica
void inc_contor (void) {contor++;}
void init (void) {i = 0;}
static void functie (exemplu *); // metoda statica
} ob1, ob2, ob3;
19
contor ++; // corect
}
void main(void)
{
ob1.init();
ob2.init();
ob3.init();
ob1.inc();
ob2.inc();
ob3.inc();
ob1.functie(&ob1); // corect
exemplu :: functie(&ob2); // corect
ob1.inc_contor();
ob2.inc_contor();
ob3.inc_contor();
exemplu :: contor+=6;
}
Aplicaia 1
ntrebri:
1. Ce sunt pointerii?
2. Ce sunt pointerii la metode?
3. Cum se declar pointerii la metode?
4. Ce sunt funciile inline?
20
5. Cum se declar funciile inline?
6. Care este avantajul folosirii funciilor inline?
7. Care sunt restriciile impuse funciilor inline?
8. Ce sunt membri statici?
9. Membri statici aparin unei clase?
10. Cum se declar u membru static?
Teme de cas:
Aplicaia 1
Implementai o coad de numere ntregi (structur de date de tip FIFO - first-in, first-
out), utilizndu-se o clas. Membrii variabili ai clasei indic vrful stivei i tabloul de ntregi n
care se rein elementele stivei. Funciile membru ale clasei vor fi:
- o funcie pentru iniializarea pointerului n stiv;
- o funcie pentru introducerea unei noi valori n stiv;
- o funcie pentru scoaterea unei valori din stiv;
Aplicaia 2
21
22
Laborator 4 POO:
Constructori i destructori
1. Constructori
Constructorul este o metod special a unei clase, care este membru al clasei
respective i are acelai nume ca i clasa. Constructorii sunt apelai atunci cnd se instaniaz
obiecte din clasa respectiv, ei asigurnd iniializarea corect a tuturor variabilelor membru
ale unui obiect i garantnd c iniializarea unui obiect se efectueaz o singur dat.
O clas poate avea orici constructori, ei difereniindu-se doar prin tipul i numrul
parametrilor. Compilatorul apeleaz constructorul potrivit n funcie de numrul i tipul
parametrilor pe care-i conine instanierea obiectului.
Tipuri de constructori
23
a. definind un constructor fr nici un parametru;
b. prin generarea sa implicit de ctre compilator. Un astfel de constructor este creat
ori de cte ori programatorul declar o clas care nu are nici un constructor. n
acest caz, corpul constructorului nu conine nici o instruciune.
Exemplu:
class X {
X (X&); // constructor de copiere
X (void); // constructor implicit
};
2. Destructorii
Destructorul este complementar constructorului. Este o metod care are acelai nume
ca i clasa creia i aparine, dar este precedat de ~. Dac constructorii sunt folosii n
special pentru a aloca memorie i pentru a efectua anumite operaii (de exemplu:
incrementarea unui contor al numrului de obiecte), destructorii se utilizeaz pentru eliberarea
memoriei alocate de constructori i pentru efectuarea unor operaii inverse (de exemplu:
decrementarea contorului).
Exemplu:
class exemplu
{
public :
exemplu (); // constructor
~exemplu (); // destructor
};
24
1. prin specificarea explicit a numelui su metoda direct;
Exemplu:
class B
{
public:
~B();
};
#define NULL 0
struct s
{
int nr;
struct s *next;
};
class B
{
int i;
struct s *ps;
public:
B (int);
~B (void);
};
B :: B (int ii = 0) // 3 si 7
{
ps = new s; ps->next = NULL; i = ps->nr = ii; // 4 si 8
} // 5 si 9
B :: ~B (void) // 11 si 14
{
delete ps; // 12 si 15
} // 13 si 16
25
ntrebri:
1. Ce sunt constructorii?
2. Cum se declar constructorii?
3. Ce tip de dat poate returna un constructor?
4. Constructorii pot avea parametri?
5. Cum se apeleaz constructorii?
6. Ce sunt constructorii de copiere?
7. O clasa poate avea mai muli constructori? Dac da, atunci cum tie compilatorul s fac
diferenierea ntre acetia?
8. Ce este un destructor?
9. Ci destructori poate avea o clas?
10. Cum se poate apela un destructor?
Aplicaia 1
class node
{
static node *head; //pointer la lista
node *next; //pointer catre urmatorul
//element
char *interpret; //numele unui obiect din lista
char *melodie; //numele unei melodii din
//lista
public:
node (char * = NULL); //declararea constructorului
void display_all (); //afiseaza nodurile listei
void citireAlbum (); //citirea informatiilor despre
//album
};
26
void main()
{
}
Teme de cas:
Aplicaia 1
Aplicaia 2
27
28
Laborator 5 POO:
1. Funcii friend
O funcie friend este o funcie care nu e membru a unei clase, dar are acces la membrii
de tip private i protected ai clasei respective. Orice funcie poate fi friend unei clase,
indiferent dac este o funcie obinuit sau este membru al unei alte clase.
Exemplu:
class exemplu {
int a;
int f (void);
friend int f1(exemplu &);
public:
friend int M::f2(exemplu &, int);
};
Dup cum se observ, nu conteaz dac o funcie este declarat friend n cadrul
seciunii private sau public a unei clase.
2. Clase friend
Dac se dorete ca toi membrii unei clase M s aib acces la partea privat a unei
clase B, n loc s se atribuie toate metodele lui M ca fiind friend ai lui B, se poate
declara clasa M ca i clas friend lui B.
29
Exemplu:
class M {
// ...
};
class B
{
// ...
friend class M;
};
Relaia de friend nu este tranzitiv, adic dac clasa A este friend clasei B, iar clasa B
este friend clasei C, aceasta nu implic faptul c, clasa A este implicit friend clasei C.
Exemplu:
class rational; // declarare incompleta
class complex
{
double p_reala, p_imaginara;
friend complex& ponderare (complex&, rational&);
public:
complex (double r, double i) : p_reala (r), p_imaginara (i)
double get_real (void) {return p_reala;}
double get_imaginar (void) {return p_imaginara;}
};
class rational
{
int numarator, numitor;
double val;
public:
friend complex& ponderare (complex& ,rational&);
rational (int n1, int n2) : numarator (n1)
{
numitor = n2!=0 ? n2 : 1;
val = ((double)numarator)/numitor;
}
double get_valoare(void) { return val; }
};
// fiind "friend", functia ponderare are acces la membrii privati ai
// claselor "complex" si "rational"
30
// nefiind "friend", "ponderare_ineficienta" nu are acces la membrii
// privati ai claselor "complex" si "rational"
void main(void)
{
complex a(2,4),b(6,9);
rational d(1,2),e(1,3);
a = ponderare(a,d);
b = ponderare(b,e);
a = ponderare_ineficienta(a,d);
b = ponderare_ineficienta(b,e);
}
ntrebri:
Teme de cas
Aplicaia 1
31
nod din lista de CD-uri. De asemenea se vor folosi funcii prietene pentru accesul la membri
privai ai clasei
Aplicaia 2
32
Laborator 6 POO:
Motenirea (derivarea) claselor
Considernd o clas oarecare A, se poate defini o alt clas B, care s preia toate
caracteristicile clasei A, la care se pot aduga altele noi, proprii clasei B. Clasa A se
numete clas de baz, iar clasa B se numete clas derivat. Acesta este numit
mecanism de motenire.
n declaraia clasei derivate nu mai apar informaiile care sunt motenite, ele
fiind automat luate n considerare de ctre compilator. Nu mai trebuie rescrise funciile
membru ale clasei de baz, ele putnd fi folosite n maniera n care au fost definite. Mai
mult, metodele din clasa de baz pot fi redefinite (polimorfism), avnd o cu totul alt
funcionalitate.
Exemplu:
//clasa "hard_disk" este derivata din clasa "floppy_disk"
class floppy_disk
{
protected: // cuvantul cheie "protected" permite
// declararea unor membrii nepublici, care
// sa poata fi accesati de catre
// eventualele clase derivate din
// "floppy_disk"
stare_protectie_la_scriere indicator_protectie;
int capacitate, nr_sectoare;
public:
stare_operatie formatare ();
stare_operatie citeste_pista (int drive, int
sector_de_start, int numar_sectoare, void *buffer);
stare_operatie scrie_pista (int drive, int
sector_de_start, int numar_sectoare, void *buffer);
stare_operatie protejeaza_la_scriere(void);
};
33
stare_operatie hard_disk :: parcheaza_disc ()
{
// CORECT: accesarea unui membru de tip "protected"
indicator_protectie = PROTEJAT;
//...
return REUSIT;
}
void functie ()
{
hard_disk hd1;
hd1.formatare(); //CORECT
hd1.indicator_protectie = NEPROTEJAT; // EROARE: incercare
// de accesare a unui membru protejat
}
Prin enunul:
[lista_obiecte];
34
protected ntlnii n cadrul declarrii clasei de baz, s stabileasc drepturile de
accesare a membrilor motenii de ctre o clas derivat.
O alt observaie este aceea c orice friend (funcie sau clas) a unei clase
derivate are exact aceleai drepturi i posibiliti de a accesa membrii clasei de baz ca
oricare alt membru al clasei derivate.
Exemplu:
class Angajat
{
private:
char nume[30];
// alte caracteristici: data de natere, adresa etc.
public:
Angajat ();
Angajat (const char *);
char *getname () const;
double calcul_salariu (void);
};
35
double comision;
double vnzri;
public:
Vnztor (const char *nm);
void setare_comision (double comis);
void setare_vnzri (double vanz);
double calcul_plata ();
};
Cuvntul cheie const folosit dupa lista de parametri ai unei funcii declar
funcia membru ca i funcie read-only funcia respectiv nu modific obiectul
pentru care este apelat.
O instaniere a unei clase derivate conine toti membrii clasei de baz i toi
acetia trebuie iniializai. Constructorul clasei de baz trebuie apelat de constructorul
clasei derivate.
36
// constructorul clasei Vnztor
Vnztor::Vnztor(const char *nm) : Angajat_plata_ora(nm)
{
commision = 0.0;
vnzri = 0.0;
}
De exemplu:
Muncitor mn;
Vnzator vanz ("Popescu Ion");
mn = vanz; // conversie derivat => baz
37
Muncitor *munc = &munc_1;
Vanzator *vanz;
vanz = (Vanzator *) munc;
38
void main ()
{
// se declara cite o instanta a fiecarei clase
// sa se afiseze informatiile referitoare la fiecare
// persoana declarata
}
ntrebri:
1. Ce este motenirea?
2. Cum se realizeaz motenirea?
3. Care sunt drepturile de acces la membrii unei clase de baz n funcie de tipul
motenirii?
4. Ce sunt constructorii?
5. Cum se apeleaz constructorii clasei de baz?
6. Care constructor se apeleaz primul, al clasei de baz sau al clasei derivate?
7. Cum se fac conversiile ntre clasa de baz i clasa derivat?
8. Cnd este folosit conversia implicit?
9. Cnd este necesar conversia explicit?
10. Cum se poate apela un destructor?
Teme de cas:
Aplicaia 1
Aplicaia 2
39
40
Laborator 7 POO:
Metode virtuale. Utilizarea listelor eterogene
Beneficiul:
- utilizarea metodelor virtuale va permite redefinirea / reutilizarea metodelor
- utilizarea metodelor virtuale ne va permite s crem i s utiliz liste eterogene
Prezentarea laboratorului:
Prin definiie, un tablou omogen este un tablou ce conine elemente de acelai tip. Un
pointer la o clasa "B" poate pstra adresa oricrei instanieri a vreunei clase derivate din
"B". Deci, avnd un ir de pointeri la obiecte de tip "B" , nseamn c, de fapt, putem
lucra i cu tablouri neomogene. Astfel de tablouri neomogene se vor numi
ETEROGENE. Una dintre caracteristicile limbajului C++ const tocmai n faptul c
mecanismul funciilor virtuale permite tratarea uniform a tuturor elementelor unui masiv
de date eterogene. Acest lucru este posibil datorit unor faciliti ce in de asocieri fcute
doar n momentul execuiei programului, nu n momentul compilrii.
Fie o clas "B" care posed o metod public "M". Din clasa "B" se deriv mai multe
clase "D1", "D2", "D3",...,"Dn". Dac aceste clase derivate redefinesc metoda "M" se
pune problema modului n care compilatorul este capabil s identifice corect fiecare
dintre aceste metode.
n mod obinuit identificarea funciei membru n cauz se poate face prin una din
urmtoarele metode:
1. prin unele diferene de semnatur (n cazul n care metoda a fost redeclarat)
2. prin prezena unui scope resolution operator (o exprimare de genul int
a=B::M() este univoc)
3. cu ajutorul obiectului cruia i se aplic metoda. O secven de genul:
D1 d;
d.M();
nu las nici un dubiu asupra metodei n cauz.
n aceste cazuri, decizia este deosebit de simpla i poate fi luat chiar n faza de
compilare.
n cazul prelucrrii de liste eterogene situaia este mai complicat, fiind implicat o
rezolvare mai trzie a asociaiilor ntre numele funciei apelate i funcia de apelat. Fie
41
irul eterogen de pointeri la obiecte de tipul "B", "D1", "D2" etc. Se presupune, de
exemplu, c ntr-o procedur se ncearc apelarea metodei "M" pentru fiecare obiect
pointat de catre un element al irului. Metoda de apelat nu va fi cunoscut n momentul
compilrii deoarece nu este posibil s se stabileasc corect despre care din funcii este
vorba ("M"-ul din "B" sau cel al unei clase derivate din "B"). Imposibilitatea identificrii
metodei apare deoarece informaiile privind tipul obiectului la care pointeaz un element
al irului nu vor fi disponibile dect n momentul executrii programului.
n continuare se analizeaz un exemplu clasic de tratare a unei liste neomogene, care apoi
este reluat utiliznd funcii virtuale.
Exemplul :
Se construiete un ir de 4 elemente ce conine pointeri att la clasa "BAZA" ct i la
"DERIVAT_1" i "DERIVAT_2".
#include <iostream.h>
typedef enum {_BAZA_, _DERIVAT_1, _DERIVAT_2} TIP;
class BAZA{
protected:
int valoare;
public:
void set_valoare(int a) (valoare = a}
void tipareste_valoare(void)
{
cout<<"Element BAZA cu VALOARE = "<<valoare<<"\n";
}
};
42
class LISTA_ETEROGENA{
BAZA *pB;
TIP t;
public:
void set(BAZA *p, TIP tp=_BAZA_)
{
pB = p; t = tp;
}
void tipareste_valoare(void)
{ if(t==_BAZA_)
pB->tipareste_valoare();
else
if(t==_DERIVAT_1)
((DERIVAT_1 *) pB->tipareste_valoare();
else
((DERIVAT_2 *) pB->tipareste_valoare();
}
};
void main()
{
BAZA a[2];
DERIVAT_1 b1;
DERIVAT_2 b2;
LISTA_ETEROGENA p[4];
a[0].set_val(1);
a[1].set_val(2);
b1.set_val(3);
b2.set_val(4);
p[0].set(&a[0]);
p[1].set(&b1,_DERIVAT_1); //se face o conversie implicita
//"DERIVAT_1"->"BAZA*"
p[2].set((BAZA *)&b2, _DERIVAT_2); //se face o conversie explicita
//"DERIVAT_2"->"BAZA *"
p[3].set(&a[1]);
for(int i=0; i<4; i++)
p[i].tipareste_valoare();
}
43
Pentru a reui o tratare uniform a celor 3 tipuri de obiecte a fost necesar crearea unei a
4-a clase, numit LISTA_ETEROGENA. Aceasta va pstra att pointer-ul la obiectul
respectiv, ct i o informaie suplimentar, referitoare la tipul obiectului referit de pointer.
Tiprirea informaiilor semnificative ale fiecarui obiect se va face n metoda
LISTA_ETEROGENA::tipareste_valoarea. n aceast funcie membru sunt necesare o
serie de teste pentru apelul metodei corespunznd fiecarui tip de obiect.
Exemplul:
Se prezint o modalitate mult mai simpl i elegant de a trata omogen un masiv de date
eterogene.
#include <iostream.h>
class BAZA{
protected:
int valoare;
public:
void set_val(int a) { valoare = a;}
virtual void tipareste_valoare(void)
{
cout<<"Element BAZA cu VALOARE = "<<valoare<<"\n";
}
};
class LISTA_ETEROGENA {
BAZA *pB;
public:
void set(BAZA *p) {pB = p;}
44
void tipareste_valoare(void)
{
pB->tipareste_valoare();
}
};
void main()
{
BAZA a[2];
DERIVAT_1 b1;
DERIVAT_2 b2;
LISTA_ETEROGENA p[4];
a[0].set_val(1);
a[1].set_val(2);
b1.set_val(3);
b2.set_val(4);
p[0].set(&a[0]);
p[1].set(&b1);
p[2].set((BAZA *) &b2);
p[3].set(&a[1]);
Prefixarea unei metode "F" a clasei "B" cu cuvntul cheie virtual are urmtorul efect:
toate clasele ce o motenesc pe "B" i nu redefinesc funcia "F" o vor moteni ntocmai.
Dac o clas "D" derivat din "B" va redefini metoda "F" atunci compilatorul are sarcina
de a asocia un set de informaii suplimentare, cu ajutorul crora se va putea decide (n
momentul execuiei) care metod "F" va fi apelat.
Observaii:
1. Deoarece funciile virtuale necesit memorarea unor informaii suplimentare,
instanierile claselor care au metode virtuale vor ocupa n memorie mai mult loc dect ar
fi necesar n cazul n care nu ar exista dect metode ne-virtuale.
Nici FUNCIILE NE_MEMBRU i nici METODELE STATICE NU POT FI
DECLARATE VIRTUALE.
45
2. Pentru ca mecanismul metodelor virtuale s poata funciona, metoda n cauz NU
POATE FI REDECLARAT n cadrul claselor derivate ca avnd aceiai parametrii i
returnnd un alt tip de dat. n schimb, este permis redeclararea cu un alt set de
argumente, dar cu acelai tip de dat returnat. dar n aceast situaie, noua funcie nu va
mai putea fi motenit mai departe ca fiind metod virtual.
3. Evitarea mecanismului de apel uzual pentru funciile virtuale se face foarte uor prin
prefixarea numelui funciei cu numele clasei aparintoare urmat de "::"
Exemplu:
void DERIVAT::f1(void)
{
BAZA::tipareste_valoare(); //apelul metodei din clasa BAZA
tipareste_valoare(); //apelul metodei din clasa DERIVAT
}
4. Constructorii nu pot fi declarai virtual, nsa destructorii accept o astfel de prefixare.
5. O funcie virtual "F", redefinit ntr-o clas derivat "D" va fi vzut n noua sa form
de ctre toate clasele derivate din "D". Aceast redefinire nu afecteaz cu nimic faptul c
ea este virtual, metoda rmnnd n continuare virtuala i pentru eventualele clase
derivate din "D".
6. Cnd se pune problema de a decide dac o metod sa fie sau nu virtual, programatorul
va lua n considerare urmtoarele aspecte:
- n cazul n care conteaz doar performanele unui program i se exclude
intenia de a-l mai dezvolta n continuare, se va opta pentru metode ne-
virtuale.
- n cazul programelor ce urmeaz s suporte dezvoltri ulterioare, situaia este
cu totul alta. Vor fi declarate virtuale toate acele metode ale clasei "CLASA"
care se consider c ar putea fi redefinite n cadrul unor clase derivate din
"CLASA" i, n plus, modificarea va trebui s fie sesizabil la "nivelul" lui
"CLASA". Toate celelalte metode pot rmne ne_virtuale.
Aplicaia 1:
Dndu-se clasa de baz Lista, s se construiasc dou clase derivate Stiva i Coada,
folosind funciile virtuale store() (adaug un element n stiv sau coad) i retrieve()
(terge un element din stiv sau coad).
ntrebri:
46
4. Cum se va face diferenierea ntre metodele(redefinite) claselor derivate?
5. Pot fi declarate ca fiind virtuale funciile nemembre?
6. Pot fi declarate ca fiind virtuale metodele statice ale unei clase?
7. Pot fi declarai constructorii ca fiind virtuali?
8. Diverse aspecte privitoare la membri virtuali.
Teme de cas:
Aplicaia1
Dndu-se clasa de baza Lista, s se construiasc dou clase derivate ListaNeordonata i
ListOrdonata, folosind funciile virtuale pentru adugare a unui element n list i
pentru afiarea elementelor unei liste.
Aplicaia2
Dndu-se clasa de baza Lista, s se construiasc dou clase derivate ListaSimpl i
ListDubl, folosind funciile virtuale pentru adugare a unui element n list i pentru
afiarea elementelor unei liste.
47
48
Laborator 8 POO:
Motenire multipl
Prezentarea laboratorului:
Exemplu:
class B1
{
//...
}
class B2
{
//...
}
class B3
{
//...
}
class D1 : B1
{
B2 b1;
B3 b2;
};
Din punct de vedere al structurii de date, clasa D1 este echivalenta cu forma:
class D1 : B1, B2, B3
{
//...
};
Din punct de vedere funcional exist totui diferene, cum ar fi, de exemplu, modul de apel al
metodelor claselor membre. Motenirea multipl elimin aceste neajunsuri. Astfel, o clas de
derivat poate avea simultan mai multe clase de baz.
49
2. O clas derivat D nu poate moteni simultan o clas B i o alt D1, derivat tot din "B". De
asemenea, nu pot fi motenite simultan dou clase D1 i D2 aflate n relaia de motenire B ->... -
>D1->...->D2.
Exemplu:
class B
{
//...
};
class C1 : B
{
//
};
class C2 : C1
{
//
};
class D1 : B, C2 //EROARE
{
//
};
class D2 : C1, C2 //EROARE
{
//
};
n schimb, este corect o motenire de genul :
class B
{
//...
};
class D1 : B
{
//
};
class D2 : B
{
//
};
class D : D1, D2
{
//
};
Pentru a realiza o motenire multipl este suficient s se specifice dup numele clasei derivate o
list de clase de baz separate prin virgul (evident, aceste nume pot fi prefixate cu modificatori
de acces public sau privat).
Ordinea claselor de baz nu este indiferent. Apelul constructorilor respectivi se va face exact n
ordinea enumerrii numelor claselor de baz. Ca i n cazul motenirii simple, apelul
50
constructorilor tuturor claselor de baz se va efectua naintea eventualelor iniializri de variabile-
membru. Mai mult, aceasta ordine nu poate fi modificat nici chiar de ordinea apelului explicit al
constructorilor claselor de baz n cadrul constructorilor clasei derivate.
Exemplu:
class B1
{
char c;
public :
B1(char c1) : c(c1) {};
};
class B2
{
int c;
public :
B2(int c1) : c(c1) {};
};
class D : B1, B2
{
char a, b;
public :
D(char a1, char b1) : a(a1), b(b1)
B2((int) a1), B1(b)
{
//...
}
};
void main(void)
{
D d(3,3);
}
Indiferent de ordinea iniializrii variabilelor i de cea a constructorilor specificai explicit, n
definirea constructorului clasei derivate:
D(char a1,char b1) : a(a1),b(b1)
B2((int) a1),B1(b1) {};
succesiunea operaiilor va fi urmtoarea : apelul constructorului B1, apoi B2 i, doar la sfrit,
iniializarea lui a i b.
Exemplu :
class B
{
//
};
51
class D1 : B
{
//...
};
class D2 : B
{
//
};
class M1 : D1,public D2
{
//
};
n urma motenirii claselor D1 i D2, clasa M1 va ngloba dou obiecte de tipul B (cte unul
pentru fiecare din cele dou clase de baz).
n practic, ar putea exista situaii n care este de dorit motenirea unui singur obiect de tip B.
Pentru aceasta se va proceda ca i n cazul clasei M2: se vor prefixa cele dou clase de baz cu
cuvntul cheie virtual. n urma acestei decizii, clasa M2 nu va conine dect O SINGURA
INSTANTIERE A CLASEI "B".
Cui aparine aceast unic instaniere (lui D1 sau D2)? Instanierea n cauz va aparine lui D1,
adic primei clase virtuale (din lista claselor de baz) care a fost derivat din B.
52
Partea practic a laboratorului:
Aplicaia1
Studenii vor dezvolta structura de mai jos:
class Location {
protected:
int X;
int Y;
public:
Location(int InitX, int InitY) {X = InitX; Y = InitY;}
int GetX() {return X;}
int GetY() {return Y;}
};
class Circle : public Point { // Derivata din class Point care este
// derivata din class Location
protected:
int Radius;
public:
Circle(int InitX, int InitY, int InitRadius);
void Show();
void Hide();
void Expand(int ExpandBy);
void Contract(int ContractBy);
};
Aplicaia2
Aplicaia de mai sus va fi extins prin crearea claselor Line i Poligon.
53
ntrebri:
1. Ce este motenirea?
2. Care sunt mecanismele motenirii?
3. Ce este motenirea multipl?
4. O clas poate motenii mai multe clase de baz?
5. Ordinea claselor de baz, motenite de o clas, este importan?
6. Ce sunt constructorii de copiere?
7. Ce sunt clasele virtuale?
8. Cum se apeleaz constructorii claselor derivate?
9. Ordinea de apel a constructorilor claselor derivate este important?
Teme de cas:
Aplicatia1
S se dezvolte, similar cu aplicaia prezentat la laborator, o aplicaie pentru clasele: Om,
Student, OnCampus i OffCampus. Clasele Om i Student sunt considerate clase de baz.
Aplicaia2
Aceleai cerine pentru clasele: Roat, Vehicul, AutoLimuzina i AutoTransport. Clasele Roat i
vehicul vor fi considerate clase de baz.
1. Octavian Catrina, Iuliana Cojocaru, "TURBO C++", Editura Teora, Bucureti 1993.
2. Vasile Stoicu-Tivadar, Programare Orientata pe Obiecte, Editura Orizonturi
Universitare, Timioara 2000, pp. 147-167, 223-259.
54
Laborator 9 POO:
abloane n C++
Ce sunt abloanele?
abloanele sunt folosite cu clase i funcii care accept parametri la folosirea clasei. Un
ablon de clas sau de funcie poate fi asimilat unui bon de comand care include
cteva spaii albe care se completeaz la expedierea comenzii, n loc de a crea o clas
tip list pentru matrice de pointeri i o alt clas de acelai tip pentru char, va putea fi
folosit o singur clas ablon drept clas tip list.
De ce s folosim abloanele?
abloanele sunt foarte utile n colecii, deoarece o singur clas tip colecie bine
conceput care folosete abloane poate fi imediat refolosit pentru toate tipurile
incorporate. n plus, utilizarea abloanelor pentru clasele tip colecie contribuie la
eliminarea unuia dintre motivele principale care necesit folosirea conversiilor forate.
CStack<int> stackOfInt;
55
Listing 1. Utilizarea cuvntului cheie typedef pentru a simplifica declaraiile de ablon.
Fig. 2.
// Constructori
56
template <class T> CStack<T>::CStack ()
{
m_p = 0;
m_nStored = 0;
m_nDepth = 0;
}
// Operaii
template <class T> int CStack<T>::IsEmpty () const
{
return m_nStored == 0;
}
Declaraia unui ablon cere compilatorului s utilizeze un tip care va fi precizat mai
trziu la multiplicarea efectiv a ablonului. La nceputul declaraiei se folosete
urmtoarea sintax:
int CStack<T>::IsEmpty ()
{
return m_nStored;
57
}
Deoarece CStack este o clas ablon, sunt necesare informaiile referitoare la ablon. O
alt modalitate de definire a funciei IsEmpty este inserarea informaiilor despre ablon
ntr-o linie separat, nainte de restul funciei.
# include <afx.h>
# include <iostream.h>
# include "stack.h"
58
void SchArg (T& foo , T& bar)
{
T temp;
temp = foo;
foo = bar;
bar = temp;
}
Utilizarea unei funcii ablon este foarte simpl. Compilatorul se ocup de toate.
Exemplul din listingul 5 afieaz un mesaj nainte i dup apelarea funciei ablonului
SchArg.
# include <afx.h>
# include <iostream.h>
# include "scharg.h"
int main ()
{
char *szMsjUnu ("Salut");
char *szMsjDoi ("La Revedere");
cout <<szMsjUnu << "\t" <<szMsjDoi <<endl;
SchArg (szMsjUnu, szMsjDoi);
cout <<szMsjUnu << "\t" <<szMsjDoi <<endl;
return EXIT_SUCCES;
}
Aplicaia 1:
S se scrie o aplicaie C++ care conine o clas ablon pentru implementarea unei liste
simplu nlnuite. Lista va putea avea noduri de orice tip de data fundamental (char, int,
double etc.). Se vor implementa funcii pentru adugarea, tergerea i cutarea unui
nod din list, precum i o funcie pentru afiarea listei.
Aplicaia 2:
59
60
Bibliografie:
61