Limbajul C++ si oop abc-doar


pe destructorul implict.Constructorul si destructorul implicit au functii



Yüklə 1,36 Mb.
səhifə4/18
tarix12.09.2018
ölçüsü1,36 Mb.
#81721
1   2   3   4   5   6   7   8   9   ...   18

pe destructorul implict.Constructorul si destructorul implicit au functii

nule si sunt creati pentru a respecta standardul compilatorului.


-19-

Daca realizati clase si obiecte valoroase,pe care doriti sa le oferiti

si altor programatori,verificati cu atentie memoria,inainte,dupa crearea

obiectului,cat si dupa eliberarea sa.Un obiect corect programat trebuie

sa nu lase "nici o urma" in memorie.Este bine sa realizati un mic pro-

gram in care sa afisati permanent continutul stream-urilor cin,cout si

cerr.Apoi apelati unitatea realizata,cosntruiti obiectele si observati

aspectul stream-urilor utilizate.Daca obiectul creaza si alte stream-uri,

verificati cu atentie fiecare stream.

Exista o literatura intreaga referitoare la constructori si destruc-

tori.Regula de aur este urmatoarea: "solutia cea mai buna este intotdea-

una cea mai simpla".

Pentru ca o anumita functie,sau chiar o intreaga clasa sa poata avea

acces la membri privati ai unei alte clase,se poate utiliza cuvantul cheie

"friend".Functiile declarate cu cuvantul cheie "friend" vor fi tratate ca

si cand ar fi fost declarate "extern".Functiile si clasele cu vizibilitate

globala pot fi declarate ca "friend" inainte de a fi definite,dar cele

care sunt membri ai altor clase nu pot fi declarate "friend" inainte de a

se declara prototipul clasei de baza (cea in care va fi membru).

EXEMPLU (vezi si cplus11.cpp )

#include

#include

class obiect1

{ friend class arie;

private: int raza;

public: obiect1(){ raza=10; };

}

class arie

{ public: int b;

arie(){ b=5; };

modifica(obiect1 x);

};

arie::modifica(obiect1 x)

{ b=x.raza; return b; };

int main()

{

obiect1 data1;

arie data2;

data2.b=data2.modifica(data1);

cout << "membrul apelat are valoarea: \n";

cout << data2.b;

getch();

}

Se observa ca variabila b a primit valoarea variabilei raza din obiectul

pentru care a fost declarata clasa "friend" chiar daca raza a fost decla-

rata cu specificatorul "private".

(Exemplu: cout << data1.b determina o eroare de compilare ).

Daca o clasa este "friend",toate functiile sale vor fi "friend".

Nu are rost sa utilizati acest mecanism decat in situatiile in care

este extrem de util,sau imperios necesar.De exemplu: daca realizati o

unitate de obiecte si doriti sa va rezervati accesul la una dintre varia-

bilele private a clasei respective (pentru depanare rapida).


-20-

Se observa ca clasa "friend" nu apeleaza direct membri privati ai clasei

prietene ci utilizeaza o functie,care are ca parametru un obiect din

tipul de data al clasei pentru care a fost declarata "friend".Acest me-

canism pare destul de complicat,dar poate fi extrem de util in anumite

situatii.Astfel,clasele declarate "extern",preluate din biblioteci DLL si

cele preluate din unitati sunt definite in alte module si nu pot fi mo-

dificate pentru a simplifica operatiile de depanare(intr-o clasa declarata

local se schimba pur si simplu specificatorul din "private" in "public" si

apoi se deruleaza programul secvential pana cand se identifica eroarea).

Pentru a putea avea acces la membri privati ai unei clase declarata extern

puteti include in definita acesteia si o clasa sau o functie declarata

"friend",care va fi utilizata exclusiv pentru depanare (De exemplu se

poate declara o clasa "friend class Debug").Daca utilizati aceasta clasa

