Sunteți pe pagina 1din 44

Capitolul 1.

noiuni de programare obiectual

11

1. Acum ncepe greul ... (noiuni de programare obiectual)

n anii 60 s-au dezvoltat tehnicile de programare structurat. Conform celebrei ecuaii a lui Nicklaus Wirth:
algoritmi + structuri de date = programe

un program este format din dou pri total separate : un ansamblu de proceduri i funcii; un ansamblu de date asupra crora acioneaz practic; Procedurile sunt prezente ca i cutii negre, fiecare avnd de rezolvat o anumit sarcin (de fcut anumite prelucrri). Aceast modalitate de programare se numete programare structurat. Evoluia calculatoarelor i a problemelor de programare, a fcut ca n aproximativ 10 ani, programarea structurat s devin ineficient. Astfel, ntr-un program bine structurat n proceduri, este posibil ca o schimbare relativ minor n structura datelor, s provoace o deranjare major a procedurilor. Programarea structurat este tehnica pe care ai abordat-o, ncepnd de la conceperea primei organigrame i terminnd cu cele mai complexe programe pe care le-ai elaborat pn n acest moment, presupunnd bineneles c, citind aceste rnduri, nu suntei nc fani ai programrii obiectuale. Scopul acestui capitol este de a v deprinde cu o tehnic nou, mult mai tnr dect programarea structurat, tehnic ce permite o concepie mult mai apropiat de natural a programelor. Aceasta este Programarea Orientat pe Obiecte (POO). Dar nainte de a ncepe s ridicm ceaa ce nconjoar aceast tehnic nou, s facem cunotin cu un nou mediu de programare, care va fi utilizat de acum nainte pentru implementarea programelor. Acest mediu este Microsoft Visual C++ 6.0. 1.1. Un mediu de programare nou? Oare cum arat? Visual C++ 6.0 face parte dintr-un ansamblu de programe, numit Visual Studio 6.0. Acesta este implementarea firmei Microsoft i pentru alte limbaje pe lng C++, cum ar fi FoxPro, Basic, Java, etc. Utilizarea acestui mediu de programare are mai multe avantaje: posibilitatea de creare de programe n diferite limbaje, care s interacioneze ntre ele; utilizarea unor obiecte native Windows, ceea ce duce la crearea de programe executabile de dimensiuni relativ mici; posibilitatea utilizrii de biblioteci complexe, ce pun la dispoziia programatorului metode de rezolvare a marii majoriti a problemelor; posibilitatea utilizrii de programe vrjitor (Wizard) care ghideaz programatorul n implementarea programului. Cum pornim Visual C++? Dac avei mediul de programare instalat pe calculatorul dumneavoastr, vei putea lansa mediul de programare astfel: alegei

H. Vlean, 2004

12

Visual C++. Programarea Interfeelor Utilizator

din meniu Start->Programs->Microsoft C++ 60 (fig. 1.1);

Visual Studio 6.0->Microsoft Visual

Fig. 1.1 Lansarea n execuie a Visual C++ 6.0

O dat lansat programul, acesta se prezint i v afieaz o caset cu diferite mecherii utile n scrierea programelor sau utilizarea mediului (fig. 1.2). Ca s putei lucra, va trebui (eventual dup ce citii o serie de astfel de informaii) s nchidei caseta apsnd butonul OK.

Fig. 1.2. Aa ncepe totul ...

n Visual C++, orice program executabil rezult n urma compilrii i editrii legturilor n cadrul unui Proiect (Project). Un proiect este o entitate constituit din mai multe fiiere header, surs, de resurse, etc, care conin toate informaiile necesare generrii programului executabil. Acesta, va rezulta ca un fiier cu acelai nume cu numele proiectului. O alt noiune nou introdus de mediul de programare este Spaiul de lucru (Workspace). Acesta este constituit din totalitatea fiierelor, utilitarelor,

Capitolul 1. noiuni de programare obiectual

13

dispozitivelor, etc, puse la dispoziia programatorului n cadrul ferestrei Visual C++, pentru a-l ajuta la crearea programului. O s constatai c, dac ai lucrat la un program i ai nchis mediul de lucru, la revenirea n acelai program totul va fi identic cu momentul n care l-ai nchis. Aceasta deoarece spaiul de lucru este salvat ntr-un fiier (cu extensia .dsw) i informaiile din acesta reconstituie n mod identic spaiul de lucru la orice nou accesare. n mod normal, fiecare spaiu de lucru are n interiorul lui un singur proiect, dar se pot aduga mai multe proiecte la acelai spaiu de lucru. Cum deschidem deci un proiect nou? Foarte simplu: n meniul File alegei opiunea New... i vei fi invitai s alegei tipul de proiect dorit (fig. 1.3).

Numele proiectului

Directorul n care se creeaz proiectul

Tipuri de proiecte

Fig. 1.3. Crearea unui nou proiect

Dup cum se poate vedea, exist mai mute tipuri de proiecte ce pot fi create. Ne vom referi n acest moment la dou, pe care le vom utiliza n capitolele ce urmeaz. 1.1.1 Win32 Console Application. sta nu e DOS? Vom ncerca nti s crem un proiect consol. Asta nseamn c vom obine un program care s ruleze ntr-un ecran MsDos, cu toate c nu se respect regulile de compunere a adreselor din acest btrn sistem de operare, adic ceea ce tim: pointeri pe 16 bii. Acum pointerii sunt pe 32 de bii, dar i vom folosi la fel ca nainte. Pentru aceasta, vom alege opiunea Win32 Console Application, i haidei s scriem n caseta Project name numele proiectului: Primul. O dat ales numele proiectului i apsat pe butonul OK, mediul de programare va dori s tie cum trebuie s creeze proiectul: s ne lase pe noi s lum totul de la 0, sau s ne ofere nite abloane de programare, cu anumite biblioteci gata incluse (fig. 1.4). Vom alege opiunea An empty project, adic va trebui s populm noi proiectul cu diferite fiiere nainte de a putea compila ceva. Apoi apsm butonul Finish pentru a ncheia procesul de generare a proiectului. nainte de final, mediul ne va mai afia o pagin de informaii, n care ne va spune ce am lucrat pn n acest moment, adic aproape nimic. Dup apsarea butonului OK, proiectul este gata s fie utilizat.

H. Vlean, 2004

14

Visual C++. Programarea Interfeelor Utilizator

Fig. 1.4. Crem un proiect gol. O s avem ceva de lucru...

Dac o s v uitai n acest moment la structura de directoare, vei constata c n directorul ales de dumneavoastr. n caseta Location, a fost creat un director cu numele Primul, care conine mai multe fiiere asociate proiectului. Nu vei gsi nici un fiier surs (.cpp) sau header (.h). Acestea trebuie inserate n proiect. Pentru aceasta, din nou n meniul File vom alege opiunea opiunea New.... De aceast dat, mediul ne va permite s populm proiectul cu diferite fiiere. Vom alege opiunea C++ Source File i vom da numele fiierului surs tot Primul (fig. 1.5). Trebuie s avem grij ca opiunea Add to project: s fie validat, n caz contrar fiierul nou creat nu va fi adugat proiectului i va trebui s-l adugm manual.

Fiier header Fiier surs Numele fiierului

Dac nu e validat, vom aduga manual fiierul la proiect

Fig. 1.5. Vom aduga i un fiier surs

Gata! Dac apsm OK, fiierul Primul.cpp e adugat directorului Primul i ateapt s fie completat cu codul surs.

Capitolul 1. noiuni de programare obiectual

15

Haidei s scriem urmtorul program:


#include <iostream.h> void main() { char nume[20]; char prop1[]="\n Salut "; char prop2[]="\n Ai scris primul program VC++!\n\n"; cout << "\n Cum te numesti : " ; cin >> nume; cout << prop1 << nume << prop2;

E un program care nu difer cu nimic de ce tim pn acuma. Va trebui s-l compilm. Pentru acesta, vom alege n meniu opiunea Build->Rebuild All (fig. 1.6), iar dac nu avem erori (ar trebui s nu avem!) vom lansa programul n execuie apsnd pe butonul !.

Compilare Execuie

Aici ne afieaz erorile!

Fig. 1. 6. Compilm i lansm n execuie

Execuia programului se face ntr-o fereastr DOS ce arat ca n fig. 1.7.

Fig. 1.7. Aa arat un program n consol

H. Vlean, 2004

16

Visual C++. Programarea Interfeelor Utilizator

1.1.2

MFC Application Wizard.

E altceva!

Vom implementa acum un program Windows, dar nu vom intra n amnunte cu privire la tehnologia folosit. Pentru aceasta, s nchidem proiectul Primul (n meniu File->Close Workspace i apoi OK) i s crem un nou proiect, pe care s-l numim Al doilea. De data aceasta, vom alege un proiect de tip MFC AppWizard (exe). Dup apsarea butonului OK, vom putea alege unul din cele 3 abloane de programe Windows pe care le putem utiliza. Vom alege Dialog based (fig. 1.8) i vom apsa Finish i apoi OK n pagina de prezentare.

Fig. 1.8. Crearea unui proiect de tip dialog

Un astfel de proiect ne va pune la dispoziie o machet, pe care putem construi cu ajutorul controalelor din bara de instrumente, interfaa utilizator dorit.

Drag & drop

Machet

Bar cu instrumente

Fig. 1.9. S compunem interfaa


here

Pentru interfaa programului nostru, vom terge textul TODO: Place dialog controls (apsm tasta dreapt a mouse-lui i apoi apsm tasta Delete) i vom insera

Capitolul 1. noiuni de programare obiectual

17

