A scrie cod orientate pe obiecte este mult mai mult decat un stil de programare; este poate, in primul rand, un mod de gandire. Redactarea codului orientat pe obiecte face ca acesta sa fie mult mai usor de intretinut, modificat, depanat, extins si refolosit. Codul orientat pe obiecte devine obligatoriu a fi folosit atunci cand se redacteaza aplicatii de dimensiuni medii sau mari, unde isi aduc aportul un numar mare de programatori.
Din punct de vedere al redactarii codului, o clasa este o colectie de campuri (date membre) si metode (functii membre). Metodele prelucreaza datele membre la care programatorul nu are acces direct. Exista metode publice pe care programatorul le poate apela pentru a folosi facilitatile clasei si exista metode private care sunt folosite la implementarea functionalitatii clasei. De regula, facilitatile oferite de o clasa sunt folosite prin intermediul unui asa numit obiect instanta clasei respective. Cu alte cuvinte, obiectul este o variabila de tip clasa. Exista si facilitati ale clasei ce pot fi utilizate fara a necesita crearea unui obiect. Membrii de acest tip ai claselor se numesc membrii statici.
Este unul dintre conceptele POO. Abstractizarea datelor inseamna crearea unui tip abstract de date (ADT = abstract data type) pentru care o parte dintre facilitati sunt ascunse. Un tip abstract de date incepe prin a specifica reprezentarea interna a datelor care, de regula, sunt ascunse, nu pot fi accesate direct, iar pentru acestea trebuie specificate suficiente functii de prelucrare astfel incat cunoasterea reprezentarii interne a tipului abstract sa nu mai fie necesara.
De exemplu, tipul FILE
din C este un bun exemplu de ADT. FILE
este o structura cu o multime de campuri folosite in prelucrarea unui fisier. In C avem suficiente functii scrise pentru tipul FILE
asa incat, prelucrarea fisierului se face exclusiv apeland la aceste functii, utilizatorul nefiind nevoit sa cunoasca niciun detaliu legat de campurile tipului FILE
, aceste campuri modificandu-se in urma apelurilor de functii. Ceea ce lipseste tipului FILE
este ascunderea datelor, adica programatorul poate interoga si modifica aceste campuri.
In momentul in care, pentru ADT avem functiile redactate, nici nu mai este indicat ca programatorul sa mai acceseze datele ADT-ului. Modificarea datelor poate altera functionalitatea gandita pentru fisier, in exemplul nostru.
Un ADT poate fi foarte bine implentat folosind o clasa. In plus fata de tipul struct din C, intr-o clasa putem ascunde datele membre, asa cum este si recomandat. Mai mult, functiile de prelucare a ADT-ului sunt puse in interiorul clasei.
Incapsularea datelor este un alt concept POO, care pune la un loc, intr-o clasa, toate datele necesare, la aceste date avand acces numai prin instantiere (creearea unui obiect). Evident, datele introduse intr-o clasa, de regula, se si ascund, se restrictioneaza accesul.
Mostenirea ajuta la reutilizarea si extinderea unui cod existent. Facilitatile uneia sau mai multor clase sunt mostenite, adica sunt aduse intr-o noua clasa. Pe langa aceste facilitati mostenite, in noua clasa pot fi adaugate facilitati noi, se pot face adaptari prin adaugare de functii noi a codului pentru un nou scop.
In general, in limbajele de programare care suporta POO, o clasa mosteneste o singura clasa. In C++ exista posibilitatea ca o clasa sa mosteneasca mai multe clase, adica avem mostenire multipla.
Clasa mostenita se numeste clasa parinte / tata / de baza. Clasa care mosteneste se numeste clasa derivata / fiu.
Mostenirea este folosita si cu scopul de a particulariza o clasa existenta. In cele mai multe situatii, o clasa derivata extinde facilitatile claselor mostenite si face particularizari.
De exemplu, pornind de la o clasa dreptunghi putem creea prin mostenire o clasa patrat. Tot ceea ce este valabil pentru dreptunghi este valabil si pentru patrat. Anumite facilitati mostenite de la dreptunghi merita a fi folosite si pentru patrat; de exemplu, pentru patrat nu merita sa rescriem aria, deoarece, atat aria dreptunghiului, cat si a patratului se reduc la o inmultire; in schimp, pentru perimetru in clasa patrat ar merita rescrisa aceasta metoda, deoarece pentru patrat perimetrul se poate calcula mai eficient, decat pentru dreptunghi. Pe langa aceste metode specifice atat dreptunghiului cat si patratului, pentru patrat putem adauga metode specifice numai acestuia, de exemplu, raza cercului inscris.
Despre o clasa derivata se spune ca este de tipul clasei mostenite sau extinde clasa mostenita. In Java, la mostentire se pune cuvantul extend
.
In exemplul anterior, in limbaj POO se spune ca patratul este dreptunghi.
Dupa anumiti specialisti in POO, mostenirea multipla nu este acceptata. In plus, mostenirea multipla vine la pachet cu o multime de probleme specifice ce trebuie rezolvate.
Este cunoscuta problema diamantului:
In D facilitatile din A ajung in D prin doua cai, atat prin B1, cat si prin B2. Cu ajutorul conceptului de mostenire se obtin ierarhiile de clase. De regula, o astfel de ierarhie este un arbore, care pleaca dintr-o radacina. Mostenirea multipla poate strica ideea de arbore dintr-o ierarhie.
Polimorfismul este un alt concept POO care apare in urma mostenirii. In sens general, prin polimorfism intelegem proprieatatea de a avea mai multe forme. Cand vorbim de POO, prin polimorfism intelegem ca o functie poate fi implentat diferit pe nivele diferite intr-o ierarhie. Apare astfel ideea de suprascriere (overwriting). De exemplu: in cazul mostenirii dreptunghi - patrat am spus ca functia perimetru merita sa fie rescrisa mai eficient in clasa patrat. Deci, avem functia perimetru implementata in dreptunghi, stil dreptunghi si suprascrisa mai eficient in clasa patrat pentru patrat. Cele doua functii au aceeasi semnatura. In consecinta, in clasa patrat avem doua metode perimetru cu aceeasi semnatura.
Limbajul C nu a fost gandit pentru a scrie cod orientat pe obiect. Cu toate acestea, folosind tipul struct
putem creea tipuri de date asemanatoare claselor, incapsularea realizandu-se. Mai mult, putem aduce functii in interiorul tipului struct
din C, prin intermediul unor campuri de tipul pointer catre functie.
Neajunsuri:
- Unul dintre cele mai mare neajunsuri este dat de faptul ca datele membre sunt toate publice, printr-o variabila de tip
struct
. - Redactarea functiilor folosind pointer catre functii este nefireasca si complicata.
- Nu avem mecanism nativ pentru mostenire si suprascriere de functii
Limnajul C++ a rezolvat toate aceste neajunsuri o data cu introducerea tipului
class
.
Din cauza ca o clasa este un tip de date ce poate deveni deosebit de complex apare necesitatea aparitiei unor membrii speciali, denumiti constructori. Atunci cand instantiem o clasa, unul din constructorii clasei este apelat. In urma apelului, se face initializarea obiectului:
- aloca zona de memorie dinamica
- initializari de campuri
- deschide de fisiere
- creare de diverse conexiuni (db, internet) Utilizand un constructor sau altul, un obiect poate fi pregatit pentru un anumit mod de utilizare. O clasa poate avea mai multi constructori. Destructorul este un membru special ce se apeleaza atunci cand un obiect este distrus (se elibereaza memoria ocupata de acesta). Inainte ca obiectul sa fie distrus, destructorul:
- elibereaza zona de memorie alocata dinamica
- inchide fisiere
- inchide conexiuni In functie de limbajul de programare, o clasa poate avea un destructor, mai multi destructori sau niciunul; de exemplu, C++ are un singur desctructor pentru clasa, Pascal accepta mai multi destructori, iar Java nu are destructori.