"friend" pentru toate clasele declarate intr-o biblioteca DLL,va fi mult

mai usor sa depanati o aplicatie,fara sa fie necesar sa modificati si

fila care contine biblioteca DLL (uneori,in procesul de depanare doriti

sa urmariti succesiv valoarea unei anumite variabile private,in urma unor

operatii succesive la care participa in cursul aplicatiei).

Membrii unei clase pot fi si proceduri.In limbajul C++ o procedura este

egala cu o functie care nu returneaza nimic.Prin urmare se declara si se

utilizeaza ca si cand ar fi o functie de tip "void".

EXEMPLU: ( vezi si cplus12.cpp )

#include

#include

class obiect1

{ public : int a; float b,c;

obiect1() { a=3; b=3.141592; c=0; };

void fun1() { c=a*b; };

~obiect1() { a=0; b=0; c=0; };

};

int main()

{

obiect1 numar;

numar.fun1();

cout << "membrul apelat are valoarea: \n";

cout << numar.c;

numar.~obiect1();

getch();

}

In rest,o procedura se comporta exact la fel ca si o procedura din Pascal.

Se observa ca procedura se apeleaza direct,fara sa fie necesara o ecuatie.

Puteti utiliza cu succes o procedura,ori de cate ori doriti sa efectuati

un numar de operatii,fara sa fie necesara returnarea unei valori imediate.

Un alt gen de functie care poate fi membru al unei clase este functia

"template".Template este un mecanism utilizat pentru a genera functii si

clase care pot accepta ca parametri mai multe tipuri de date.Se spune

despre aceste functii si clase ca sunt "tipuri parametrizate" sau ca

opereaza cu parametrii de tip "generic".Prin utilizarea acestui mecanism

se pot declara functii care opereaza cu parametrii din doua sau mai multe

tipuri de data,alternativ.Aceeasi functie va putea fi utilizata si pentru

date de tip int si pentru date de tip float,double sau char.


-21-

EXEMPLU: (vezi si cplus13.cpp)

#include

#include

template

type1 min(type1 a,type2 b)

{ type1 r,b_converted;

r=a;

b_converted = (type1) b;

if (b_converted < a) r=b_converted;

return r;

};

int main()

{

int c;

double d;

c=33;

d=7.41;

cout << "numarul mai mic este: " << min(c,d);

cout << "\n numarul mai mic este: " << min(d,c);

cout << "\n numarul mai mic este: " << min(c,'A');

cout << "\n numarul mai mic este: " << min(d,'X');

getch();

}

Se observa ca functia face conversia dintre tipul1 si tipul2 de data.Ca

rezultat,cele doua tipuri de data pot fi inversate intre ele,deoarece

functia va face automat conversia astfel incat sa poata executa operatia.

Mai mult decat atat,functia accepta orice alt tip de data care poate fi

convertit la valoarea necesara pentru comparatie.

Se poate spune despre aceste functii ca isi schimba reciproc parametrii.

Atunci cand o functie "template" este apelata pentru prima data cu un

anumit tip de data,compilatorul creaza o instanta,adica o versiune a

functiei specializata pentru tipul respectiv de data.Aceasta instanta

poate fi apoi apelata ori de cate ori este necesar pentru tipul respectiv

de data.Instanta respectiva va fi utilizata chiar daca se vor utiliza

date din module diferite (dar din acelasi tip de data cu cel pentru care

s-a realizat instanta functiei).Acest mecanism se poate utiliza pentru

orice tipuri de date care accepta conversia reciproca.

Mai mult decat atat,functia "template" poate fi specializata astfel

incat sa prezinte un comportament "special" doar pentru un anumit tip de

parametru si sa prezinte un comportament "normal" pentru restul parametri-

lor.

In mod similar,se pot realiza clase "templates" sau familii de clase

care opereaza asupra unui anumit tip de data.

EXEMPLU: template class TempClass