un buton de comand (Button) i o caset de editare (EditBox) din bara de instrumente (fig. 1.9). Inserarea se face uor, suntem deja familiarizai cu tehnica drag and drop caracteristic sistemelor Windows. Acum, apsm dublu-click pe butonul Button1 i acceptm numele OnButton1 propus de mediul de programare (adic apsm butonul OK). Mediul de programare ne va poziiona ntr-o funcie, cu implementarea de mai jos:
void CAldoileaDlg::OnButton1() { // TODO: Add your control notification handler code here }

n aceast funcie, vom aduga codul nostru:


void CAldoileaDlg::OnButton1() { // TODO: Add your control notification handler code here CString nume, afisare; GetDlgItem(IDC_EDIT1)->GetWindowText(nume); afisare+="Salut "+nume+"\n Este primul tau program Windows!"; AfxMessageBox(afisare); }

S facem o convenie. Tot codul ce trebuie adugat de noi, va fi scris de acum nainte cu caractere bold. Acum vom compila i lansa n execuie programul (similar proiectului Win32 Console Application), i va fi afiat macheta din fig. 1.10. n aceast machet, vom utiliza caseta de editare pentru introducerea numelui, iar la apsarea butonului, va fi afiat mesajul din fig. 1.11.

Fig. 1.11. Mesajul afiat Fig. 1.10. Aa arat macheta

Asta este! Am implementat i primul program Windows! De fapt, am dorit doar s facem cunotin cu mediul de programare. Cu tipul de proiect de mai sus ne vom ntlni abia mai trziu, n capitolele urmtoare. Deocamdat, ne vom mulumi cu proiecte Win32 ConsoleApplication. 1.2 S revenim la oile noastre... adic, POO! De ce programare obiectual i nu programare structurat? S ncercm s nelegem dintr-un exemplu simplu.

H. Vlean, 2004

18

Visual C++. Programarea Interfeelor Utilizator

S presupunem c ntr-un program, avem de manipulat ca informaie puncte n plan. Ar trebui deci s declarm o structur de forma:
struct punct_plan { int coordx; int coordy; }; punct_plan punct1;

S presupunem c logica programului cere ca toate punctele utilizate s fie de ordonat pozitiv, deci deasupra axei reale (fig. 1.12).
y
Zona de existen a punctelor

x
Fig. 1.12. Aici avem puncte...

Cu alte cuvinte, pentru orice punct introdus n program, ordonata va trebui nlocuit cu valoarea ei absolut. Va trebui s scriem de fiecare dat, o secven de forma (evident, fr s uitm s includem bibliotecile iostream.h i math.h)
cin >> punct1.coordx >>punct1.coordy; punct1.coordy= abs(punct1.coordy);

Dar cine garanteaz c a doua linie de program este introdus ntotdeauna? Poate c o soluie mai bun ar fi citirea ordonatei prin intermediul unei funcii, care s returneze ntotdeauna valoarea ei absolut. Vom putea avea spre exemplu n program declarat funcia
int citescoordy () { int inputy; cin >> inputy; return (abs(inputy)); }

iar secvena de program de citire a punctului ar putea fi


cin >> punct1.coordx ; punct1.coordy=citescoordy();

Dar, din nou, cine garanteaz c undeva n program nu se strecoar i una din liniile
cin >> punct1.coordy;

sau
punct1.coordy=7;

Capitolul 1. noiuni de programare obiectual

19

Nu avem n acest moment la ndemn nici o tehnic prin care s fim obligai s folosim doar funcia de citire pentru atribuirea de valori ordonatei unui punct, sau care s ne oblige s atribuim doar valori pozitive acesteia. 1.2.1 Ce este o clas? Ce este un obiect? Din cele artate mai sus, apare ideea introducerii unui nou tip, care s ncapsuleze o structur de date i un set de funcii de interfa care acioneaz asupra datelor din structur. n plus, noul tip trebuie s asigure diferite niveluri de acces la date, astfel nct anumite date s nu poat fi accesate dect prin intermediul unor funcii de interfa i nu n mod direct. Acest nou tip este denumit clas. n limbajul C++ clasele se obin prin completarea structurilor uzuale din limbajul C, cu setul de funcii necesare implementrii interfeei obiectului. Aceste funcii poart denumirea de metode. Pentru realizarea izolrii reprezentrii interne de restul programului, fiecrui membru (dat din cadrul structurii, sau metod) i se asociaz nivelul de ncapsulare public sau private (fig. 1.13). Un membru public corespunde din punct de vedere al nivelului de accesibilitate, membrilor structurilor din limbajul C. El poate fi accesat din orice punct al programului, fr s se impun vreo restricie asupra lui. Membrii private sunt accesibili doar n domeniul clasei, adic n clasa propriuzis i n toate funciile membre.

Membri publici. Pot fi accesai i din afara clasei

Funcii de interfaa (metode)

Membri privai. Pot fi accesai doar din cadrul

Fig. 1.13. Accesibilitatea membrilor clasei

Sintaxa folosit pentru declararea unei clase este urmtoarea:


class Nume_clasa { [ [private :] lista_membri_1 ] [ [public :] lista_membri_2 ] };

Cuvntul cheie class indic faptul c urmeaz descrierea unei clase, avnd numele Nume_clasa. Numele clasei poate fi orice identificator (orice nume valid de variabil), dar trebuie s fie unic n cadrul domeniului de existen respectiv. Descrierea clasei const din cele dou liste de membri, prefixate eventual de cuvintele cheie private i public. Observaie: Dac n declaraia unei clase apare o list de membri fr nici un specificator de acces, aceti membri vor fi implicit privai.

H. Vlean, 2004

20

Visual C++. Programarea Interfeelor Utilizator

n exemplul nostru, este evident c va trebui s declarm o clas care s conin ca structur de date dou valori ntregi. Deoarece ordonata poate fi doar pozitiv, ea nu trebuie s poat fi accesat direct din program, ci doar prin intermediul unei metode care s i atribuie o valoare, dar numai pozitiv. Deci, va trebui s o declarm ca membru privat. Cum ordonata nu poate fi accesat direct din program, va trebui s adugm clasei i metode care s permit citirea i introducerea valorii ei. Uzual, declararea unei clase se face ntr-un fiier header. Nu este obligatoriu, dar o s vedem n capitolele urmtoare c acest mod de implementare a programului duce la o mai mare claritate a programului, n special cnd acesta conine un numr mare de clase. Acestea fiind spuse, s ne facem curaj i s declarm prima noastr clas. Pentru aceasta, vom deschide un proiect nou, de tip Win32 Console Application. Haidei s-l numim Prima_Clasa. Acestui proiect i vom aduga in fiierul header Prima_Clasa.h, n care vom declara clasa astfel: // fiierul Prima_Clasa.h
class punct_plan { int coordy; public: int coordx; void setcoordy(int cy){coordy=abs(cy);}; int getcoordy() {return coordy;}; };

Am declarat astfel o clas pe care o vom utiliza n continuare. Se poate observa c variabila coordy este privat, deci va putea fi accesat n afara clasei, doar prin intermediul metodelor puse la dispoziie de clas. n cadrul metodelor, deci din interiorul clasei, variabila poate fi accesat n mod direct. Va trebui acum s declarm nite variabile de tipul clasei. O astfel de variabil poart denumirea de obiect i reprezint n fapt o instaniere (concretizare) a clasei respective. Putem da acum i o definiie pentru clas: Definiia 1.1: O clas este un tip de date care descrie un ansamblu de obiecte cu aceeai structur i acelai comportament. Obiectele de tipul clasei, le vom declara n fiierul surs Prima_Clasa.cpp, pe care l vom aduga proiectului (Ai uitat cum? Simplu: File->New->C++ Source File), n care vom scrie instruciunile de mai jos: // fiierul Prima_Clasa.cpp
#include <iostream.h> #include <math.h> #include "Prima_Clasa.h" void main() { punct_plan punct, *ppunct; int valy; cout << "\n Introduceti abscisa : ";

Capitolul 1. noiuni de programare obiectual


cin >> punct.coordx; cout << "\n Introduceti ordonata : "; cin >> valy; punct.setcoordy(valy); cout <<"\n Valorile pentru abscisa si ordonata sunt " << punct.coordx <<" si "<<punct.getcoordy(); ppunct=&punct; cout <<"\n Acum cu pointer " << ppunct->coordx <<" si " <<ppunct->getcoordy() <<"\n\n"; }

21

Vom compila i executa programul obinut. Rezultatul execuiei programului este prezentat n fig. 1.14.

Fig. 1.14. Asta am obinut!

Ce observaii putem face dac ne uitm la program: n primul rnd, membrul privat al clasei poate fi accesat din main() (adic din afara clasei) doar prin intermediul metodelor clasei. Dac am fi scris o instruciune de forma
cin >> punct.coordy;

am fi obinut o eroare nc din faza de compilare. Cu alte cuvinte, problema noastr, de a fora valori pozitive pentru ordonat e rezolvat! i n cazul obiectelor, ca i n cazul variabilelor de un tip standard sau utilizator, se poate face atribuirea
ppunct=&punct

unde prin &punct nelegem (din nou) adresa obiectului punct. Deci, formalismul lucrului cu pointeri este neschimbat; accesul la componentele unui obiect, fie elemente ale structurii de date, fie metode, se face ca i n cazul structurilor. Putem observa c i n cazul obiectului punct i n cazul pointerului ppunct, accesul se face n forma deja cunoscut; Un membru privat al unei clase, poate fi totui accesat i din afara clasei. Dar funcia care-l acceseaz trebuie s fie o funcie prieten. O funcie este prezentat clasei ca fiind prieten prin intermediul cuvntului rezervat friend:
class punct_plan { friend void functie_prietena(punct_plan); int coordy; public: int coordx;

H. Vlean, 2004

22

Visual C++. Programarea Interfeelor Utilizator


void setcoordy(int cy){coordy=abs(cy);}; int getcoordy() {return coordy;};

};

Funcia, cu toate c nu aparine clasei, ci este o funcie global, va putea accesa direct variabila privat din clas:
#include <iostream.h> #include <math.h> #include "Prima_Clasa.h" void functie_prietena(punct_plan pct) { cout << "\n coordy " << pct.coordy; } void main() { ... cout <<"\n Acum cu pointer " << ppunct->coordx <<" si "<<ppunct->getcoordy() <<"\n\n"; functie_prietena(punct); }

S revenim puin la mediul de programare. n partea stng, se poate observa o fereastr ce conine 2 etichete: Class View i respectiv File View. n fereastra Class View (fig. 1.15), mediul ne afieaz toate componentele programului: clasa punct_plan, marcat cu o pictogram de tip arbore, metodele setcoordy() i getcoordy() marcate prin paralelipipede de culoare roie, variabilele coordy i coordx, marcate prin paralelipipede albastre, precum i mrimile globale, n cazul nostru, doar funcia main(). Putem observa de asemenea, c n dreptul variabilei coordy este desenat un lact, marcnd faptul c aceasta este o mrime privat. Un dublu click asupra oricrei componente, va afia i fixa prompterul n zona de implementare corespunztoare n fereastra programului.

Metode, n fiierul .cpp Date, n fiierul .h Mrimi globale, n cazul nostru funcia main()

Fig. 1.15. Mrimile din program sunt figurate n Class View!

Capitolul 1. noiuni de programare obiectual

23

Pentru eticheta File View (fig. 1.16) fereastra din stnga va conine toate fiierele care compun proiectul, n funcie de tipul lor. Din nou, un dublu click asupra unui nume de fiier, va face ca n fereastra programului s se afieze coninutul fiierului respectiv.

Fig. 1.16 . Aa arat File View

1.2.2 Funcii inline. La ce or fi bune? S modificm declaraia clasei


punct_plan

ca i n codul de mai jos:

class punct_plan { int coordy; public: int coordx; inline void setcoordy(int cy){coordy=abs(cy);}; inline int getcoordy() {return coordy;}; };

Ce am modificat? Am introdus cuvintele inline n faa definirii metodelor, transformdu-le astfel n funcii inline. Dac compilm i executm programul, constatm c nimic nu s-a schimbat. Atunci, ce este de fapt o funcie inline? S ne reamintim care este mecanismul care se pune n micare, atunci cnd ntr-un program se face un apel de funcie (fig. 1.17): la ntlnirea unui apel de funcie, se salveaz n stiv adresa din memorie a codului urmtoarei instruciuni executabile, precum i valorile parametrilor funciei; se sare din secvena normal de instruciuni i se execut prima instruciune din funcie, aflat la o adres cunoscut din memorie; se execut toate instruciunile funciei, iar la sfrit se extrage din stiv adresa urmtoarei instruciuni executabile din programul apelant; se continu execuia normal a programului.

H. Vlean, 2004

24

Visual C++. Programarea Interfeelor Utilizator

Cod executabil funcie

Acest mecanism asigur o dimensiune redus a codului executabil, pentru c toate codurile executabile asociate funciilor vor apare o singur dat n codul programului. Dar, fiecare apel de funcie nseamn respectarea mecanismului descris mai sus. Fiecare operaie dureaz un interval de timp, timp care poate fi chiar mai mare dect timpul de execuie al codului funciei apelate, dac acesta este scurt.
Cod executabil program principal

n cazul funciilor cu puine instruciuni, este uneori util s le declarm inline. n acest caz, nu se mai genereaz un apel normal al funciei, cu tot mecanismul aferent, ci pur i simplu, codul funciei este inserat n locul n care a fost apelat (fig. 1.18). Se obine astfel un cod executabil mai lung, dar timpul de execuie al programului este scurtat. Declararea unei funcii inline este lsat la latitudinea noastr. Depinde dac urmrim un cod executabil mai redus ca dimensiune, sau un timp de execuie mai scurt. Un sfat ar fi totui, s nu declarm niciodat inline o funcie care are multe instruciuni. 1.2.3 Nume calificat. Operator de domeniu Metodele clasei punct_plan au instruciuni foarte puine, deci nu a fost o problem s le implementm n momentul declarrii i chiar s le definim inline. Dar, n marea majoritate a cazurilor, n fiierele header se face doar declararea metodelor, iar implementarea lor este fcut n fiierele .cpp, avnd astfel loc o separare clar a implementrii unei clase de interfaa ei. Pentru a respecta cele artate mai sus, s rescriem coninutul fiierului Prima_Clasa.h ca mai jos:
class punct_plan { int coordy; public:

Cod executabil program principal

apel funcie

adres de retur

prametri

stiv

Fig. 1.17. Aa se apeleaz o funcie

Cod executabil program principal

Cod executabil funcie

apel funcie

Fig. 1.18. Funcii inline

Capitolul 1. noiuni de programare obiectual


int coordx; void setcoordy(int cy); int getcoordy();

25

};

Cum definirea metodelor nu este fcut, acestea vor trebui implementate n fiierul Prima_Clasa.cpp. S modificm acest fiier ca mai jos:
#include <iostream.h> #include <math.h> #include "Prima_Clasa.h" void setcoordy(int cy) { coordy=abs(cy); } int getcoordy() { return(coordy); } void main() { punct_plan punct, *ppunct; int valy; cout << "\n Introduceti abscisa : "; cin >> punct.coordx; cout << "\n Introduceti ordonata : "; cin >> valy; punct.setcoordy(valy); cout <<"\n Valorile pentru abscisa si ordonata sunt " << punct.coordx <<" si "<<punct.getcoordy(); ppunct=&punct; cout <<"\n Acum cu pointer " << ppunct->coordx <<" si "<<ppunct->getcoordy() <<"\n\n";

Aparent totul e corect. Dac ns vom compila programul, vom obine erori. Compilatorul, n cazul nostru, nu va ti cine este variabila coordy. Aceasta se ntmpl pentru c, din modul de definire, funciile getcoordy() i setcoordy() sunt interpretate de compilator ca i funcii globale (fig. 1.19) i nu aparinnd clasei punct_plan, iar variabila coordy nu este declarat nicieri n main(). Ea este o proprietate intrinsec a clasei punct_plan, iar compilatorul nu are de unde s tie c cele dou funcii sunt metode ale clasei. De altfel, am putea avea declarate mai multe clase, fiecare coninnd cte o metod setcoordy() i respectiv getcoordy(), avnd implementri complet diferite. De unde tim c o funcie este declarat ntr-o clas sau n alta? Pentru clarificarea problemei, va trebui ca n momentul definirii unei funcii, pe lng numele ei, s precizm compilatorului i clasa din care face parte aceasta. Prin adugarea numelui clasei la numele funciei se obine numele complet (sau numele calificat) al funciei. Adugarea numelui clasei se face cu ajutorul operatorului ::, numit operator de domeniu. Deci, de exemplu, numele calificat al funciei getcoordy() va fi punct_plan::getcoordy(). Astfel, funcii cu acelai nume, dar aparinnd la clase diferite, vor fi identificate corect de compilator.

H. Vlean, 2004

26

Visual C++. Programarea Interfeelor Utilizator

Pentru ca programul nostru s nu mai aib erori de compilare, vom modifica fiierul Prima_Clasa.cpp ca mai jos:

Funciile sunt considerate globale, nu membre ale clasei punct_plan

Aceast variabil nu este declarat n main()!

Fig. 1.19. Funciile sunt considerate globale!


#include <iostream.h> #include <math.h> #include "Prima_Clasa.h" void punct_plan::setcoordy(int cy) { coordy=abs(cy); } int punct_plan::getcoordy() { return(coordy); } void main() { ... }

1.2.4 Pointerul ascuns this. Uf, ce ncurctur Haidei s modificm din nou fiierul Prima_Clasa.cpp, astfel nct s avem dou obiecte de clas punct_plan, respectiv punct1 i punct2:
#include <iostream.h> #include <math.h> #include "Prima_Clasa.h" void punct_plan::setcoordy(int cy) { coordy=abs(cy); } int punct_plan::getcoordy()

Capitolul 1. noiuni de programare obiectual


{ } void main() { punct_plan punct1, punct2; int valy; cout << "\n Introduceti abscisa 1: "; cin >> punct1.coordx; cout << "\n Introduceti ordonata 1: "; cin >> valy; punct1.setcoordy(valy); cout << "\n Introduceti abscisa 2: "; cin >> punct2.coordx; cout << "\n Introduceti ordonata 2: "; cin >> valy; punct2.setcoordy(valy); cout <<"\n Valorile pentru abscisa1 si ordonata1 sunt " << punct1.coordx <<" si "<<punct1.getcoordy() << "\n iar pentru abscisa2 si ordonata2 sunt " << punct2.coordx <<" si "<<punct2.getcoordy() <<"\n\n";

27

return(coordy);

Dac executm programul, vom observa c funcia setcoordy() modific valoarea punct1.coordy cnd este apelat de punct1 i respectiv punct2.coordy cnd este apelat de punct2. Similar i funcia getcoordy() returneaz corect valorile variabilelor coordy pentru cele 2 obiecte. Dar, dac ne uitm la implementarea funciilor, fiecare dintre ele manipuleaz o variabil ntreag coordy, fr a se face vreo referire la obiectul creia i aparine acea mrime. De unde tie totui programul s atribuie n mod corect mrimea coordy unui obiect sau altuia? Pentru a identifica obiectul implicit asupra cruia se opereaz, compilatorul genereaz un pointer ascuns, numit this, care se ncarc naintea oricrei operaii cu adresa obiectului curent. Codul corect n accepiunea compilatorului este de fapt:
void punct_plan::setcoordy(int cy) { this->coordy=abs(cy); } int punct_plan::getcoordy() { return(this->coordy); }

astfel programul putnd ti exact ce variabil de la ce adres de memorie s modifice. Este foarte simplu s verificm faptul c acest pointer se ncarc cu adresa obiectului curent. Este suficient pentru aceasta, s modificm programul astfel nct, n funcia setcoordy() de exemplu, s afim adresa coninut de this, iar n programul principal adresele obiectelor utilizate pentru apelul funciei.
#include <iostream.h> #include <math.h> #include "Prima_Clasa.h"

H. Vlean, 2004

28

Visual C++. Programarea Interfeelor Utilizator

void punct_plan::setcoordy(int cy) { this->coordy=abs(cy); cout << " this= " << hex << this <<"\n\n"; } int punct_plan::getcoordy() { return(this->coordy); } void main() { punct_plan punct1, punct2; cout <<"\n &punct1= " << hex << &punct1 <<"\n punct1.setcoordy() "; punct1.setcoordy(10); cout <<"\n &punct2= " << hex << &punct2 << "\n punct2.setcoordy() "; punct2.setcoordy(10); }

Dac executm acest program, vom obine rezultatul din fig. 1.20.

this = &punct1 this = &punct2

Fig. 1.20. Aa lucreaz de fapt compilatorul...

punct1,

Ce observm? C la apelul punct1.setcoordy(), this se ncarc cu adresa obiectului iar la apelul punct2.setcoordy(), this se ncarc cu adresa obiectului punct2. Apoi, funcia va modifica valoarea cmpului coordy a obiectului pointat de this.

1.2.5 Membri statici. Ce mai sunt i tia? S modificm declaraia clasei punct_plan ca mai jos:
class punct_plan { int coordy; public: static int coordx; void setcoordy(int cy); int getcoordy(); void inccoordy(); void inccoordx(); };

Capitolul 1. noiuni de programare obiectual

29

Am declarat dou noi funcii, care urmeaz s fie definite, dar lucru nou, am adugat cuvntul rezervat static n faa declarrii cmpului coordx. Astfel, coordx devine o variabil static. O variabil static este un atribut propriu clasei i nu fiecrui obiect n parte. Dac pentru o variabil obinuit, se rezerv cte o zon de dimensiunea sizeof() n cazul declarrii fiecrui obiect, o variabil static are o unic zon de rezervare. Ea apare ca i o zon de memorie comun tuturor obiectelor (fig. 1.22). Ea exist i dac nu a fost declarat nici un obiect din clasa respectiv! O variabil static trebuie neaprat iniializat interiorul fiierului n care a fost declarat. Vom completa fiierul Prima_Clasa.cpp ca mai jos:
#include <iostream.h> #include <math.h> #include "Prima_Clasa.h" void punct_plan::setcoordy(int cy) { this->coordy=abs(cy); cout << " this= " << hex << this <<"\n\n"; } int punct_plan::getcoordy() { return(this->coordy); } void punct_plan::inccoordx() { coordx++; cout << " coordx= " << dec << coordx; } void punct_plan::inccoordy() { coordy++; cout << " coordy= " << dec << coordy; } int punct_plan::coordx=10; // am iniializat variabila static! void main() { cout << "\n Variabila statica : " << punct_plan::coordx << "\n"; punct_plan punct1, punct2; cout <<"\n &punct1= " << hex << &punct1 <<"\n punct1.setcoordy() "; punct1.setcoordy(10); cout <<"\n &punct2= " << hex << &punct2 << "\n punct2.setcoordy() "; punct2.setcoordy(10); cout <<" \n Pentru punct1 avem : "; punct1.inccoordx(); punct1.inccoordy(); cout <<" \n Pentru punct2 avem : "; punct2.inccoordx(); punct2.inccoordy(); cout << "\n\n"; }

i nu este vizibil dect n

H. Vlean, 2004

30

Visual C++. Programarea Interfeelor Utilizator

Ce observm? Funciile inccoordx() i incoordy() nu fac altceva dect s incrementeze cmpul corespunztor al structurii de date. De asemenea, se observ c variabila static este iniializat nainte de folosire i mai mult, valoarea ei poate fi afiat nainte de declararea unui obiect al clasei. Rezultatul execuiei programului este prezentat n fig. 1.21.

variabila static exist i dac nu este declarat nici un obiect

variabila static este comun obiectelor, este deci o caracteristic a clasei

variabila nestatic este proprie fiecrui obiect n parte

Fig. 1.21. Variabilele statice sunt atribute ale clasei

De ce coordx ia valoarea 12 dup operaiile de incrementare? Pentru c ea este un atribut al clasei! Variabila static va fi incrementat o dat prin apelarea funciei de incrementare prin punct1 i o dat prin punct2. Variabila nestatic este, dup cum se vede un atribut al obiectului, ea este instaniat i incrementat separat pentru fiecare obiect n parte (fig. 1.22).

&punct1.coordy 11 &punct2.coordy 11 &punct1.coordx 11 12 &punct2.coordx


Fig. 1.22 Variabila static este comun obiectelor

memorie

Acum, s mai facem o modificare n fiierul de declarare a clasei. S declarm funcia incoordx() ca funcie static:
class punct_plan { int coordy; public: static int coordx; void setcoordy(int cy); int getcoordy(); static void inccoordy(); void inccoordx(); };

Capitolul 1. noiuni de programare obiectual

31

Dac compilm programul, vom obine erori de compilare (fig. 1.23).

Apelul variabilei coordy este fcut incorect pentru o funcie static!

Fig. 1.22. Funcia static nu poate accesa coordy. Nu exist this!

Eroarea se datoreaz faptului c, o funcie static, fiind de asemenea un atribut al clasei, nu poate instania corect variabila coordy. Pur i simplu nu tie crui obiect i aparine, deoarece funciile statice nu primesc ca argument pointerul this, la fel ca i funciile nestatice. Va trebui ca funciei statice s-i transmitem explicit un pointer spre obiectul curent:
class punct_plan { int coordy; public: ... static void inccoordy(punct_plan* ptr); void inccoordx(); };

Modificrile n fiierul surs vor fi:


#include <iostream.h> ... void punct_plan::inccoordy(punct_plan* ptr) { ptr->coordy++; cout << " coordy= " << dec << ptr->coordy; } int punct_plan::coordx=10; void main() { ... punct_plan punct1, punct2, *pointer; ... punct1.inccoordx(); pointer=&punct1; punct_plan::inccoordy(pointer);

H. Vlean, 2004

32

Visual C++. Programarea Interfeelor Utilizator

cout <<" \n Pentru punct2 avem : "; punct2.inccoordx(); pointer=&punct2; punct_plan::inccoordy(pointer); cout << "\n\n"; }

1.2.6 Constructori, destructori ... Am nvat c la declararea unei variabile de un anumit tip, se rezerv n memorie o zon de dimensiune sizeof(tip) pentru stocarea valorilor variabilei. i n cazul obiectelor trebuie rezervat o zon de memorie pentru stocarea valorilor structurii de date ce compune clasa. Aceast rezervare este fcut de o funcie special, numit constructor. Constructorul unei clase este generat implicit de compilator, dar poate fi redefinit de programator. Un constructor este o funcie care are acelai nume ca i clasa din care face parte. Spre deosebire de o funcie obinuit, un constructor nu returneaz nici o valoare. Rolul constructorului este de a iniializa un obiect. O clas poate avea mai muli constructori, care difer ntre ei prin semntur (lista parametrilor formali). Ca i n cazul unei funcii obinuite, unii parametri pot fi specificai implicit. Un constructor, trebuie s fie ntotdeauna public, n caz contrar la declararea unui obiect n afara clasei, el nu va fi accesibil! O problem important apare n cazul claselor care conin membri de tip pointer, pentru care trebuie alocat dinamic memorie. Aceast operaiune este efectuat de obicei n constructorul clasei. Alocarea dinamic a memoriei se poate face folosind funcia malloc(), totui limbajul C++ aduce o nou facilitate n acest sens: operatorul new. S lum un exemplu: s deschidem un nou proiect de tip Win32 Console Application, numit Constructori i s-l populm cu fiiere cu acelai nume. n fiierul header vom declara clasa elem_lista, ca mai jos:
class elem_lista { int val; int* urmatorul; public: elem_lista(int); elem_lista(int,int); void afisez(); };

Ce am declarat? O clas care are ca structur de date un element de tip list simplu nlnuit. Clasa declar de asemenea doi constructori, care pot fi deosebii prin lista de argumente precum i o funcie de afiare. S implementm acum coninutul fiierului surs.
#include <iostream.h> #include "Constructori.h"

Capitolul 1. noiuni de programare obiectual

33

inline elem_lista::elem_lista(int valoare) { val=valoare; urmatorul=NULL; } inline elem_lista::elem_lista(int valoare1, int valoare2) { val=valoare1; urmatorul=new int(valoare2); } void elem_lista::afisez() { if (urmatorul) cout << "\n val= " << val << " *urmatorul= " << *urmatorul << " urmatorul= " << hex << urmatorul; else cout << "\n val= " << val << " urmatorul= " << hex << urmatorul; } void main() { elem_lista element1(5); element1.afisez(); elem_lista element2(7,9); element2.afisez(); elem_lista *pelement=new elem_lista(3,5); pelement->afisez(); cout << "\n\n"; }

Cei doi constructori creeaz structuri de date ca n fig. 1.23.


elem urmatorul valoare NULL elem urmatorul valoare1 adresa elem_lista(int,int) valoare2
new

elem_lista(int)

Figura 1.23. Constructorii creeaz structuri de date diferite

Secvena if else din funcia de listare este necesar, deoarece o ncercare de afiare a coninutului adresei 0 (p=NULL) duce n Windows la violarea proteciei i n consecin, terminarea anormal a programului. Dac programul se execut sub DOS, aceast secven nu este necesar.

Figura 1.24. Rezultatul execuiei programului

H. Vlean, 2004

34

Visual C++. Programarea Interfeelor Utilizator

n fig. 1.24 putem vedea rezultatul execuiei programului. Putem observa c apelul primului constructor completeaz doar structura declarat n clas, iar apelul celui de al doilea constructor, fie la construirea unui obiect al clasei, fie la construirea unui pointer la clas, aloc n mod dinamic memorie pentru cel de-al doilea ntreg. n acest caz, va trebui ca la terminarea domeniului de existen a obiectului, zona de memorie alocat dinamic s fie eliberat. Acest lucru se face de o alt funcie special, numit destructor. O clas poate declara destructorul, care este apelat automat de compilator n momentul distrugerii obiectelor din acea clas. Funcia destructor nu returneaz nici o valoare, iar numele ei este format cu construcia ~nume_clas. Destructorul nu primete nici un parametru. O clas poate avea un sigur destructor. De obicei, rolul destructorului este de a dealoca memoria alocat dinamic n constructor. Pentru eliberarea memoriei alocate, se poate folosi n continuare funcia free(), dar se recomand folosirea operatorului delete. S modificm declaraia clasei ca mai jos:
class elem_lista { int val; int* urmatorul; public: elem_lista(int); elem_lista(int,int); ~elem_lista(); void afisez(); };

Funcia nou adugat este destructorul clasei. Implementarea lui va fi:


#include <iostream.h> #include "Constructori.h" ... inline elem_lista::elem_lista(int valoare1, int valoare2) { val=valoare1; urmatorul=new int(valoare2); } inline elem_lista::~elem_lista() { if (urmatorul != NULL) delete urmatorul; } ... }

Destructorul va fi apelat automat la terminarea programului. Am nvat c n C++ o variabil poate fi iniializat la declarare. n mod absolut similar i un obiect poate fi iniializat la declarare. Putem scrie de exemplu, n cazul clasei declarate n primul program,
punct_plan punct1;

Capitolul 1. noiuni de programare obiectual


punct_plan punct2=punct1;

35

n acest caz, cel de-al doilea obiect al clasei va fi construit n mod identic cu primul. Pentru construirea obiectului punct2, compilatorul apeleaz (i creeaz implicit) un nou constructor, numit constructor de copiere. Constructorul de copiere nu face altceva dect s rezerve memorie pentru structura de date a celui de-al doilea obiect i apoi s copieze bit cu bit valorile din cmpurile primului obiect n cmpurile celui de al doilea. Dac vom modifica ns fiierul Constructori.h astfel nct structura de date a clasei s fie public (pentru a putea afia direct n programul principal valorile cmpurilor):
class elem_lista { public: int val; int* urmatorul; elem_lista(int); elem_lista(int,int); ~elem_lista(); void afisez(); };

i apoi vom modifica fiierul Constructori.cpp ca mai jos,


#include <iostream.h> ... void main() { elem_lista element1(6,7), element2=element1; cout << "\n val1= " << element1.val << " *urmatorul1= " <<*element1.urmatorul << " urmatorul1= " << hex << element1.urmatorul; cout << "\n val2= " << element2.val << " *urmatorul2= " << *element2.urmatorul << " urmatorul2= " << hex << element2.urmatorul; cout << "\n\n"; }

vom obine eroarea de execuie din fig. 1.25:

Fig. 1.25. Obinem o eroare de aserie!

De fapt, ce am fcut n program? Am declarat obiectul element1, pentru care a fost creat structura de date, conform celui de al doilea constructor. Apoi, la declararea obiectului element2, a intrat n funciune constructorul de copiere, care construiete cel de-al doilea obiect copiind bit cu bit valorile din structura de date asociat. Adic,

H. Vlean, 2004

36

Visual C++. Programarea Interfeelor Utilizator

pointerul element2.urmatorul va fi o copie bit cu bit a pointerului element1.urmatorul. Adic, cele dou obiecte vor pointa spre aceeai adres creat dinamic n timpul execuiei (fig. 1.26). La terminarea programului (sau a domeniului de vizibilitate a obiectelor), destructorul elibereaz zona alocat dinamic i distruge ultimul obiect creat. Deci, n primul obiect, va rmne un pointer ncrcat cu o adres ce nu mai aparine programului.

element1 copie bit cu bit element2

6 adr 6 adr

Fig. 1.26. al doilea obiect este o copie a primului

Dac structura de date a clasei conine pointeri ce aloc dinamic memorie, constructorul de copiere va trebui declarat explicit. Constructorul de copiere este o funcie cu acelai nume cu cel al clasei, care primete ca argument o referin la un obiect de tipul clasei. n cazul nostru, va trebui s declarm un constructor de copiere:
class elem_lista { public: int val; int* urmatorul; elem_lista(int); elem_lista(int,int); elem_lista(elem_lista&); ~elem_lista(); void afisez(); };

i s-l implementm
#include <iostream.h> ... elem_lista::elem_lista(elem_lista& obiectsursa) { this->val=obiectsursa.val; this->urmatorul=new int(*obiectsursa.urmatorul); } inline elem_lista::~elem_lista() { if (urmatorul != NULL) delete urmatorul; } ... void main() { ... }

Ce face constructorul de copiere n acest caz? n primul rnd, primete ca argument o referin spre obiectul surs. Apoi atribuie valoarea cmpului val din obiectul surs, cmpului val al noului obiect creat. Cmpul urmtorul al acestui obiect se ncarc apoi cu adresa unui ntreg creat dinamic i n care se depune coninutul

Capitolul 1. noiuni de programare obiectual

37

adresei pointate de cmpul urmtorul din obiectul surs. Vor rezulta astfel dou obiecte care conin valori identice, dar la adrese diferite, lucru care se vede cu uurin n fig. 1.27. Ca o observaie, pointerul this este scris explicit n acest exemplu, doar din motive didactice. El este oricum creat ascuns de ctre compilator i fr s fie scris.

Valorile cmpurilor celor dou obiecte sunt identice, dar la adrese diferite

Fig. 1. 27. Constructorul de copiere creeaz dinamic o nou adres

O situaie special apare n cazul obiectelor care au ca membri instanieri ale unor obiecte de alt tip, rezultnd astfel o ncuibrire a claselor. n acest caz, regulile sunt: 1. constructorii obiectelor ncuibrite se apeleaz naintea constructorului obiectului cuib; 2. dac nu sunt apelai explicit constructorii obiectelor ncuibrite, se ncearc apelarea unui constructor cu parametrii lund valori implicite; 3. destructorul obiectului cuib este apelat naintea destructorilor obiectelor ncuibrite. De exemplu, ntr-un nou proiect scrie:
// Patrat.h
class Punct { int coordx, coordy; public: Punct(int x=0, int y=0); ~Punct(); }; class Patrat { Punct st_sus, st_jos, dr_sus, dr_jos; public: Patrat(); ~Patrat(); }; // Patrat.cpp #include <iostream.h> #include "Patrat.h" Punct::Punct(int x, int y) { coordx=x; Win32 Console Aplication,

numit Patrat putem

H. Vlean, 2004

38

Visual C++. Programarea Interfeelor Utilizator


coordy=y; cout << "\n Constructor clasa Punct cu x= " << coordx << " y= " << coordy;

} Punct::~Punct() { cout << "\n Destructor clasa Punct cu x= " << coordx << " y= " << coordy <<" "; } Patrat::Patrat():st_jos(0, 1), dr_jos(1, 1), dr_sus(1, 0) { cout << "\n Constructor clasa Patrat"; } Patrat::~Patrat() { cout << "\n Destructor clasa Patrat"; } void main() { Patrat p; cout <<"\n"; }

