- •Programare orientată pe obiecte
- •§1. Principiile programării orientate pe obiecte.
- •§3. Constructori şi destructori.
- •8) Aceste mesaje apar după ce se termină lucrul programului.
- •§4. Operaţii de intrare/ieşire a informaţiei.
- •§5. Moştenire simplă.
- •§6. Moştenire multiplă
- •§7. Moştenirea pe mai multe niveluri. Clase virtuale
- •§9. Definirea şi utilizarea referinţelor
- •§10. Tablouri de obiecte. Pointeri şi referinţe la obiecte. Pointeri la membrii clasei
- •§11. Dirijarea dinamică a memoriei
- •§12. Constructor de copiere
- •§13. Funcţii prietene şi clase prietene
- •§14. Supraîncărcarea operatorilor
- •§15. Supraîncărcarea operatorilor prin funcţii prietene
- •§16. Supraîncărcarea unor operatori speciali
- •Operatorii de incrementare şi decrementare
- •Operatorii de inserţie şi extragere
- •Operatorul indice
- •Operatorul funcţie
- •Operatorii new şi delete
- •Operatorul virgulă
- •Operatorul de conversie
- •§17. Funcţii-şablon şi clase-şablon
- •§18. Realizarea conceptului de polimorfism
- •§19. Clase abstracte
- •§20. Membrii statici ai clasei
- •§23. Tratarea excepţiilor
- •Bibliografie
Programare orientată pe obiecte
(suport de curs)
§1. Principiile programării orientate pe obiecte.
Strategia procedurală şi structurată de scriere a programelor presupune existenţa unui program principal şi a unui şir de proceduri (subprograme, funcţii), iar rezultatul final este obţinut prin apelarea procedurilor respective transmiţându-le lor datele necesare:
Programarea orientată pe obiecte (POO) este tehnologia de programare în care programul este reprezentat ca o colecţie de obiecte discrete, care conţin nişte seturi de structuri de date şi proceduri ce interacţionează cu alte obiecte:
Există mai multe sensuri ale cuvântului „obiect”:
corp solid, cunoscut direct cu ajutorul simţului;
lucru sau complex de lucruri apărute ca rezultat al procesului de muncă (de exemplu, obiect de consum, obiect de uz casnic);
materie asupra căreia este orientată activitatea spirituală sau artistică (de exemplu, obiect de cercetare, obiect de descriere);
fiinţă sau lucru pentru care cineva manifestă un sentiment (de exemplu, obiect de admiraţie);
disciplină de studiu într-o instituţie de învăţământ;
filos. Corp sau fenomen existent în realitate, în afara subiectului şi independent de conştiinţa acestuia;
lingv. Parte de propoziţie care indică asupra cui este orientată acţiunea verbului; complement.
Aceste definiţii cuprind diferite domenii ce ţin de natură şi de activitatea omului. Ele toate pot fi modelate în POO prin declararea claselor şi crearea obiectelor respective.
Pentru a aplica tehnologia orientată pe obiecte, iniţial, este necesar de a realiza o etapă importantă - abstractizarea. Modelul fizic al oricărui domeniu de problemă este unul complex, depinzând de mulţi parametri şi relaţii. Astfel, se face o simplificare a modelului fizic, obţinând modelul logic al domeniului de problemă. Sunt omise din modelul fizic o serie de proprietăţi şi legături care nu sunt esenţiale în contextul problemei formulate. Acest proces de simplificare se numeşte abstractizare:
Deci, abstractizarea presupune „sărăcirea” datelor iniţiale.
Exemplu. La elaborarea unui program de evidenţă a cadrelor într-o întreprindere, este necesar să descriem obiectul „Angajat al întreprinderii” care în esenţă este o persoană (o fiinţă umană). Orice persoană angajată la întreprindere poate fi descrisă (caracterizată) printr-un set de proprietăţi, cum ar fi de exemplu:
nume;
prenume;
domiciliu;
anul naşterii;
funcţia;
salariu;
vechimea în muncă;
înălţime;
culoarea ochilor;
culoarea părului;
greutatea;
numărul de copii;
etc.
De aici se observă că o serie de proprietăţi nu sunt adecvate problemei formulate, de exemplu, înălţime, culoarea ochilor, culoarea părului şi greutatea, ele fiind eliminate din descriere.
Pe lângă proprietăţi un obiect poate fi caracterizat şi printr-o serie de operaţii (funcţionalitate), de exemplu:
Schimbarea salariului;
Schimbarea domiciliului;
Schimbarea funcţiei;
Creşterea vechimii în muncă;
Schimbarea numelui;
etc.
Definiţie. Obiectul în programare reprezintă modulul de program care îndeplineşte următoarele cerinţe principale:
întruneşte date (caracterizează proprietăţile obiectului) şi operaţiile asupra lor (se mai numesc metode şi caracterizează comportarea obiectului sau posibilităţile obiectului, adică ceea ce poate face obiectul);
posedă proprietăţile de moştenire, încapsulare şi polimorfism.
La baza programării orientate pe obiecte stă noţiunea de clasă.
O clasă în programare întruneşte toate obiectele de una şi aceeaşi natură (la fel ca şi în lumea reală). Obiectele care aparţin uneia şi aceleiaşi clase posedă una şi aceeaşi structură, comportare şi relaţii cu obiecte din alte clase.
Definiţie. Clasa este tipul abstract de date creat de utilizator (programator). În clasă se descriu împreună câmpurile de date şi metodele sub formă de funcţii membre. Metodele efectuează operaţii asupra acestor date şi alte acţiuni ce caracterizează comportamentul obiectelor clasei respective. În baza claselor deja create există posibilitatea de a crea clase derivate care moştenesc proprietăţile claselor de bază.
Exemplarul de obiect în POO se mai numeşte instanţă (engl. instance), este un obiect concret din setul de obiecte ale uneia şi aceleiaşi clase. Crearea exemplarului de obiect al unei clase se numeşte instanţierea obiectului.
Când creăm o clasă, definim implicit un nou tip de date. Deci, un obiect este o variabilă de un tip definit de utilizator (tipul clasă).
Programarea orientată pe obiecte se bazează pe un şir de principii.
Principiul 1. Încapsulare.
Încapsularea în POO (engl. encapsulation) este unirea într-un singur tot a datelor şi metodelor, ceea ce permite ascunderea structurii interne de date şi a metodelor obiectului de restul programului. Celelalte obiecte din program au acces numai la interfaţa obiectului. Interfaţa este reprezentată prin membrii publici prin care se efectuează toate interacţiunile cu acest obiect.
Încapsularea este un mecanism care leagă într-un tot întreg codul şi datele, păstrându-le pe ambele în siguranţă contra intervenţiilor din afară şi de utilizări greşite. În plus, încapsularea asigură crearea obiectelor. Un obiect este o entitate logică ce încapsulează atât date, cât şi codul care manevrează aceste date. Într-un obiect, o parte din cod şi/sau date pot fi particulare (private sau protejate) acelui obiect şi inaccesibile pentru oricine din afara lui. Astfel, un obiect dispune de un nivel semnificativ de protecţie care împiedică modificarea accidentală sau utilizarea incorectă a părţilor obiectului de către secţiuni ale programului cu care acestea nu au legătură.
Principiul 2. Moştenire.
Moştenirea în POO (engl. inheritance) reprezintă proprietatea obiectului, care constă în aceea că caracteristicile unui obiect (obiectul-părinte) pot fi transmise unui alt obiect (obiect-fiu) fără descrierea lor repetată. Moştenirea simplifică descrierea obiectelor şi admite clasificarea lor.
Exemplu. Clasa furnici face parte din clasa de insecte fără aripi, care la rândul său face parte din clasa mai generală insecte. O astfel de clasificare se numeşte taxonomie. Taxonomia este o ştiinţă care se ocupă cu stabilirea legilor de clasificare şi de sistematizare a domeniilor cu o structură complexă.
Fără utilizarea claselor cu proprietatea de moştenire fiecare obiect ar trebui definit cu enumerarea tuturor caracteristicilor sale. Însă, prin folosirea clasificărilor, un obiect are nevoie doar de definirea acelor calităţi care îl fac unic în clasa sa. Mecanismul moştenirii permite ca un obiect să fie descris ca un exemplar al unui caz mai general.
Principiul 3. Polimorfism.
În limbajele de programare orientate pe obiecte polimorfismul este caracterizat prin fraza „o singură interfaţă, metode multiple”. Polimorfismul în POO (engl. polymorphism) este capacitatea obiectului de a selecta, din mai multe metode metoda corectă, în dependenţă de tipul de date, primite în mesaj, sau, altfel spus, posibilitatea de a denumi la fel diferite acţiuni la diferite niveluri ale ierarhiei de clase. Polimorfismul simplifică programarea, deoarece selectarea acţiunii necesare se realizează automat.
Dăm un exemplu de polimorfism din matematică. Una şi aceeaşi expresie a+b va fi interpretată în conformitate cu tipul operanzilor operaţiei de adunare. Dacă a şi b sunt două numere, expresia dată reprezintă suma a două numere, dacă a şi b sunt două matrice – rezultatul va fi suma matricelor, dacă a şi b sunt doi vectori – expresia reprezintă vectorul rezultant etc.
În limbajele de programare orientate pe obiecte se folosesc mai multe mecanisme prin care se realizează principiul de polimorfism. Cele mai principale sunt suprascrierea şi supraîncărcarea funcţiilor şi operatorilor, posibilitatea de a specifica valorile implicite pentru parametri, regulile de atribuire între obiecte ale claselor descendente, funcţiile virtuale.
§2. Clase şi obiecte în C++.
În forma cea mai generală o clasă poate fi descrisă astfel:
class [nume_clasa]
{
//membrii clasei
};
unde membrii clasei sunt de două tipuri:
- date membre;
- funcţii membre.
Cu ajutorul datelor membre pot fi descrise atributele care caracterizează proprietăţile obiectelor reale, modelate în cadrul programelor create. Funcţiile membre descriu funcţionalităţile claselor de obiecte din domeniul de problemă.
Forma generală de descriere a unei clase este următoarea:
class [nume_clasa] //identificator
{
[specificator_de_acces_a1:]
lista_membri_1;
specificator_de_acces_a2:
lista_membri_2;
- - - - - - - - - - -
specificator_de_acces_aN:
lista_membri_N;
} [lista_obiecte] ;
[descrierea_funcţiilor_membre_si_prietene]
unde nume_clasa – un identificator, numele clasei,
lista_obiecte – variabile de tip clasă, sau obiecte ale clasei descrise.
În această formă de declarare a unei clase, componentele incluse în paranteze pătrate sunt opţionale (pot fi omise).
Componentele din descriere specificator_de_acces_a1, specificator_de_acces_a2, ..., specificator_de_acces_aN, se numesc specificatori de acces şi indică gradul de acces la membrii clasei. Fiecare membru al clasei se află sub acţiunea unui specificator de acces. Specificatorii de acces sunt descrişi prin cuvintele cheie private, protected şi public (rom.: acces privat, protejat, public), urmate de semnul „:”. Următorul tabel descrie gradul de acces permis de fiecare specificator:
|
Funcţii membre ale clasei date |
Funcţii prietene
|
Funcţii membre ale claselor derivate |
Funcţii externe
|
private |
+ |
+ |
- |
- |
protected |
+ |
+ |
+ |
- |
public |
+ |
+ |
+ |
+ |
După cum se vede, declaraţia începe cu cuvântul-cheie class, după care urmează denumirea clasei. Denumirea clasei este orice identificator creat de către programator conform sintaxei limbajului C++ şi care este liber în momentul declarării. Apoi, urmează corpul clasei luat în acolade { şi }. Corpul poate fi şi vid. Corpul clasei poate conţine declarări de câmpuri (date) membre, declarări de prototipuri ale funcţiilor membre şi ale funcţiilor prietene, definiri de funcţii membre şi de funcţii prietene, declarări de clase prietene. Declararea câmpurilor şi a funcţiilor membre se repartizează pe secţiuni în dependenţă de regimul dorit de accesare a lor. Acest regim este stabilit prin utilizarea a trei specificatori de acces: public, protected, private. Ordinea secţiunilor, precum şi numărul de repetări sunt arbitrare. Dacă la începutul corpului de declarare a clasei, pentru un set de câmpuri şi funcţii membre specificatorul de acces nu este indicat, implicit, pentru câmpurile şi funcţiile membre din această secţiune (prima secţiune) va fi stabilit regimul de accesare private (privat) – cel mai dur regim de accesare din cele trei. Câmpurile şi funcţiile membre private sunt accesibile numai din interiorul acestei clase, adică acces la ele au numai funcţiile membre ale sale, sau funcţiile de tip prieten (friend) al acestei clase. Regimul de accesare protected (protejat) este puţin mai liber în comparaţie cu regimul private. El stabileşte că câmpurile şi funcţiile membre ale clasei definite vor fi accesibile şi din interiorul claselor derivate ale clasei definite. Specificatorul de acces public este cel mai liber din cei trei. El admite accesarea câmpurilor şi a funcţiilor membre ale acestei secţiuni din orice loc al programului unde va fi vizibil obiectul concret al clasei definite. Aşa o accesare este asigurată prin intermediul numelui acestui obiect.
În C++ declararea claselor este similară cu declararea structurilor. În cazul structurilor se foloseşte cuvântul-cheie struct în loc de class. O deosebire constă în aceea că în cazul structurilor, implicit, va fi stabilit regimul de accesare public şi nu private, cum a fost descris mai sus pentru clase.
Imediat după descrierea corpului clasei (acolada de închidere) putem crea un set de obiecte ale acestei clase. Însă, acest lucru nu este obligatoriu. Obiectele pot fi create şi mai târziu în program.
După caracterul ';' urmează definiţiile (realizările) funcţiilor membre, dacă prototipurile lor au fost declarate în corpul clasei.
Exemplu 1. Declarăm versiunea simplificată a clasei pentru reprezentarea fracţiilor fără semn, cu numărător şi numitor – numere naturale (numere raţionale pozitive). Vom numi această clasă fractie:
#include <conio.h>
#include <iostream.h>
class fractie
{
protected:
unsigned int numarat;
unsigned int numit;
};
void main()
{
fractie fr1; // creăm un obiect numit fr1, reprezentantul clasei
// fractie
fractie fr2; // mai creăm un obiect numit fr2, reprezentantul aceleiaşi
// clase
fr1 = fr2; // atribuim obiectului fr1 obiectul fr2
char c; cin >> c; // se aşteaptă apăsarea oricărei taste
}
De fapt, conform sintaxei, o versiune minimală a clasei fractie este:
class fractie
{
};
Însă, fiind perfect validă din punctul de vedere al sintaxei, ea nu este interesantă din punctul de vedere al semanticii, de aceea nici nu va fi examinată.
Clasa fractie conţine două câmpuri: numarat şi numit pentru păstrarea, respectiv, a numărătorului şi a numitorului ce alcătuiesc o fracţie raţională fără semn. Ambele câmpuri sunt de tipul unsigned int şi au unul şi acelaşi tip de acces protected. Dacă înlăturăm linia:
protected:
din programul de mai sus, atunci, implicit, câmpurile numarat şi numit vor avea tipul de acces private. Însă, în ambele cazuri, cu obiectele create ale clasei fractie putem doar să atribuim un obiect altuia, cum este arătat în funcţia main(). Operatorul de atribuire este implicit realizat cu orice clasă declarată. El presupune copierea datelor (valoarea numărătorului şi numitorului) din a doua fracţie în prima. Nu se ştie ce va fi copiat în fr1 în exemplul nostru, fiindcă nici în câmpurile obiectului fr1, nici în câmpurile obiectului fr2 nu a fost înscrisă nici o valoare până la aplicarea operaţiei de atribuire. Iniţializarea nu a fost prevăzută în versiunea clasei de mai sus. Mai mult ca atât, nici nu există posibilitatea de a înscrie cumva valori în câmpurile obiectelor fr1 şi fr2 (din cauza că sunt protejate). Unicul lucru care poate fi afirmat este că după atribuire, conţinuturile câmpurilor obiectelor fr1 şi fr2 vor fi identice.
Din cauza că câmpurile clasei sunt protejate, orice încercare de accesare, cum ar fi, de exemplu:
fr1.numarat = 2;
fr2.numit = 3;
vor fi respinse de către compilator, fiind interzise (se va semnala o eroare).
În versiunea propusă a clasei fractie nu putem iniţializa câmpurile obiectelor create, nu putem afişa obiectele, nu putem efectua calcule asupra lor etc. Aceste acţiuni se pot realiza cu ajutorul funcţiilor membre (metodelor) ale clasei.
Exemplu 2. De alcătuit un program în care este descrisă clasa carte. În baza acestei clasei sunt create obiecte, care sunt apoi utilizate, apelând la metodele clasei initializare şi afisare.
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
class carte
{ char* autor;
char* denumire;
int an_ed;
public:
void initializare (char* a, char* d, int ae);
void afisare();
void nimicire();
};
void carte::initializare(char* a, char* d, int ae)
{
autor=(char*)malloc(srtlen(a)+1);
strcpy(autor,a);
denumire=(char*)malloc(strlen(d)+1);
strcpy(denumire,d);
an_ed=ae;
}
void carte::afisare()
{
printf(”Autorul cartii: %s\n”,autor);
printf(”Denumirea cartii: %s\n”,denumire);
printf(”Anul editarii: %d\n”,an_ed);
}
void carte::nimicire()
{
free (autor);
free (denumire);
}
void main ()
{
class carte em, an;
em.initializare(”M.Eminescu”,”Opere complete”,2002);
an.initializare(”A.Demidovici”,”Analiza matematica”,1960);
em.afisare();
an.afisare();
em.nimicire();
an.numicire();
}
În rezultatul îndeplinirii acestui program se va afişa pe ecran:
Autorul cartii: M.Eminescu
Denumirea cartii: Opere complete
Anul editarii: 2002
Autorul cartii: A.Demidovici
Denumirea cartii: Analiza matematica
Anul editarii: 1960