{ public: TempClass (void);

~TempClass(void);

int MemberSet(T a,int b);

private: T Tarray[i];

int arraysize;

}

In exemplul de mai sus,clasa template TempClass utilizeaza doi parametri:


-22-

un parametru din tipul de data T,si un parametru din tipul int.Paramterul

T poate accepta orice tipuri de data,inclusiv structuri si clase iar

pentru parametrul de tip int se va putea utiliza o constanta de tip int.

De exemplu,se va putea utiliza i pentru a specifica dimensiunea unei

arii de date,care va fi utilizata pentru parametrul T.

Un alt tip de membru al unei clase poate fi o alta clasa.In acest caz

se spune ca este o clasa "intricata" ("nested class") in alta clasa.Clasa

care este membru al unei alte clase,are vizibilitate locala,doar in

interiorul clasei in care a fost declarata.Declararea unei astfel de

clase nu introduce in clasa gazda decat un tip de data,fara materializare.

La declararea unui obiect din clasa gazda nu se creaza automat si un

obiect din clasa intricata.Daca este necesar,obiectul din tipul clasei

intricate trebuie declarat individual.

EXEMPLU: (vezi si cplus14.cpp)

#include

#include

class obiect1

{ public: int a; float b,c;

obiect1(){ a=9; b=13.2345; c=0; };

float fun1() { c=a*b; return c; };

~obiect1() { a=0; b=0; c=0; }

class obiect2

{ public: int d; };

};

int main()

{

obiect1 numar;

obiect1::obiect2 data1;

data1.d=7;

cout << "membrul apelat are valoarea: \n";

cout << data1.d;

numar.~obiect1();

getch();

}

Se observa ca clasa obiect2 este inclusa in clasa obiect1.Pentru a putea

opera cu membrul d din clasa obiect2 este necesar sa se declare un obiect

din clasa obiect2,utilizand operatorul de scope (::) prin care se specifi-

ca faptul ca obiect2 este membru al clasei obiect1.

In mod obisnuit,clasele incluse in alte clase se utilizeaza doar pentru

operatii in cadrul clasei "gazda" si nu au vizibilitate in afara acestei

clase.Exemplul a fost prezentat pentru a intelege mecanismul de apelare

a membrilor,atunci cand este necesar (de exemplu in timpul unei operatii

de depanare a unei aplicatii).

Restul tipurilor de date care pot fi utilizate ca membri (uniuni,struc-

turi,enumarari,campuri de biti,etc.) nu prezinta nici un fel de particu-

laritati si se utiulizeaza corespunzator cu tipul respectiv de data.

Pentru accesul rapid la o clasa,sau la membrul unei clase se pot uti-

liza pointerii.Un pointer specific pentru membrii claselor este pointerul

"this",care pointeaza obiectul care contine functia apelata (pointeaza o

instanta a clasei respective,adica un obiect).Pointerul "this" returneaza

adresa obiectului care contine functia apelata.


-23-

CLASE DERIVATE
Asadar,o clasa este un tip de data,iar un obiect este o instanta a unei

clase,adica o variabila din acel tip.O clasa poate fi "sablon" nu numai

pentru un obiect din clasa respectiva,dar si pentru declararea si defi-

nirea unei alte clase.Procesul prin care o clasa mosteneste caractere de

la o alta clasa poarta numele de derivare.Clasa utilizata ca sablon poarta

numele de clasa de baza,iar clasa obtinuta se numeste clasa derivata.

Pentru a specifica faptul ca o clasa noua este derivata dintr-o alta

clasa se utilizeaza o expresie de genul:

Numele clasei derivate : specificator Numele clasei de baza {definitia};

Daca mostenirea va fi virtuala se poate adauga si specificatorul "virtual"

EXEMPLE: class A: virtual public class B { .... expresii };

class C: private virtual class D { ... expresii };

class E: protected class F {};

Clasa mostenitoare va avea acces la toti membrii publici si protected ai