Ce putem observa? nainte de construirea obiectului de clas Patrat, se construiesc cele 4 obiecte de clas Punct care-l compun conform constructorului. Destructorii sunt apelai n ordine invers apelului constructorilor. De observat forma aparte a constructorului clasei Patrat. Construcia cu : urmat de o list de apeluri de constructor ale obiectelor membru se numete list de iniializare. Atenie, instruciunile dintr-o list de iniializare au o ordine de execuie pur aleatoare! Clasa Patrat apeleaz explicit constructorii pentru obiectele st_jos, dr_jos i dr_sus. Constructorul obiectului st_sus este apelat implicit, cu parametri implicii de valoare 0. 1.2.7. Redefinirea (supranscrierea) operatorilor O facilitate extrem de puternic a limbajului C++ este dreptul programatorului de a aduga operatorilor limbajului i alte sensuri dect cele predefinite. Cu alte cuvinte, este vorba despre o redefinire a operatorilor limbajului C++, fr a se pierde vechile sensuri. Termenul consacrat n literatura de specialitate pentru aceast operaiune este cel de overloading operators. Redefinirea operatorilor (sau suprancrcarea operatorilor) se poate face n dou moduri: fie sub form de funcii membre, fie ca i funcii friend. Redefinirea unui operator presupune declararea unei funcii al crei nume este format din cuvntul cheie operator, un spaiu, i simbolul operatorului redefinit. La ce e de fapt bun redefinirea operatorilor? Haidei s lum iar un exemplu: s declarm o structur care s implementeze un numr complex. Numrul complex va avea forma Real+i*Imaginar, unde i este operatorul complex.
struct Complex {

Capitolul 1. noiuni de programare obiectual


double Re; double Im; }; Complex nr1, nr2;

