Notiuni de baza ale programarii orientate pe obiecte în limbajul C

23
Notiuni de baza ale programarii orientate pe obiecte în limbajul C++ 1.Introducere Exista limbaje concepute strict pe baza conceptelor programarii orientate pe obiecte (POO), de exemplu Simula sau Smalltalk. O alta abordare este de a adauga unor limbaje cu o popularitate bine stabilita, de exemplu Pascal si C, elementele tehnicii POO. C++ combina avantajele oferite de limbajul C( eficienta, flexibilitate si popularitate) cu avantajele oferite de tehnica POO. Desi adopta principiile POO, C++ nu impune aplicarea lor stricta ( se pot scrie programe fara elemente POO). Conceptul fundamental în C++ este clasa. Clasele sunt tipuri definite de utilizator, asigura încapsularea datelor, garanteaza initializarea datelor, gestiunea memoriei controlata de utilizator, mecanisme pentru supraincarcarea operatorilor. C++ contine de asemenea îmbunatatiri ale limbajului C care nu sunt direct legate de POO, cum ar fi: tipul referinta, nume de functii supraincarcate, operatori pentru gestionarea memoriei libere. 2. îmbunatatiri ale limbajului C introduse de C++ 2.1.Transmiterea parametrilor În C++ exista doua posibilitati de transmitere a parametrilor actuali catre o functie: 1. mecanismul de transmitere prin valoare 2. mecanismul de transmitere prin referinta

Transcript of Notiuni de baza ale programarii orientate pe obiecte în limbajul C

Page 1: Notiuni de baza ale programarii orientate pe obiecte în limbajul C

Notiuni de baza ale programarii orientate pe obiecte în limbajul C++

1.Introducere

Exista limbaje concepute strict pe baza conceptelor programarii orientate pe obiecte (POO), de exemplu Simula sau Smalltalk. O alta abordare este de a adauga unor limbaje cu o popularitate bine stabilita, de exemplu Pascal si C, elementele tehnicii POO.

C++ combina avantajele oferite de limbajul C( eficienta, flexibilitate si popularitate) cu avantajele oferite de tehnica POO. Desi adopta principiile POO, C++ nu impune aplicarea lor stricta ( se pot scrie programe fara elemente POO). Conceptul fundamental în C++ este clasa. Clasele sunt tipuri definite de utilizator, asigura încapsularea datelor, garanteaza initializarea datelor, gestiunea memoriei controlata de utilizator, mecanisme pentru supraincarcarea operatorilor.

C++ contine de asemenea îmbunatatiri ale limbajului C care nu sunt direct legate de POO, cum ar fi: tipul referinta, nume de functii supraincarcate, operatori pentru gestionarea memoriei libere.

2. îmbunatatiri ale limbajului C introduse de C++

2.1.Transmiterea parametrilor

În C++ exista doua posibilitati de transmitere a parametrilor actuali catre o functie:

1. mecanismul de transmitere prin valoare 2. mecanismul de transmitere prin referinta

Primul este cunoscut, constituie modul standard de transmitere a parametrilor in C.

Prin folosirea parametrilor formali referinta, se permite realizarea transferului prin referinta(transmiterea adresei) de o maniera similara celei din Pascal (parametrii VAR). Se elimina astfel necesitatea recurgerii la artificiul din C, adica utilizarea parametrilor formali pointeri, în cazul în care modificarile facute in interiorul functiei asupra parametrilor trebuie sa ramâna si dupa revenirea din procedura.

Exemplu:

void schimba(int &a, int &b) {int aux=a; a=b; b=aux; }

Page 2: Notiuni de baza ale programarii orientate pe obiecte în limbajul C

Transferul prin referinta este util si atunci când parametrul are dimensiune mare (struct, class) si crearea în stiva a unei copii a valorii lui reduce viteza de executie si încarca stiva.

2.2.Parametrii cu valori implicite

C++ ofera posibilitatea declararii functiilor cu valori implicite ale parametrilor. La apelarea unei astfel de functii se poate omite specificarea parametrilor efectivi pentru acei parametri formali care au declarate valori implicite si se transfera automat valorile respective. Se pot specifica mai multe argumente cu valori implicite pentru o functie. Este obligatoriu ca numai ultimele argumente sa aiba astfel de valori si nu este permisa alternarea argumentelor cu si fara valori implicite.