clasei ancestoare.Specificatorul protected,se utilizeaza in interiorul

unei clase,tocmai pentru a limita accesul la datele respective numai

pentru membrii clasei si pentru clasele derivate.Deci,protected este

un specificator intermediar intre public si private: permite accesul si

din exteriorul clasei,dar numai pentru clasele drivate (mostenitoare).

Membrii privati ai clasei ancestrale nu pot fi accesati din clasa derivata

(nu au vizibilitate in afara clasei).

EXEMPLU: (vezi si cplus15.cpp)

#include

#include

class obiect1

{ public: int a; float b,c;

obiect1(){ a=5; b=3.73; c=2.72; d=12;};

~obiect1(){ a=0; b=0; c=0; d=0; };

protected: int d;

}

class obiect2: public obiect1

{ public: int x;

obiect2() { x=d; };

};

int main()

{

obiect2 numar;

cout << "membrul apelat are valoarea: \n";

cout << numar.x;

getch();

}

Se observa ca obiect2,care este derivat din obiect1,utilizeaza in cons-

tructor variabila d,mostenita de la obiect1(desi d este un membru de tip

"protected" si nu are vizibilitate in afara clasei).Obiect2 mosteneste si

toti ceilalti membri din obiect1.Puteti incerca cu : cout << numar.a,

sau cout << numar.b etc.

Acest mecanism sta la baza familiilor de clase,in care exista un cap

de serie si un numar oarecare de clase derivate succesiv,in care se

adauga progresiv functii sau membrii de date cu actiuni specifice.


-24-

In aceste familii de clase (cea mai cunoscuta este MFC=Microsoft Founda-

tion Class Library),exista o structura ierarhica de tip arbore genealogic

in care o clasa de baza este derivata de una...sau mai multe ori,pentru a

obtine grupuri de clase descendente specializate.

EXEMPLU: (vezi si cplus16.cpp)

#include

#include

class obiect1

{ public: int a; float b,c;

obiect1(){ a=5; b=3.73; c=2.72; d=12; };

~obiect1(){ a=0;b=0;c=0; };

protected: int d;

};

class obiect2: protected obiect1

{ public: int x;

obiect2() { x=d; };

};

class obiect3: private obiect2

{ public: int y;

obiect3() { y=x+d+a; }

};

int main()

{

obiect2 numar;

obiect3 numar2;

cout << "membrul apelat are valoarea: \n";

cout << numar.x;

cout << "\n valoarea lui y din obiect3 este: \n";

cout << numar2.y;

getch();

}

Se observa ca obiect2 este derivat din obiect1,iar obiect3 este derivat

din obiect2.Ca rezultat,obiect3 mosteneste toti membrii obiectelor ances-

toare,la care se adauga si membrul y specific numai pentru obiect3.

Fiind clasa derivata,poate avea acces la membri claselor "sablon" si

utilizeaza aceasta proprietate in constructorul sau,care formeaza y cu

ajutorul membrilor mosteniti.Dar,absolut toti membrii mosteniti vor avea

in obiect3 caracterul "private" deoarece,pentru procesul de mostenire s-a

utilizat specificatorul "protected" in primul caz si respectiv "private"

in cel de al doilea caz.Regula pentru specificatorul de mostenire este

urmatoarea: 1.daca specificatorul este "public",atunci membrii public si

protected din clasa ancestoare raman tot public si protected si in clasa

derivata 2.daca specificatorul este "protected" atunci membrii de tip

public si protected vor fi mosteniti cu specificatorul "protected",iar

3. daca specificatorul utilizat in ecuatie este "private" atunci toti

membri mosteniti vor avea specificatorul "private".

In exemplul de mai sus,la cea de a doua mostenire s-a utilizat speci-

ficatorul "private",astfel incat toti membrii mosteniti vor fi privati

(nu au vizibilitate in afara clasei).De exemplu: cout << numar2.x retur-