39

nr1

S presupunem c am atribuit valori prilor reale i imaginare pentru variabilele i nr2 i dorim s efectum operaia nr3=nr1+nr2. Pentru aceasta, fie c declarm o funcie, fie c facem operaia de adunare n programul principal, va trebui s facem adunrile separat pentru partea real i respectiv pentru partea imaginar:

Complex Adun(Complex n1, Complex n2) { Complex n3; n3.Re=n1.Re+n2.Re; n3.Im=n1.Im+n2.Im; return n3; } void main() { Complex nr1, nr2, nr3; nr3=Adun(nr1,nr2); }

O modalitate de implementare a problemei mai apropiat de spiritul C++, este de a redefini operatorii aritmetici, astfel nct, dac operanzii sunt numere complexe, s se poat scrie direct instruciuni de forma nr3=nr1+nr2, nr3=nr1*nr2, etc. Pentru nceput, s declarm clasa Complex (ntr-un nou proiect Win32 Console Application, numit Complex):
class Complex { double Re, Im; public: Complex(double Real=0, double Imag=0){Re=Real;Im=Imag;}; void afisez(); Complex& operator + (Complex&); friend Complex& operator - (Complex&, Complex&); };

Ce conine clasa? Am declarat pentru structura de date doi membri privai, care vor implementa partea real i partea imaginar a numrului complex. Constructorul primete dou argumente de tip double, care au valorile implicite 0. De asemenea, clasa declar o funcie de afiare. Ca noutate, apare declararea operatorului + sub forma de funcie membr a clasei i respectiv a operatorului -, ca funcie prieten. Deoarece rezultatul unei operaii ntre dou numere complexe este tot un numr complex, cei doi operatori redefinii vor returna o referin la clasa Complex. De fapt, operatorul este interpretat ca o funcie, dar cu un rol special. Operatorul + n cazul nostru va fi interpretat pentru secvena c=a+b ca i c=a.functia+(b), iar pentru secvena c=a-b, c=fuctia-(a,b). S implementm acum fiierul surs:
#include <iostream.h>