Exemplu:

void fct(int, int=10);void fct(int p1, int p2){

tipareste(p1); tipareste(p2);}

void main(void) {fct(1,2);fct(3);}

Rezultatul rularii acestui program va fi tiparirea numerelor:1, 2, 3,10.

2.3.Functii inline

O tehnica utila pentru a mari performanta programelor ce folosesc multe functii de foarte mici dimensiuni, pentru care regia de apel este semnificativa în raport cu timpul de executie al functiei propriu-zise, este folosirea functiilor inline. Functiile declarate ca inline vor fi expandate in-line la compilare, compilatorul generind codul corespunzator functiei în pozitia apelului, in loc de a genera secventa de apel. Pentru a indica faptul ca o functie este de acest tip, se precede declaratia sau definitia ei cu cuvântul cheie inline.

2.4.Supradefinirea (supraîncarcarea) functiilor

Supradefinirea reprezinta posibilitatea de a atribui unui simbol mai multe semnificatii, care pot fi deduse în functie de context. Limbajul C++ permite supraincarcarea (overloading) functiilor, adica existenta mai multor functii cu acelasi nume. Când compilatorul întâlneste un apel la o functie supraîncarcata, va determina care functie va fi apelata prin examinarea numarului si tipului argumentelor. Nu se verifica si tipul valorii returnate, deci doua functii supraîncarcate nu pot diferi doar prin valoarea returnata.

De exemplu:

void fct(int a) { cout << "functia 1" << a;

Page 3: Notiuni de baza ale programarii orientate pe obiecte în limbajul C

}

void fct(char *a) { cout << "functia 2" << a;}

In particular, majoritatea operatorilor C++ pot fi priviti ca nume de functii si deci pot fi supraîncarcati. Antetul unei asemenea functii contine cuvântul cheie operator în fata simbolului operatorului.

2.5. Alocarea dinamica a memoriei folosind operatorii new si delete

Operatorii new si delete sunt similari functiilor din grupul malloc() si free(), dar constituie o metoda noua, superioara acestora si adaptata programarii orientate pe obiecte.

Operatorul new poate fi folosit in urmatoarele situatii:

Exemplu:

int * ip1, *ip2, *ip3;ip1=new int; // variabila întreaga neinitializataip2=new int(2); // variabila întreaga initializata cu 2ip3=new int[100]; // tablou de 100 de întregi

Operatorul new poate fi folosit si la alocarea dinamica a obiectelor,in cazul acesta permitînd apelarea constructorului clasei.

Operatorul delete este complementarul lui new si inlocuieste functia free() de dezalocare a memoriei dinamice alocate. Daca exista un destructor pentru obiectul care se dezaloca, acest destructor va fi apelat automat de catre delete.

2.6. Operatorul de rezolutie

In C++ este definit un operator de rezolutie ( :: ), care permite accesul la un identificator cu domeniu fisier, dintr-un bloc în care acesta nu este vizibil, datorita unei alte declaratii. Principala aplicatie a operatorului este legata de clase si obiecte si va fi prezentata mai târziu.

Exemplu:

int n=1;void main(void) { int n=2; afiseaza(n); // afiseaza 2, valoarea variabilei locale n afiseaza(::n); // afiseaza 1,valoarea variabilei globale n }

Page 4: Notiuni de baza ale programarii orientate pe obiecte în limbajul C

3. Clase

3.1. Tipuri class

In C++ clasa este un concept fundamental, este constructia prin care se definesc noi tipuri de date, prin asocierea unui set de functii la o structura de date.

Definitia unei clase presupune declararea clasei, la care se specifica numele clasei, lista claselor de baza din care e derivata clasa, daca exista, si membrii clasei, atât membrii de date cat si functii.

Este posibil controlul accesului atat la datele membre cat si la functiile membre ale unei clase. In acest scop, se pot utiliza specificatorii de control al accesului: public, private si protected. Membrii privati(date si functii) sunt accesibili numai functiilor membre si prietene(friend) ale clasei. Un membru public poate fi accesat de orice functie din domeniul de declaratie al clasei. Accesul la membrii protejati este similar celui la membrii privati, dar accesul se poate extinde la functiile membre si prietene ale claselor derivate din clasa respectiva. De exemplu, în cazul clasei Stiva, sunt ascunse utilizatorului toate detaliile de implementare (membrii de date):

class Stiva{protected:

int nmax; // numarul maxim de elementeint *tab; // tabloul in care se vor memora elementele int varf; // indexul elementului din varf

public:Stiva(int); // constructorStiva(); // constructor implicit~Stiva(); // destructorBOOL Push(int);BOOL Pop(int &);BOOL Top(int &);BOOL not_vida();BOOL not_plina();

};

Definirea functiilor membre se poate face fie ca functii inline, la declararea lor in cadrul clasei, fie în exteriorul clasei. Pentru definitiile functiilor membre aflate în afara declaratiei clasei, este necesara specificarea numelui clasei urmat de operatorul de rezolutie înaintea numelui functiei. De exemplu, definirea operatiei de adaugare în stiva se face in felul urmator: BOOL Stiva::Push(int ElementNou){if (not_plina()) {

tab[++varf]=ElementNou;return TRUE;}

else return FALSE;}

Page 5: Notiuni de baza ale programarii orientate pe obiecte în limbajul C

Functiile membru pot avea acces la orice membru al clasei respective. Accesul la membrii de date ai instantei curente se face direct, prin numele lor.

Pentru apelul functiilor membre publice sau pentru accesul la datele publice ale unui obiect, din functii care nu sunt membre, se folosesc operatorii de selectie (.) si (->), ca în cazul structurilor si uniunilor din C.

Exemplu:

Stiva s1, *ps; ... ps=new Stiva(20); ... s1.push(5);

ps->push(8);

3.2. Autoreferinta. Cuvântul cheie this

Pentru a defini functiile membre sunt necesare referiri la datele membre ale clasei fara a specifica un obiect anume. La apelare, functia este informata asupra identitatii obiectului asupra caruia va actiona prin transferul unui parametru implicit care reprezinta adresa obiectului. De exemplu, în cazul apelului: s1.Push(3), funtia Push() primeste si adresa stivei s1 în afara de valoarea 3.

Exista situatii în care este necesar ca adresa obiectului sa fie utilizata în definitia functiei. Ne putem referi la acest pointer prin cuvântul cheie this, declarat implicit în orice functie si initializat sa indice inspre obiectul pentru care este invocata functia membru. Principala utilizare a lui this este la scrierea functiilor care manipuleza direct pointeri.

3.3. Membrii statici ai unei clase

Fiecare obiect de tip clasa are propriile lui copii ale tuturor membrilor acelei clase. Este posibila definirea de membrii care sa fie folositi în comun de catre toate obiectele clasei. Datele statice exista intr-o singura copie, comuna tuturor obiectelor. Crearea, initializarea si accesul la aceste date statice sunt total independente de obiectele clasei. Functiile membre statice efectueaza operatii care nu sunt asociate obiectelor individuale, ci intregii clase. Din acest motiv, la apelarea lor nu este obligatorie indicarea unui obiect. Un membru static poate fi referit si indicând numele clasei si folosind operatorul de rezolutie de domeniu.

3.4. Functii si clase friend

Functiile prietene (friend) pot folosi membrii privati ai unei clase, desi ele însele nu sunt membri. Functiile prietene au fost introduse pentru cazurile în care o functie coopereaza strâns cu o clasa, dar nu face parte din acea clasa. O functie prietena este declarata asemanator cu o functie membru, avand prototipul în interiorul clasei, precedat de cuvântul cheie friend. Exista si posibilitatea ca una sau mai multe functii membre ale unei clase sa fie functii prietene ale altei

Page 6: Notiuni de baza ale programarii orientate pe obiecte în limbajul C

clase. Se poate declara si o clasa Y prietena a unei clase X, în acest caz toate functiile clasei Y sunt prietene ale clasei X si au acces nelimitat la membrii privati ai clasei X.

Exemplu:

In clasa Stiva definita anterior, varful este o variabila protejata, care poate fi accesata numai din cadrul functiilor membre ale clasei stiva. Daca se defineste clasa Test, prietena a clasei Stiva, atunci orice functie membra a clasei Test are acces direct nelimitat la variabila varf a unei stive:

class Stiva { ..... friend class Test; };

class Test { ..... void ofct(); };

void Test::ofct() { Stiva s; .... s.varf=5; // devine o operatie permisa, pentru ca Test e clasa prietena ! .... }

3.5. Constructori si destructori

In cazul variabilelor obisnuite, compilatorul asigura alocarea spatiului de memorie si eventual initializarea explicita cu valori initiale în declaratie. Pentru variabilele dinamice, compilatorul C nu dispune de nici o metoda de initializare si nici operatorul new nu rezolva toate situatiile. In acest caz, ramîne în grija programatorului atribuirea de valori adecvate datelor inainte de utilizare. Aceasta abordare este nesatisfacatoare în multe situatii în cazul obiectelor.

Pentru crearea, initializarea, copierea si respectiv distrugerea obiectelor, în C++ se folosesc functii speciale, numite constructori si destructori. Constructorul se apeleaza automat la crearea fiecarui obiect al clasei, static, automatic sau dinamic(cu operatorul new), inclusiv pentru obiecte temporare.

Destructorul este apelat automat la eliminarea unui obiect, la incheierea timpului sau de viata, sau poate fi solicitat prin program, cu operatorul delete.

Constructorii si destructorii se declara si se definesc similar cu celelalte functii membre, dar se disting de acestea printr-o serie de caracteristici specifice:

numele functiilor constructor sau destructor coincide cu numele clasei careia îi apartin; destructorii au numele precedat de caracterul (~).

în declaratie si definitie nu se specifica nici un tip de rezultat, nici macar void

Page 7: Notiuni de baza ale programarii orientate pe obiecte în limbajul C

constructorii pot avea parametrii, inclusiv parametrii impliciti, si pot fi supradefiniti; destructorii nu au aceste proprietati

nu se pot utiliza pointeri catre constructori sau destructori daca o clasa nu dispune de constructori si destructori definiti, compilatorul va genera

automat un constructor implicit, respectiv un destructor, functii publice

Alta categorie de constructor este constructorul de copiere. Necesitatea de copiere a obiectelor intervine cînd un obiect este transferat ca parametru sau rezultat al unei functii, sau la crearea unui obiect temporar. Solutia oferita de C++ consta în utilizarea unui constructor special, numit constructor de copiere. In absenta unei definitii explicite în cadrul clasei, compilatorul genereaza automat un constructor de copiere care initializeaza datele noului obiect cu valorile corespunzatoare din obiectul specificat, prin copiere membru cu membru. Aceasta nu este o solutie buna în cazul în care clasa contine membrii variabile dinamice si cand e necesara scrierea unui constructor de copiere special.

Declaratia constructorului de copiere pentru o clasa trebuie sa specifice un parametru unic de tipul referinta de obiecte a acelei clase.

De exemplu, definitia unui constructor de copiere adecvat pentru clasa Stiva ar fi:

Stiva::Stiva(Stiva&s) { nmax=s.nmax; tab=new int[nmax]; varf=s.varf; for (int i=0; i<=varf; i++) tab[i]=s.tab[i]; }

3.6. Supraîncarcarea operatorilor

Functiile operator constituie un tip special de functii , care s-ar putea utiliza pentru redefinirea operatorilor de baza care apar în C. Un tip de clasa se poate defini împreuna cu un set de operatori asociati, obtinuti prin supraincarcarea operatorilor existenti. In acest fel, se efectueaza operatii specifice cu noul tip la fel de simplu ca in cazul tipurilor standard. Procedeul consta in definirea unei functii cu numele

operator < simbol >

De exemplu, se presupune definirea unei clase NrComplex, si se doreste supradefinirea operatorului de adunare, pentru adunarea a doua numere complexe. Functia operator+() trebue sa primeasca doi parametri de tipul NrComplex si sa returneze un rezultat de tip NrComplex, avand acces direct la datele membre ale clasei.

Exista 2 posibilitati de realizare: ca functie prietena a clasei NrComplex sau ca functie membra a clasei NrComplex.

Exemplu - supradefinirea operatorului + ca functie friend:

Page 8: Notiuni de baza ale programarii orientate pe obiecte în limbajul C

class NrComplex { float real, imaginar; public:

NrComplex(float r=0, float i=0);friend NrComplex operator+(NrComplex, NrComplex); // operatorul suma

definit ca // functie prietena a clasei };

NrComplex operator+ (NrComplex c1, NrComplex c2) { NrComplex c; c.real=c1.real+c2.real; c.imaginar=c1.imaginar+c2.imaginar; return c; }

void main() { NrComplex c1(1,2), c2(5,5), s; s=c1+c2; }

Expresia c1+c2 este interpretata de compilator ca un apel al functiei operator+(c1,c2)

Exemplu - supradefinirea operatorului + ca functie membra:

class NrComplex { float real, imaginar; public:

NrComplex(float r=0, float i=0);NrComplex operator+(NrComplex); // operatorul suma definit ca

// functie membra a clasei };

NrComplex NrComplex::operator+ (NrComplex c2) { NrComplex c; c.real=real+c2.real; c.imaginar=imaginar+c2.imaginar; return c; }

void main() { NrComplex c1(1,2), c2(5,5), s; s=c1+c2; }

O functie membra primeste in mod implicit adresa obiectului pentru care este apelata. Functia operator+() va avea prototipul NrComplex operator+(NrComplex);

Expresia c1+c2 este interpretata de compilator ca un apel al functiei: c1.operator+(c2)

In acest caz, al supradefinirii operatorului + ca functie membru, datorita transferului implicit al primului operand, apare o asimetrie in definitia functiei operator+().

Page 9: Notiuni de baza ale programarii orientate pe obiecte în limbajul C

Mai apare si o alta restrictie impusa de functiile operator membre ale clasei: primul operand este intotdeauna de tipul clasa respectiv, deci solutia nu este adecvata daca primul operand trebuie sa fie de un alt tip.

Alte precizari privind supradefinirea operatorilor:

Se pot supradefini in C++ numai operatori existenti, deci simbolul asociat functiei operator trebuie sa fie deja definit ca operator pentru tipurile standard (nu e permisa introducerea unor simboluri noi de operatori). De asemenea, nu se pot modifica pluralitatea (un operator unar nu poate fi supradefinit ca unul binar sau invers), precedenta, asociativitatea.

Functia operator trebuie sa aiba cel putin un parametru (implicit sau explicit) de tipul clasa caruia ii este asociat operatorul respectiv. Aceasta restrictie implica faptul ca supradefinirea operatorilor e posibila numai pentru tipurile clasa definite in program, pentru tipurile standard operatorii isi pastreaza definitia.

Functiile operator pot fi implementate ca si functii membre a clasei sau ca si functii prietene a clasei. In particular, pentru operatorii: = [] () -> functia operator trebuie sa fie membra a clasei.

In general, pentru un operator binar op, expresia x op y este interpretata:

x.operator op(y); // functie membru

sau operator op(x,y); // functie friend

In general, pentru un operator unar op, expresiile x op sau op x se interpreteaza:

x.operator op(); // functie membru

sau operator op(x); // functie friend

2.Clase

�n acest capitol vom introduce si vom exemplifica notiunea de clasa. �n pasi succesivi vom construi clasa Person ce va putea fi folosita pentru crearea unei baze de date. Definitia acestei clase este:

class Person

{

public: // functiile interfata

void setname( char const *n);

void setaddress (char const *a);

void setphone (char const *p);

Page 10: Notiuni de baza ale programarii orientate pe obiecte în limbajul C

char const *getname (void);

char const *getaddress (void);

char const *getphone (void);

private: //c�mpurile de date

char *name;

char *address;

char *phone;

};

Datele s�nt private, deci pot fi accesate doar de functiile din clasa Person. Aceste functii fie seteaza unui c�mp o anumita valoare (set..()), fie inspecteaza datele (get...()).

3.1. Constructori si destructori

O clasa �n C++ poate contine doua functii speciale care s�nt implicate �n munca interna a clase respective. Ele s�nt numite constructor si destructor.

Constructorul

Functia constructor are prin definitie acelasi nume ca si clasa corespunzatoare. Ea nu are o valoare returnata specifica, nici macar void. De exemplu, pentru clasa Person, constructorul este Person::Person (). Sistemul C++ asigura apelul unui constructor pentru o anumita clasa atunci c�nd este creat un obiect al acelei clase . Este desigur posibil sa definesti o clasa fara nici un constructor explicit: �n acest caz sistemul fie nu face nici un apel, fie apeleaza un constructor fals (ce nu face nimic) �n momentul crearii unui obiect. Daca un obiect este o variabila locala ne-statica �ntr-o functie, constructorul este apelat �n momentul executiei functiei. Daca obiectul este o variabila statica sau globala, constructorul este apelat �naintea �nceperii executiei programului (chiar �nainte de main()).

#include <stdio.h>

// o clasa Test cu o functie constructor

class Test

{ public: Test (); }; //functia constructor declarata public

Test::Test ()

{ puts ("constructorul clase Test este apelat");}

// programul test

Test g; // obiect global

Page 11: Notiuni de baza ale programarii orientate pe obiecte în limbajul C

void func ()

{ Test l; // obiect local �n functia func()

puts(" Aici e apelat func");

}

int main()

{ Test x; // obiect local �n functia main()

puts ("functia main()");

func();

return (0);

}

Aici am definit o clasa ce contine doar o functie: constructorul. Constructorul �nsusi nu are dec�t o singura actiune: listeaza un mesaj. Programul contine trei obiecte din clasa Test: unul global, unul local �n main() si unul local �n func(). Se poate observa: numele constructorului (acelasi ca al clasei), lipsa valorii returnate si lipsa argumentelor (asa numitul constructor implicit - e posibil �nsa sa fie definiti si constructoru cu argumente). Apelul constructorului �n executia programului are loc �n ordinea: 1) la crearea obiectului global g; 2) la crearea obiectului local x din functia main(); 3) la crearea obiectului local l din functia func().

Pe ecran se va obtine deci �n final:

constructorul clasei Test este apelat

constructorul clasei Test este apelat

functia main()

constructorul clasei Test este apelat

Aici este apelat func

Destructorul

O a doua functie speciala este destructorul (opusul constructorului �n sensul ca ea este apelata �n momentul �n care un obiect �nceteaza sa mai existe). Pentru obiectele ne-statice locale, destructorul este chemat atunci c�nd functia �n care este definit obiectul �ntoarce valoarea return; pentru cele statice (globale), destructorul este apelat �naintea terminarii programului. Chiar daca programul este �ntrerupt cu un exit(), vor fi apelati destructorii pentru obiectele existente. C�nd se defineste un desrtuctor, se respecta urmatoarele reguli:

. Destructorul are acelasi nume ca si clasa, dar precedat de tilda

. Destructorul nu are nici un argument sau o valoare de returnat.

Page 12: Notiuni de baza ale programarii orientate pe obiecte în limbajul C

Exemplu:

class Test

{

public: Test (); //constructorul

~Test (); // destructorul

...

};

O prima aplicatie

Una din aplicatiile constructorului si destructorului o reprezinta managementul alocarii memoriei. Exemplificam cu clasa Person. Ea contine trei pointer private, toti de tip char *. Acesti membrii s�nt manipulati prin functiile interfata. C�nd un nume, adresa sau telefon s�nt definite, va fi alocata memorie pentru memorarea acestor date. Evidentiem operatiile necesare:

. Constructorul clasei se asigura ca toate datele s�nt initial pointeri NULL

. Destructorul va elibera toata memoria alocata

. Definirea unui nume (adresa, telefon), prin intermediul functiilor set..() va consta �n doi pasi. �n primul, memoria alocata anterior va fi eliberata; �n al doilea sirul (argumentul functiei set..()) este copiat �n memorie

. Inspectarea unei date prin intermediul functiilor get..() va returna un pointer: fie pointerul NULL (data nu e definita), fie un pointer catre zona de memorie alocata datei.

Functiile set..() s�nt prezentate �n continuare. Copierea sirurilor se face printr-o functie imaginara xtrdup(), care copie sirul sau termina executia programului daca memoria nu este suficienta.

// functiile interfata set..()

void Person::setname (char const *n)

{ free (name);

name = xstrdup (n); }

void Person::setaddress (char const *n)

{ free (address);

address = xstrdup (n); }

void Person::setphone (char const *n)

{ free (phone);

Page 13: Notiuni de baza ale programarii orientate pe obiecte în limbajul C

name = xstrdup (n); }

Sa observam ca desi instructiunea free(..) este executata neconditionat, nu apar actiuni incorecte: daca datele au fost deja definite, la o noua definire memoria alocata anterior va fi eliberata; daca datele n-au fost �nca definite, pointerii s�nt NULL, iar free (0) nu executa nimic. Mai trebuie precizat ca acest cod este mai familiar programatorilor �n C (ce utilizeaza functia free()), pentru C++ exist�nd o instructiune mai potrivita, si anume delete.

// functiile interfata get..()

char const *Person :: getname ()

{ return (name); }

char const *Person :: getaddress ()

{ return (address); }

char const *Person :: getphone ()

{ return (phone); }

�n final, constructorul, destructorul si definitia clasei Person:

class Person

{ public: Person (); //constructorul

~Person (); //destructorul

// functiile ce seteaza datele

void setname (char const *n);

void setaddress (char const *a);

void phone (char const *p);

//functiile ce inspecteaza datele

char const *getname (void);

char const *getaddress (void);

char const *getphone (void);

private: char * name;

char *address;

char *phone;

Page 14: Notiuni de baza ale programarii orientate pe obiecte în limbajul C

};

//constructorul

Person::Person ()

{ name = address = phone = 0; }

//destructorul

Person :: ~Person ()

{ free (name); free (address); free (phone); }

Exemplificam �n continuare cu un posibil program pentru folosirea acestei clase. Functia printperson () va fi utilizata pentru listarea datelor unui obiect de tip Person. Argumentul functiei va fi de tip referinta la un obiect Person. Faptul ca functia nu modifica argumentul (deci obiectul) este clar din prezenta cuv�ntului const. Mai remarcam ca destructorul nu este apelat �n mod explicit.

void printperson (Person const &p)

{ printf ("Numele: %s\n", "Adresa: %s\n", "Telefon: %s\n", p.getname(), p.getaddress(), p.getphone()); }

int main ()

{ Person p;

p.setname("Liviu T.");

p.setaddress(" Iasi, Copou");

p.setphone ("123213");

printperson (p)

return (0);

}

Acest cod este doar un exemplu didactic; majoritatea compilatoarelor C++ n-ar putea genera cod executabil. Motivul este ca functia printperson () primeste un argument const, dar apelurile pentru acest argument (get..()) l-ar putea modifica, deci 'constanta' lui p n-ar putea fi garantata. Solutia va fi declararea acestor functii ca nemodific�nd obiectul (explicatii, putin mai t�rziu). S-ar mai putea modifica codul lui printperson () pentru o verificare initiala a existentei unor date concrete (nume, adresa, telefon) si listarea numai acelora (cum ?).

Constructori cu argumente

�n C++ este permisa descrierea de constructori cu argumente. Pentru clasa Person un asemenea constructor poate fi definit ca utiliz�nd trei argumente pentru nume, adresa, telefon:

Person::Person (char const *n, char const *a, char const *p)

Page 15: Notiuni de baza ale programarii orientate pe obiecte în limbajul C

{ name = xstrdup (n); address = xstrdup (a); phone = xstrdup (p); }

Constructorul trebuie inclus �n definitia clasei. O declaratie �ntr-un fisier header ar putea arata astfel:

class Person

{ public: Person::Person (char const *n, char const *a, char const *p);

.. ... };

Deoarece �n C++ este permisa acoperirea functiilor, o asemenea declaratie poate coexista cu un constructor fara argumente. Utilizarea constructorului cu argumente este ilustrata �n continuare:

int main ()

{ Person a ("Karel", "Brasov", "213342"), b;

. . . }

Ordinea apelului constructorului

Posibilitatea de a defini constructori cu argumente ne permite sa monitorizam exact �n ce moment al executiei programului este creat sau distrus un obiect.

class Test

{ public: Test () ; //constructor fara argumente

Test (char const *name); // constructor cu argumente

~Test ();

private: char *n //datele

};

Test::Test ()

{ n = strdup ("Fara nume");

printf ("Creat obiect Test fara nume\n"); }

Test::Test (chat const *name)

{ n = strdup (name);

printf ("Creat obiect Test cu numele %s\n",n); }

Test::~Test ()

Page 16: Notiuni de baza ale programarii orientate pe obiecte în limbajul C

{ printf ("Distrus obiect Test cu numele %s\n", n);

free (n); }

Test globaltest ("global");

void func ()

{ Test functest ("func"); }

int main ()

{

Test maintest ("main");

func ();

return (0);

}

Listigul dupa rularea programului va fi:

Creat obiect Test cu numele global

Creat obiect Test cu numele main

Creat obiect Test cu numele func

Distrus obiect Test cu numele func

Distrus obiect Test cu numele main

Distrus obiect Test cu numele global

Functii membru constante si obiecte constante

Cuv�ntul cheie const este deseori folosit �n declaratiile functiilor membru, indic�nd faptul ca aceste functii nu pot altera c�mpurile de date, ci numai inspecta. Exemplu:

class Person

{

public:

....

char const *getname (void) const;

Page 17: Notiuni de baza ale programarii orientate pe obiecte în limbajul C

char const *getaddress (void) const;

char const *getphone (void) const;

private: .. ...

};

Dupa cum se poate observa, cuv�ntul const apare dupa lista argumentelor. Regula cuv�ntului const se aplica si aici: tot ce apare dupa cuv�ntul cheie nu poate fi alterat sau nu poate altera datele. Aceeasi specificatie trebuie sa apara si �n definitia functiilor membru:

char const *Person::getname () const

{ return (name); }

O functie membru declarata const nu poate altera nici un c�mp al clasei sale. Scopul functiilor const este acela de a permite crearea de obiecte const. Pentru asemenea obiecte pot fi apelate numai functiile ce nu le modifica, deci cele declarate const (cu singura exceptie: constructorul si destructorul, apelati automat). Sa cream un obiect const din clasa Person:

Person const me ("Profesor", "Iasi", "146141"); //se face initializarea de catre constructor

Instructiunea me.setname ("Student") este deci ilegala.

3.3 Operatorii new si delete

Limbajul C++ defineste doi operatori ce s�nt specifici pentru alocarea si dealocarea de memorie. Ei s�nt new si delete. Fie, de exemplu, o variabila pointer la un int folosita pentru a pointa catre memoria alocata de new, memorie eliberata mai t�rziu de operatorul delete:

int *ip;

ip = new int;

. . .

delete ip;

Fiind operatori, new si delete nu necesita paranteze (ca pentru functiile malloc() si free()).

Alocarea si dealocarea tablourilor

C�nd operatorul new este folosit pentru alocarea memoriei pentru un tablou, dimensiunea variabilei trebuie plasata, �ntre paranteze patrate, dupa tip:

int *intarray;

intarray = new int [20] ; //se aloca 20 de �ntregi

Page 18: Notiuni de baza ale programarii orientate pe obiecte în limbajul C

Regula sintactica pentru operatorul new este ca el trebuie urmat de un tip, urmat optional de o dimensiune; tiupul si numarul s�nt folosite de compilator pentru calcularea dimensiunii memoriei necesare alocarii. Un tablou este dealocat de operatorul delete:

delete [] intarray;

�n aceasta instructiune operatorii de tablou [] indica faptul ca este dealocat un tablou. Regula este urmatoare: de c�te ori new este urmat de [], delete trebuie urmat tot de [].