neaza o eroare de compliare (chiar daca x a fost definit ca public in

obiect2 si poate fi apelat prin cout << numar.x ).


-25-

Intelegerea mecanismului de mostenire este extrem de importanta nu

doar pentru clasele definite de catre utilizator,ci si pentru a putea

opera cu bibliotecile mari de clase,cum este de exemplu OWL (Object

Windows Library).C++ poate realiza orice interfata de tip Windows cu

ajutorul obiectelor definite in biblioteca OWL,dar pentru a apela toate

functiile,datele sau metodele acestor obiecte,trebuie sa puteti urmari

genealogic filiatia fiecarui obiect.

Clasele definite nu sunt in mod obligatoriu statice.Pentru a asigura

o cat mai mare diversitate si libertate de exprimare,clasele de obiecte

accepta si functii virtuale,adica functii care sunt doar declarate,dar

urmeaza sa fie definite doar in momentul utilizarii.

EXEMPLU: (vezi si cplus17.cpp)

#include

#include

class obiect1

{ public: int a; float b,c;

obiect1(){ a=5; b=3.73; c=2.72 ; d=12;};

~obiect1()( a=0;b=0;c=0; };

virtual void arie();

protected: int d;

}

class obiect2: public obiect1

{ public: int x;

obiect2() { x=d; };

}

void obiect1::arie()

{ b=a*c; };

int main()

{

obiect2 numar;

numar.arie();

cout << "membrul apelat are valoarea: \n";

cout << numar.b;

getch();

}

Se observa ca definitia pentru procedura "arie()" este in afara clasei

obiect1,iar clasa obiect2 mosteneste procedura gata definita.In exemplu,

definitia pentru clasa obiect1 este in acelasi modul cu definitia pentru

procedura arie(),dar este posibil ca o clasa sa fie importata dintr-o

biblioteca iar definitia functiilor virtuale sa se faca diferit,in functie

de modulul in care va fi aplicata functia.Prin acest mecansim,de la o

singura clasa de baza se pot dezvolta obiecte diferite,in functie de

necesitatile de moment.Ca rezultat,instantele clasei respective (adica

obiectele generate) pot fi diferite.Pe langa acest mecanism,clasele de-

rivate pot adauga date si functii noi,astfel incat orice obiect derivat

sa poata deveni mai specializat decat cel obtinut din clasa de baza.

O clasa care contine cel putin o functie virtuala este denumita clasa

polimorfica,deoarece poate genera obiecte diferite in functie de modul

in care va fi definita functia virtuala.Clasele pot contine mai multe

functii virtuale,caz in care vor genera un polimorfism si mai accentuat.

Pointerul spre clasa de baza are acelasi tip ca si cel spre clasa drivata.


-26-

Un tip special de functie virtuala este functia virtuala pura.O astfel

de functie se declara ca avand valoarea zero (fx()=0).O clasa care contine

cel putin si o functie virtuala pura,poarta numele de clasa abstracta.

Clasele abstracte sunt concepute special pentru a fi cap de serie,

intr-o familie de clase,adica pentru a fi mostenite.O clasa abstracta

poate fi clasa de baza pentru clasele derivate,dar nu poate fi utilizata

pentru a declara obiecte (nu se pot declara instante ale clasei abstracte)

si nici nu poate fi utilizata ca argument al unor functii.Se pot declara

pointeri spre clasa abstracta.

Pentru ca o clasa derivata dintr-o clasa abstracta,sa poata fi utili-

zata pentru a declara obiecte,este obligatoriu ca functia virtuala pura

Yüklə 1,36 Mb.

Dostları ilə paylaş:
1   2   3   4   5   6   7   8   9   ...   18




Verilənlər bazası müəlliflik hüququ ilə müdafiə olunur ©muhaz.org 2024
rəhbərliyinə müraciət

gir | qeydiyyatdan keç
    Ana səhifə


yükləyin