H. Vlean, 2004

40
#include "Complex.h"

Visual C++. Programarea Interfeelor Utilizator

Complex& Complex::operator +(Complex& operand) { return *new Complex(this->Re+operand.Re, this->Im+operand.Im); } void Complex::afisez() { if (Im>0) cout << "\n" << Re << "+"<< Im <<"i"; else cout <<"\n" << Re << Im <<"i"; } Complex& operator - (Complex& desc, Complex& scaz) { return *new Complex(desc.Re-scaz.Re,desc.Im-scaz.Im); } void main() { Complex nr1(4,5), nr2(7,8), nr3; nr3=nr1+nr2; nr3.afisez(); nr1=nr1-nr3-nr2; nr1.afisez(); cout <<"\n\n"; }

Ce face operatorul +? Creeaz un nou obiect Complex, a crui parte real, respectiv imaginar este suma dintre partea real (imaginar) a obiectului curent, adic a primului operand i partea real (imaginar) a obiectului primit ca argument, adic cel de-al doilea operand. Returneaz apoi valoarea noului obiect Complex. n mod absolut similar este implementat i funcia prieten pentru operatorul -. Unii dintre operatori (operatorul new, operatorul delete, operatorul [], operatorul () i operatorul =) necesit precauii suplimentare n cazul redefinirii lor. Dintre acetia, cel mai des ntlnit este operatorul =. Dac o clas nu redefinete operatorul =, compilatorul ataeaz clasei respective un operator = implicit, care, s ne reamintim, efectueaz o copie bit cu bit a operandului din dreapta n operandul din stnga. Situaia este identic cu cea n care se genereaz un constructor de copiere implicit. Dac clasa conine membri de tip pointer, se ajunge la situaia n care doi pointeri se ncarc cu adresa aceleiai zone de memorie, care poate avea efecte dezastruoase dac se elibereaz unul din pointeri i apoi se acceseaz memoria prin al doilea pointer. De aceea se recomand redefinirea operatorului = doar n cazul claselor care nu au membri de tip pointer. Spre exemplu, n cazul clasei elem_lista, vom putea redefini operatorul = ca mai jos: Va trebui nti declarat operatorul n zona de declaraii publice ale clasei:
elem_lista& operator = (elem_lista);

apoi va trebui definit:


elem_lista& elem_lista::operator =(elem_lista operand)

Capitolul 1. noiuni de programare obiectual


{

41

if (this!=&operand) { val=operand.val; *urmatorul=*operand.urmatorul; } return *this;

Ce facem de fapt? nti ne uitm dac this nu conine tocmai adresa operandului transmis ca argument, adic dac nu suntem n cazul element2=element1. n acest caz, nu trebuie s facem nimic, dect s returnm valoarea de la acea adres. Dac operanzii sunt diferii, vom ncrca la adresa this coninutul de la adresa pointat de pointerul operandului din dreapta. Astfel, vom avea din nou, adrese diferite, dar ncrcate cu aceeai valoare. 1.3 Motenirea S mergem mai departe i s nelegem un concept nou, care d adevrata putere a programrii obiectuale. Pentru aceasta, s deschidem un proiect nou, pe care s-l numim sper exemplu Mostenire. n fiierul header, s declarm din nou clasa punct_plan, ca mai jos, adugnd i a treia dimensiune (ce nseamn un membru protected vom vedea imediat):
class punct_plan { public: int coordx; private: int coordy; protected: int coordz; public: void setcoordy(int cy); int getcoordy(); void setcoordz(int cz); int getcoordz(); }; Fiierul surs va fi completat ca mai jos: #include <iostream.h> #include "Mostenire.h" void punct_plan::setcoordy(int cy) { coordy=cy; } int punct_plan::getcoordy() { return coordy; } void punct_plan::setcoordz(int cz) { coordz=cz; } int punct_plan::getcoordz() {

H. Vlean, 2004

42
return coordz;

Visual C++. Programarea Interfeelor Utilizator

void main() { punct_plan punct1; punct1.coordx=5; punct1.setcoordy(10); punct1.setcoordz(3); cout <<"\n punct1= (" << punct1.coordx << " , " << punct1.getcoordy() << " , " << punct1.getcoordz() <<")"; cout << "\n\n";

Dac ne uitm n ClassView, vom observa c n dreptul variabilei coordz este figurat o cheie. Deci, variabila protejat nu are nici indicaia variabilei publice, nici a celei private. Totui, n program, nu am fi putut s o accesm direct i a fost nevoie de implementarea metodelor de interfa setcoordz() i getcoordz(). Deci, fa de programul principal, sau mai bine zis, fa de mediul din afara clasei, variabila protejat are statut de variabil privat. S declarm acum o nou clas, numit punct_colorat, care pe lng cele trei coordonate ale clasei punct_plan, va mai avea un atribut i anume, un cod de culoare. Am putea declara noua clas ca mai jos (tot n fiierul Mostenire.h):
class punct_colorat { public: int coordx; private: int coordy; protected: int coordz; int culoare; public: void setcoordy(int cy); int getcoordy(); void setcoordz(int cz); int getcoordz(); void setculoare(int cul); int getculoare(); };

Este totui neperformant s declarm astfel cea de a doua clas, deoarece ea repet identic informaiile din prima clas, adugnd ceva n plus. n C++, se poate stabili o ierarhie de clase, astfel nct acestea s se afle ntr-o relaie de motenire. O clas poate fi derivat din alt clas, motenindu-i atributele i metodele, putnd aduga n plus altele noi. Clasa de la care se motenete se numete clas de baz, sau superclas, iar clasa care se deriveaz este clas derivat sau subclas. O subclas poate fi derivat din clasa de baz n mod public sau privat( n funcie de specificatorul de acces folosit: public sau private). n baza celor artate aici, rezult c o sintax mai rafinat pentru declararea unei clase este urmtoarea:
class Nume_Clasa [:public/private Nume_Clasa_De_Baza] {

Capitolul 1. noiuni de programare obiectual


[private: lista membri privati] [public: lista membri publici] };

43

Modul normal de declarare a clasei punct_colorat este:


class punct_colorat: public punct_plan { int culoare; public: void setculoare(int cul); int getculoare(); };

Clasa punct_colorat este derivat public din clasa punct_plan. Ea va moteni toate atributele (cele 3 coordonate) i toate funciile de interfa pe care le are i superclasa, dar va aduga un nou atribut (culoare) i dou noi metode. Vom completa acum fiierul surs cu definiiile celor dou metode noi.
#include <iostream.h> #include "Mostenire.h" ... int punct_plan::getcoordz() { return coordz; } void punct_colorat::setculoare(int cul) { coordz=coordx; culoare=cul; } int punct_colorat::getculoare() { return culoare; } void main() { punct_plan punct1; punct_colorat punctc1; ... punctc1.coordx=5; punctc1.setcoordy(10); punctc1.setculoare(4); cout <<"\n punctc1= (" << punctc1.coordx << " , " << punctc1.getcoordy() << " , " << punctc1.getcoordz() <<")"; cout << " Culoare= " << punctc1.getculoare(); cout << "\n\n";

Ce observm? C funcia setculoare(), n afar de faptul c atribuie o valoare membrului privat culoare, atribuie membrului protejat coordz motenit din clasa de baz valoarea membrului public coordx motenit de asemenea. Deci, un membru protejat al clasei de baz este accesibil ca i un membru public dintr-o clas derivat. Am fi putut declara clasa punct_colorat ca mai jos:
H. Vlean, 2004

44

Visual C++. Programarea Interfeelor Utilizator

class punct_colorat: private punct_plan { int culoare; public: void setculoare(int cul); int getculoare(); };

n acest caz, clasa punct_colorat este derivat privat din clasa punct_plan. Dac vom compila n acest caz programul, vom observa o mulime de erori, datorate faptului c toi membrii publici motenii din clasa de baz, accesai prin intermediul obiectului de clas derivat privat se comport ca i cum ar fi fost privai n clasa de baz. Aceasta este deosebirea dintre motenirea public i cea privat: un obiect al unei clase derivate public pstreaz tipul de acces al membrilor motenii din clasa de baz n mod identic. Un obiect al unei clase derivate privat, transform toi membri motenii din clasa de baz n membri privai. Acest fapt este sintetizat n tabelul 1.1, care prezint posibilitatea accesului direct al unui membru al clasei derivate:
Tabelul 1.1 Drept de acces n clasa de baz
public protected private public protected private

Specificator de acces (tip motenire)


public

Acces n clasa derivat


accesibil accesibil inaccesibil accesibil accesibil inaccesibil

Acces n afara claselor de baz i derivat


accesibil inaccesibil inaccesibil inaccesibil inaccesibil inaccesibil

private

1.3.1 Constructorii i destructorii claselor aflate n relaia de motenire Este interesant de vzut care este ordinea i modalitile de apel a constructorilor i destructorilor n cazul motenirii. Regula este urmtoarea: n cazul constructorilor, se apeleaz mai nti constructorul clasei de baz, i apoi constructorul clasei derivate. Apelarea constructorului clasei de baz se face implicit, dac este posibil i dac constructorul clasei de baz nu este apelat explicit n clasa derivat; n cazul destructorilor, se apeleaz mai nti destructorul clasei derivate, i apoi destructorul clasei de baz; Pentru a lmuri aceast problem, s modificm programul astfel nct s definim explicit constructorii i destructorii, iar n programul principal s declarm dou obiecte:
class punct_plan { ... public: punct_plan(){ cout << "\n Constructor punct_plan ";}; ~punct_plan(){ cout << "\n Destructor punct_plan ";}; void setcoordy(int cy); ...

Capitolul 1. noiuni de programare obiectual


}; class punct_colorat: public punct_plan { int culoare; public: punct_colorat(){ cout << "\n Constructor punct_colorat ";}; ~punct_colorat(){ cout << "\n Destructor punct_colorat ";}; ... };

45

respectiv
#include <iostream.h> #include "Mostenire.h" ... void main() { punct_plan punct1; punct_colorat punctc1; }

Rezultatul programului este prezentat n fig. 1.28.

Fig. 1.28. Aa se apeleaz constructorii i destructorii


punct_plan.

Ce observm? La declararea obiectului punct1, se apeleaz constructorul clasei Apoi, la declararea obiectului punctc1, se apeleaz nti constructorul clasei de baz i apoi constructorul clasei derivate. La distrugere, destructorii se apeleaz invers. 1.3.2 Pointeri. Cnd facem conversii explicite de tip? S declarm acum 2 pointeri (n programul surs):

void main() { punct_plan punct1, *ppunct1; punct_colorat punctc1, *ppunctc1; }

Vom putea face direct conversii ntre clasa de baz i subclas, sau va trebui s facem o conversie explicit de tip? Dac punct_colorat este derivat public, putem scrie:

H. Vlean, 2004

46

Visual C++. Programarea Interfeelor Utilizator

ppunct1=&punct1; // evident, sunt de acelai tip ppunctc1=&punctc1; ppunct1=&punctc1; ppunctc1=(punct_colorat*)&punct1;

Dac punct_colorat este derivat privat, putem scrie:


ppunct1=&punct1; // evident, sunt de acelai tip ppunctc1=&punctc1; ppunct1=(punct_plan*)&punctc1; ppunctc1=(punct_colorat*)&punct1;

n concluzie, orice conversie de la superclas la subclas trebuie fcut n mod explicit. n cazul conversiei de la subclas la superclas, avem cazurile: implicit dac motenirea este public; explicit, dac motenirea este privat; Faptul c printr-un pointer la clasa de baz putem accesa direct un obiect al unei clase derivate public, duce la nite consecine extrem de interesante. 1.3.3 Tablouri eterogene. Funcii virtuale Pn n acest moment, am fost obinuii ca tablourile s conin elemente de acelai tip. Aceste tablouri se numesc tablouri omogene. O observaie important care se impune este aceea c un pointer la o clas de baz, poate pstra adresa oricrei instanieri a unei clase derivate public. Aadar, avnd un ir de pointeri la obiecte de clas de baz, nseamn c unii dintre aceti pointeri pot referi de fapt obiecte de clase derivate public din aceasta, adic tabloul de pointeri este neomogen. Un astfel de tablou neomogen se numete eterogen. Limbajul C++ aduce un mecanism extrem de puternic de tratare de o manier uniform a tablourilor eterogene: funciile virtuale. O funcie virtual este o funcie care este prefixat de cuvntul cheie virtual, atunci cnd este declarat n clasa de baz. Aceast funcie este redeclarat n clasa derivat (cu aceeai semntur, adic aceeai list de parametri formali, acelai nume i acelai tip returnat), i prefixat de cuvntul cheie virtual. S presupunem c un obiect instaniat dintr-o clas D (care este derivat public din superclasa B) este accesat folosind un pointer la un obiect de tip B. S mai facem presupunerea c aceast clas B declar o metod virtual M(), care este apoi redeclarat n clasa D. Atunci cnd se ncearc apelarea metodei M(), folosind pointerul la un obiect de tip B, compilatorul va lua decizia corect i va apela metoda virtual M() redeclarat n clasa D. Dac metoda nu este declarat virtual, la apelul metodei prin pointerul la clasa B, va fi apelat metoda clasei de baz. Comportamentul diferit al unei funcii cu acelai nume pentru obiecte din superclas, respectiv din clasele derivate, se numete polimorfism. n concluzie, n C++ polimorfismul este implementat prin funcii virtuale. S verificm aceast problem ntr-un nou proiect, pe care s-l numim Virtual.
class ObGrafic { public: ObGrafic(); ~ObGrafic();

Capitolul 1. noiuni de programare obiectual


virtual void Desenez(); }; class Cerc: public ObGrafic { public: Cerc(); ~Cerc(); virtual void Desenez(); }; class Patrat: public ObGrafic { public: Patrat(); ~Patrat(); virtual void Desenez(); };

47

Cerc.

Am declarat superclasa ObiectGrafic, din care am derivat public clasele Patrat i Fiecare clas implementeaz un constructor i un destructor, pentru a putea vizualiza modul de construcie i distrugere a obiectelor i o funcie Desenez(), declarat ca i virtual. Implementarea fiierului surs este:

#include <iostream.h> #include "Virtual.h" ObGrafic::ObGrafic(){cout << "\n Constructor ObiectGrafic";} ObGrafic::~ObGrafic(){cout << "\n Destructor ObiectGrafic";} void ObGrafic::Desenez(){cout << "\n Desenez un ObiectGrafic";} Cerc::Cerc(){cout << "\n Constructor Cerc";} Cerc::~Cerc(){ cout << "\n Destructor Cerc";}

void Cerc::Desenez(){cout << "\n Desenez un Cerc";} Patrat::Patrat(){cout << "\n Constructor Patrat";} Patrat::~Patrat(){cout << "\n Destructor Patrat";} void Patrat::Desenez(){cout << "\n Desenez un Patrat";} void main() { ObGrafic* ptab[3]; ptab[0] = new ObGrafic(); ptab[1] = new Cerc(); // conversie implicita! ptab[2] = new Patrat; // din nou conversie implicita! // acum ptab este un tablou neomogen... for(int i=0; i<3; i++) ptab[i]->Desenez(); // ... care este tratat ntr-o maniera uniforma,datorita mecanismului // functiilor virtuale for(i=0; i<3; i++) delete ptab[i]; // eliberare memorie }

H. Vlean, 2004

48

Visual C++. Programarea Interfeelor Utilizator

Am creat tabloul ptab[3] de pointeri la clasa ObGrafic. Deoarece primul element pointeaz spre un obiect ObGrafic, al doilea spre un obiect Cerc i al treilea spre un obiect Patrat, este un tablou neomogen. Exist totui o observaie legat de programul de mai sus: dac este rulat, se poate observa c memoria nu se elibereaz corect! Este apelat de trei ori destructorul clasei ObGrafic, dar nu se apeleaz nicieri destructorii claselor derivate! Aceast problem apare datorit faptului c destructorii nu au fost declarai virtuali. Un pointer la clasa de baz, va apela doar destructorul clasei de baz. Problema se rezolv folosind tot mecanismul funciilor virtuale i declarnd destructorii claselor ca fiind virtuali.
Destructorul distruge obiectele ObGrafic, pointate de ptab[i] adr. ObGrafic adr. ObGrafic adr. ObGrafic ptab

ObGrafic

Cerc

Patrat

Figura 1.28. Eliberarea incorect a memoriei

Aadar, codul corectat este:


class ObGrafic { public: ObGrafic(); virtual ~ObGrafic(); virtual void Desenez(); }; class Cerc: public ObGrafic { public: Cerc(); virtual ~Cerc(); virtual void Desenez(); }; class Patrat: public ObGrafic { public: Patrat(); virtual ~Patrat(); virtual void Desenez(); };

Destructorul este virtual, distruge corect obiectele pointate a dr. Ob Grafic a dr. Ob Grafic a dr. Ob Grafic ptab

ObGrafic

Cerc

Patrat

Figura 1.29. Eliberarea corect a memoriei

Capitolul 1. noiuni de programare obiectual

49

1.3.4 Clase abstracte. Funcii virtuale pure Am nvat pn acuma c o funcie o dat declarat va trebui s fie i definit, n caz contrar compilatorul genereaz o eroare. Exist uneori situaii n crearea unei ierarhii de clase, n care este util s declarm ca i superclase clase generice, care nu implementeaz anumite operaiuni, ci doar le declar (descriu), urmnd a fi implementate n clasele derivate. Aceasta se realizeaz folosind funciile virtuale pure, care este un alt concept specific limbajului C++. Pentru o funcie virtual pur, declaraia este urmat de =0; Aceasta nu nseamn o iniializare, ci specific caracterul virtual pur al funciei. O clas care conine cel puin o funcie virtual pur se numete clas abstract. Ea nu poate fi instaniat, deci nu se pot declara obiecte de aceast clas, datorit faptului c ea nu furnizeaz implementarea metodei virtuale pure! Ca exemplu (n proiectul Virtual_Pur), putem presupune c avem o clas de baz Animal care declar, fr a implementa, metoda Hraneste(). Aceast metod este apoi implementat n clasele derivate. Clasa Animal este o clas generic, adic nu putem crea obiecte de acest tip. Putem n schimb crea obiecte de tipul claselor derivate. Fie urmtoarele declaraii de clase:
class Animal { public: virtual ~Animal(){}; virtual void Hraneste()=0; }; class Ghepard: public Animal { public: virtual ~Ghepard(){}; virtual void Hraneste() {cout <<"\n Omoara o gazela si maninc-o";} }; class Casalot: public Animal { virtual ~Casalot(){}; virtual void Hraneste() {cout <<"\n Prinde pesti si maninca-i";} }; class Pisica: public Animal { virtual ~Pisica(){}; virtual void Hraneste() {cout <<"\n Bea niste lapte";} };

Fiierul surs va fi:


#include <iostream.h> #include "Virtual_Pur.h" void main() { Animal * ptr[3]; ptr[0] = new Ghepard(); ptr[1] = new Casalot(); ptr[2] = new Pisica(); ptr[0]->Hraneste(); ptr[1]->Hraneste(); ptr[2]->Hraneste(); delete ptr[0]; delete ptr[1];

H. Vlean, 2004

50
delete ptr[2];

Visual C++. Programarea Interfeelor Utilizator

Se poate observa c programul se execut corect, cu toate c funcia Hranete() este doar declarat n clasa de baz, nu i implementat. n schimb, n clasele derivate este obligatorie definirea funciei virtuale pure! 1.4 S facem un exemplu complet Haidei s implementm o stiv (Last In First Out) i o coad de numere ntregi (First In First Out), pornind de la o clas de baz abstract, numit Base. Aceast clas declar dou metode virtuale pure, Push() i Pop(). Acestea sunt implementate n clasele derivate, specific fiecrei structuri de date. n cazul stivei, metoda Pop() returneaz ultimul element din stiv. n cazul cozii, metoda Pop() returneaz primul element din coad. Metoda Push() introduce un ntreg n capul listei interne folosite pentru stocare n cazul stivei, sau la coada ei, n cazul cozii. Derivarea din aceeai clas de baz i folosirea metodelor virtuale permite tratarea ntr-o manier omogen a tablourilor eterogene de obiecte de tip stiv sau coad. S deschidem un nou proiect Win32 Console Application, pe care s-l numim Liste. S implementm fiierul header ca mai jos:
class Baza; class Stiva; class Coada; class ElementLista { int valoare; ElementLista* urmatorul; public: ElementLista(int i=0); friend class Baza; friend class Stiva; friend class Coada; }; class Baza { protected: ElementLista* CapLista; public: // Baza(): CapLista(NULL){} Baza() {CapLista=NULL;} ~Baza(); virtual void Afisez(); virtual void Push(int)=0; virtual int Pop() =0; }; class Stiva: public Baza { public: virtual void Afisez(); virtual void Push(int); virtual int Pop(); };

Capitolul 1. noiuni de programare obiectual

51

class Coada: public Baza { public: virtual void Afisez(); virtual void Push(int); virtual int Pop(); };

Ce am declarat de fapt? O clas ElementLista, care implementeaz o structur de date caracteristic listei simplu nlnuite. Cum datele sunt private, dar vor fi folosite n alte clase, am declarat aceste clase ca fiind prietene. Deoarece aceste clase nu au fost nc declarate, ele trebuie anunate la nceputul fiierului. Constructorul clasei construiete implicit un obiect cu cmpul valoare=0. Am declarat apoi o clas abstract Baza, care declar funciile virtuale pure Push() i Pop() i funcia virtual Afisez(). Este evident c funciile Push() i Pop() sunt virtuale pure, deoarece nc nu tim clar n ce poziii ale listei acioneaz ele. Clasa conine un pointer la clasa ElementLista, care va fi de fapt capul listei simplu nlnuite. Constructorul clasei construiete acest pointer implicit NULL (s ne reamintim c am dat cmpului valoare implicit valoare 0 din construcia obiectului ElementLista). n exemplu sunt date dou modaliti de implementare a constructorului : cu list de iniializare i respectiv clasic. n continuare sunt declarate clasele derivate din clasa de baz, care vor implementa stiva i respectiv coada. Fiierul surs pentru programul exemplu va fi:
#include <iostream.h> #include "Liste.h" ElementLista::ElementLista(int i) { valoare=i; urmatorul=NULL; } Baza::~Baza() { ElementLista* ptr=CapLista; while (CapLista!=NULL) { CapLista=CapLista->urmatorul; delete ptr; ptr=CapLista; } } void Baza::Afisez() { ElementLista* ptr=CapLista; if (ptr==NULL) cout << "\n Structura de date este vida! "; else while (ptr!=NULL) { cout << "\n " << ptr->valoare; ptr=ptr->urmatorul; }

H. Vlean, 2004

52
}

Visual C++. Programarea Interfeelor Utilizator

void Stiva::Push(int i) { ElementLista* ptr=new ElementLista(i); ptr->urmatorul=CapLista; CapLista=ptr; } int Stiva::Pop() { int valret; ElementLista* ptr; if (CapLista==NULL) { cout << "\n Stiva este vida! "; return 0; } valret=CapLista->valoare; ptr=CapLista; CapLista=CapLista->urmatorul; delete ptr; return valret; } void Stiva::Afisez() { cout << "\n Stiva contine: "; Baza::Afisez(); } void Coada::Push(int i) { ElementLista* ptr, *ElementNou= new ElementLista(i); if (CapLista==NULL) CapLista=ElementNou; else { ptr=CapLista; while(ptr->urmatorul!=NULL) ptr=ptr->urmatorul; ptr->urmatorul=ElementNou; }

int Coada::Pop() { int valret; ElementLista* ptr; if (CapLista==NULL) { cout << "\n Coada este vida! "; return 0; } valret=CapLista->valoare; ptr=CapLista; CapLista=CapLista->urmatorul; delete ptr; return valret;

Capitolul 1. noiuni de programare obiectual


void Coada::Afisez() { cout << "\n Coada contine: "; Baza::Afisez(); } void main() { Baza* ptab[2]; ptab[0]=new Stiva; ptab[1]=new Coada; ptab[0]->Push(1); ptab[0]->Push(2); ptab[0]->Afisez(); ptab[0]->Pop(); ptab[0]->Afisez(); ptab[1]->Push(1); ptab[1]->Push(2); ptab[1]->Afisez(); ptab[1]->Pop(); ptab[1]->Afisez(); }

53

Funciile Push() i Pop() sunt astfel implementate nct pentru stiv insereaz i scot un element din capul listei, iar pentru coad, insereaz un element la sfrit i respectiv scot un element din capul listei. ntrebri i probleme propuse Implementai i executai toate exemplele propuse n capitolul 1; Este ntotdeauna util declararea unei funcii inline? n ce situaii este util? n ce condiii poate fi apelat din afara clasei un membru private? Cnd trebuie utilizat numele calificat pentru definirea unei funcii membru a unei clase? 5. Ce deosebire este ntre o variabil static i una nestatic n declararea unei clase? 6. Cnd i de ce trebuie declarat explicit constructorul de copiere? n acelai caz este obligatorie supranscrierea operatorului =? 7. Avem urmtoarele linii de cod:
class A { public: int v1; protected: int v2; private: int v3; }; class B: public A { public: void afisez() { cout << v1; cout << v2; }; };

1. 2. 3. 4.

H. Vlean, 2004

54

Visual C++. Programarea Interfeelor Utilizator


class C: private A { public: void SiEuAfisez() { cout << v1; cout << v2; cout << v3; }; }; void main() { A vara; B varb; C varc; vara.v1=5; vara.v2=7; varb.v1=3; varb.v2=5; varc.v1=7; varc.v3=8; }

Care din liniile surs vor genera erori de compilare i de ce? 8. n ce situaii funciile trebuie declarate virtuale? Cnd o funcie virtual poate fi declarat, dar nu i implementat? 9. Concepei i implementai obiectual un program care s creeze i s execute operaii cu o list dublu nlnuit (creare, adugare la nceput i sfrit, tergere la nceput i sfrit, parcurgere nainte i napoi, etc);

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