|
sa fie redeclarata,atat pentru clasa abstracta cat si pentru clasa deri-
|
səhifə | 5/18 | tarix | 12.09.2018 | ölçüsü | 1,36 Mb. | | #81721 |
| sa fie redeclarata,atat pentru clasa abstracta cat si pentru clasa deri-
vata.Astfel,declararea unei functii virtuale pure "forteaza" redeclararea
functie respective la nivelul descendentilor (spre deosebire de functia
virtuala la care redeclararea este optionala).
EXEMPLU: (vezi si cplus18.cpp)
#include
#include
class obiect1
{
private: virtual void arie()=0;
}
void obiect1::arie(){};
class obiect2:public obiect1
{ public: int z;
obiect2() { z=5; };
void arie() { z=z*7; };
};
int main()
{ obiect2 data1;
data1.arie();
cout << "\n z are valoarea: \n";
cout << data1.z;
getch();
}
In exemplul de mai sus,obiect1 este o clasa abstracta.Daca se omite rede-
finirea clasei arie(),atunci si clasa derivata obiect2 va fi tot abstracta
si nu va putea fi instantializata.Acest mecanism se utilizeaza atunci
cand clasa abstracta defineste doar prototipul unei serii de clase deri-
vate.In fiecare clasa derivata,va trebui ca functia virtuala pura sa
fie redefinita.O clasa abstracta se poate utiliza si pentru a declara un
tip de data care nu poate sa fie utilizata in program.De exemplu,puteti
realiza o clasa destinata exclusiv pentru depanarea programului.La nevoie,
derivati o clasa din clasa abstracta,redefiniti functia virtuala pura si
apoi depanati programul (se poate utiliza pentru depanare automata).
Clasele abstracte contribuie si ele la fenomenul de polimorfism.
O clasa derivata poate avea mai multi ancestori,adica se formeaza prin
preluarea datelor de la mai multe clase de baza.In acest caz se vorbeste
despre mostenire multipla.Situatia reciproca,in care o clasa de baza este
mostenita de mai multe ori,nu se numeste mostenire multipla ci se numeste
"derivare multipla".
-27-
EXEMPLU: ( vezi si cplus19.cpp )
#include
#include
class obiect1
{
public: int a,b; float c;
obiect1() { a=15;b=22;c=3.14; };
};
class obiect2
{ public: int d,e; float f;
obiect2() { d=33; e=45; f=2.72; };
};
class obiect3: public obiect1,public obiect2
{ public: float h;
obiect3(){ h=((a+b)*c)/((d+e)*f); };
};
int main()
{
obiect3 data1;
cout << "\n h are valoarea: \n ";
cout << data1.h;
getch();
}
Se observa ca clasa obiect3 se formeaza prin derivarea claselor obiect1 si
obiect2.Cu alte cuvinte,mosteneste ambele clase,astfel incat poate sa
utilizeze in constructor toti membrii claselor ancestoare.
Este foarte important sa nu se confunde specificatorul de derivare
(public,protected,private) cu specificatorul de acces la membrii clasei.
Daca nu se utilizeaza nici un specificator,valoarea implicita va fi de
tip "private".Exemplu: class A: class B este identica cu:
class A: private class B
Daca omiteti specificatorul,clasa derivata va avea toti membrii mosteniti
de tip private (adica nu vor avea vizibilitate decat locala).
Mostenirea multipla,introduce si o alta notiune,denumita ambiguitate.
Astfel,daca in doua clase diferite se utilizeaza acelasi identificator,iar
cele doua clase vor fi clase de baza pentru o clasa derivata,atunci in
clasa derivata se vor mosteni doi membri diferiti cu acelasi identificator
Aceasta situatie nu genereaza o eroare de compilare,dar in momentul in
care se va apela unul dintre cei doi membri "dublati",se va genera o
eroare de executie (compilatorul nu stie la ce adresa sa faca referinta).
Pentru evitarea acestui gen de situatii,este bine sa utilizati pentru
toate datele din program identificatori cat mai discriminativi.In acest
manual,variabilele au fost denumite simplist,cu a,b,c,d...etc. pentru a
fi cat mai usor de urmarit.In aplicatiile reale,este bine sa utilizati
identificatori formati din 5-6 caractere (litere si cifre).Daca realizati
biblioteci de clase,incercati sa utilizati denumiri diferite pentru
fiecare tip de data,din fiecare clasa.Exista si o serie de conventii uti-
lizate pentru formarea denumirilor (Exemplu: notatia hungara).Daca doriti,
si puteti,este bine sa tineti cont si de aceste conventii.
Exista si situatii in care clasele sunt gata declarate in unitati se-
parate si nu se poate evita dublarea unui anumit identificator.Pentru
-28-
iesire din starea de "ambiguitate" se pot utiliza diferite artificii de
programare.Cea mai simpla solutie este sa utilizati o variabila globala
in care sa salvati valoarea membrului "dublat".
EXEMPLU: (vezi si cplus20.cpp )
#include
#include
float x,y;
class obiect1
{ public: int a,b; float c;
obiect() { a=15; b=22; c=3.14; };
};
class obiect2: int d,e; float c;
obiect2() { d=33; e=45; c=2.72; };
};
class obiect3: public obiect1,public obiect2
{ public: float h;
obiect3(){ h=((a+b)*x)/((d+e)*y); };
};
int main()
{
obiect1 numar;
x=numar.c;
obiect2 numar2;
y=numar2.c;
obiect3 data1;
cout << " \n h are valoarea: \n ";
cout << data1.h;
getch();
}
Se observa ca cele doua clase,obiect1 si obiect2 au cate un membru de tip
float declarat cu identificatorul "c".Fiecare clasa initializeaza acest
identificator cu alta valoare (3.14 si respectiv 2.72).Clasa obiect3
mosteneste ambii membri.In situatia in care se apeleaza membrul c din
clasa obiect3,intervine situatia de ambiguitate (se returneaza o eroare
de tip "Make failed").Pentru a evita aceasta situatie,am declarat doua
variabile globale de tip int,apoi am declarat obiecte din cele doua clase
si am salvat valorile celor doi membri.In clasa derivata,am utilizat cele
doua variabile globale in locul celor doi membri "dublati".Acest gen de
"artificiu tehnic" nu este recomandabil,dar poate reprezenta o solutie,in
situatii disperate.Solutia se poate utiliza si pentru a depana un program
"blocat" din cauza unei situatii de ambiguitate.
In rezumat,proprietatile claselor de obiecte sunt:
1.INCAPSULARE (datele au vizibilitate doar in interiorul clasei)
2.MOSTENIRE(o clasa derivata mosteneste membrii clasei ancestoare)
3.POLIMORFISM(o clasa poate genera obiecte diferite-redefineste functii)
4.AMBIGUITATE(o clasa poate accepta si membrii cu acelasi identificator)
5.ABSTRACTIZARE(o clasa cu o functie virtual pura nu are instante)
6.AUTOMATISM(prin constructor si destructor,aloca si dealoca memoria)
7.AUTONOMIE(o clasa permite operarea cu blocuri de date independente )
Pentru informatii mai detaliate despre clase si obiecte,consultati
literatura de specialitate.
-29-
MODULE,FILE,CONTEINERE,UNITATI,BIBLIOTECI
Limbajul C,cel care a stat la baza limbajului C++,a fost dezvoltat
prin expandarea unui algoritm intern utilizat de centralele telefonice.
Deoarece procesoarele centralei erau produse pe 16 biti,la fel a fost si
conceptia originala a acestui limbaj.Ca rezultat,C si urmasul sau C++ au
nevoie de structuri suplimentare pentru a comunica cu perifericele.
Limbajul C++ a fost dezvoltat intr-un moment in care majoritatea calcu-
latoarelor erau proiectate cu o arhitectura de 32 de biti,motiv pentru
care C++ a extins formatul de lucru la 32 de biti.
In epoca moderna,formatul de 32 de biti nu mai este satisfacator,astfel
a fost necesar sa se introduca diverse structuri software,care sa permita
lucrul cu formate mult mai mari.Aceste structuri nu au corespondent hard-
ware ci sunt un fel de tabele,sau matrici,in care datele se expandeaza
pana la formatul necesar.Cu ajutorul acestor structuri,procesoarele mo-
derne pot opera cu volume din ce in ce mai mari de date.Cu cat un procesor
este mai puternic (opereaza cu mai multa memorie/unitatea de timp) cu
atat are nevoie de un volum din ce in ce mai mare de date/unitatea de timp
pentru a putea fi exploatat rational( Exemplu: -un procesor de 3 Gbiti
utilizat doar pentru a citi texte editate in MS-DOS este subexploatat).
Pentru a putea satura cu date procesoarele rapide,se creaza structuri din
ce in ce mai complexe,in care se executa un numar din ce in ce mai mare
de operatii simultane.
Un prim pas in acest sens,il reprezinta clasele si obiectele sau bazele
de date formatate.Toate datele incluse intr-un obiect formeaza un mediu
de operare,izolat de cel al celorlalte obiecte.Restul variabilelor,decla-
rate extern se spune ca au vizibilitate globala.In cazul in care o apli-
catie lucreaza simultan cu mai multe file de program,toate datele decla-
rate in afara obiectelor se vor acumula intr-un spatiu comun,cu vizibili-
tate globala.Acest fapt,poate fi avantajos in unele situatii,dar poate
genera situatii de ambiguitate in situatiile in care exista date denumite
la fel in file diferite.Pentru a putea grupa toate aceste date fara a crea
conflicte de identificator (ambiguitati) se pot forma module de program,
in care spatiul comun din memoria de operare va fi subimpartit in unitati
distincte.Aceste unitati vor fi asemanatoare cu un "supraobiect",in care
se pot grupa: variabile,constante,functii si proceduri,obiecte si clase,
file header si unitati sau biblioteci,etc.
Acest gen de module se declara cu ajutorul cuvantului cheie "namespace"
urmat de un identificator care denumeste modulul respectiv.Elementele
incluse intr-un astfel de modul se declara la fel ca si elementele unei
clase si pot fi apelate cu ajutorul operatorului de scope (::),la fel ca
si membrii unui obiect.Practic,un astfel de modul este un superobiect in
care se grupeaza toate elementele pe care dorim sa le izolam de restul
spatiului din memoria de operare.Cu cat memoria de operare este mai mare,
cu atat este mai comod de lucrat cu astfel de module.In module se pot
include filele header si bibliotecile dorite,astfel incat sa formeze un
anumit "mediu de operare" in care se pot executa algoritmii doriti.
Aceasta forma de suprastructurare este evidemta (si obligatorie) la
versiunile mai noi de program (incepand cu Visual C++) si este conceputa
special pentru programele care opereaza cu interfete grafice sau cu
obiecte si structuri de date complexe (file de 4-8 sau chiar 32 MB ).
-30-
Prin acest mecanism,mediul de operare nu mai este unic,ci este frag-
mentat.Astfel,in momentul necesar se incarca modulul dorit,se executa
operatia necesara,dupa care se poate elibera memoria pentru un nou modul.
EXEMPLU: ( vezi si cplus21.cpp )
#include
#include
namespace modul1 { int nr=5; };
namespace modul2 { float nr= 3.55; };
int main()
{
cout << "\n Se executa primul modul din program ! ";
cout << modul1::nr;
cout << "\n Se executa al doilea modul din program ! ";
cout << modul2::nr;
getch();
}
Se observa ca in program pot coexista fara probleme doua variabile din
tipuri diferite,cu valori diferite si cu acelasi identificator (nr).
Acest gen de compartimentare este extrem de util atunci cand pentru a
proiecta o aplicatie avem nevoie de doua sau mai multe biblioteci de
functii,realizate de autori diferiti,dar care au utilizat aceeasi identi-
ficatori pentru date diferite.In aceasta situatie,filele se pot incarca
in module diferite si se pot apela in momentul in care sunt necesare:
EXEMPLU: ( vezi si cplus22.cpp )
namespace modul1 {
#include ;
#include ; };
int main()
{
int x=1;
for( x; x<10; x++ )
{
modul1::printf("text oarecare \n");
modul1::sleep(1);
}
}}
Pentru a incarca in memoria de operare toate datele dintr-un modul se va
utiliza formula: using namespace "numele modulului";
EXEMPLU: ( vezi si cplus23.cpp )
namespace modul1 {
#include ;
#include ; }
int main()
{
using namespace modul1;
printf("text oarecare \n");
getch();
}}
Observati avantajele acestui mecanism.Se incarca in memoria de operare
atat cat este necear pentru executie,sau tot modulul.
-31-
Cu ajutorul modulelor declarate prin "namespace" programatorul poate
gestiona mult mai usor spatiul de memorie.Exista si un astfel de modul
standardizat,denumit "namespace std",care este utilizat de catre biblio-
teca STL (Standard Template Library) si de multe alte file header sau
DLL,pentru descarca clasele definite.Pentru a putea avea acces la datele
definite in aceste unitati,trebuie sa includeti si comanda:
using namespace std;
Containerele sunt alt tip de "superclase" sau "superobiecte" si se
utilizeaza pentru a gestiona serii si colectii de obiecte.Acest tip de
structuri a fost definit anume pentru a putea efectua cat mai usor ope-
ratiile repetitive,asupra unor obiecte din acelasi tip,sau asupra unor
grupuri neomogene de date din tipuri diferite.Containerele sunt de fapt
clase template (parametrizate) si au toate in comun urmatoarele elemente:
un constructor implicit,un destructor si un operator de atribuire.
Containerele se pot imparti in trei tipuri fundamentale:
1.SECVENTIALE (double ended queue,list,vector )
2.ASOCIATIVE (map,multimap,set,multiset )
3.ADAPTOARE (priority-queues,queues,stack)
Fiecare tip de container detine si un set de functii specializate
(membrii clasei) prin care opereaza asupra datelor.Fiecare tip de con-
tainer detine o fila specializata in care este definita clasa si metodele
sale.Filele au acelasi nume ca si tipul de container: ,,
,
Pentru a utiliza un astfel de conteiner,se incarca fila header corespunza-
toare,se declara "using namespace std;" apoi se declara tipul de data cu
o expresie de genul: conteiner identificator;
EXEMPLU: vector v1;
Pentru a apela membrii unui obiect,se utilizeaza un alt tip de data denu-
mit itaratori.Iteratorii au aceleasi proprietati ca si pointerii si se
pot apela si utiliza la fel ca si pointerii.Iteratorii pot fi de mai
multe tipuri:
1.input_iterator-(InIt)- citeste valori prin avansare.Poate fi incre-
mentat,comparat sau dereferit.Valoarea citita va fi de tipul
V=*X iar incrementarea se poate face prin V=*X ++
2.output_iterator-(OutIt)- scrie valori prin avansare pas cu pas.Poate
fi incrementat sau dereferit.Exemplu: *X++ = V
3.forward_iterator-(FwdIt)-citeste sau scrie valori prin incrementare.
Combina proprietatile celor doi iteratori de mai sus.
4.bidirectional_iterator-(BidIt)- citeste si scrie valori atat prin
incrementare cat si prin decrementare. V = *X ++ sau V = *X--
5.random_iterator-(RanIt) - are acces aleator atat pentru scriere cat
si pentru citire a valorilor,la orice adresa din conatiner.Este
cel mai versatil dintre iteratori si permite orice fel de operatie
aritmentica ce se poate efectua asupra pointerilor.Exemplu: poate
executa salturi de la orice membru,la orice alt membru
6.reverse_iterator - poate fi de tip random sau bidirectional dar se
deplaseaza doar in directia inversa a containerului.
Dupa cum le spune si numele,se utilizeaza mai ales pentru operatii
repetitive in cadrul unor bucle FOR...WHILE...DO etc.Orice iteroator
poate fi inlocuit de un obiect de tip pointer.Daca sunteti mai familia-
rizati cu pointerii,puteti utiliza pointeri in locul iteratorilor.
-32-
Conteinerele sunt destinate pentru operatii cu serii mari de date din
acelasi fel,sau din tipuri diferite.Containerul va avea acelasi tip de
data cu elementele pe care le contine.Exemplu: va fi de tip struct daca
contine structuri sau va fi de tip INT daca nu contine decat valori nu-
merice de tip INT.In functie de tipul de data si de numarul de obiecte
pe care le contine,durata pentru a accesa un anumit membru,sau pentru a
cauta un anumit membru in memorie depinde si de tipul containerului.
Astfel,tipul vector,care este de tip arie si lucreaza cu iterator de
tip random-acces are un timp de acces la membri constant (cel mai scurt),
dar timpul pentru sortare sau cautare a datelor depinde de numarul de
elemente din container.Prin contrast,containerele de tip stiva au atat
timpul de cautare si sortare cat si timpul de acces la membri dependent
de numarul de elemente din container.In cazul in care lucrati cu baze
de date care contin sute de mii de inregistrari,diferentele dintre tipu-
rile de conteinere devin din ce in ce mai pronuntate.Alegerea tipului de
container se va face atat in functie de tipul datelor arhivate cat si in
functie de numarul estimat de inregistrari,raportat la viteza de proce-
sare.In general,containerele se utilizeaza pentru a forma arhive mari de
date,iar tipul de container preferat este tipul vector.
Tipul vector
METODE (allocator_type,assign,at,back,begin,capacity,clear,const_iterator,
const_reference,const_reverse_iterator,difference_type,empty,end,
erase,front,get_allocator,insert,iterator,max_size,operator[],
pop_back,push_back,rbegin,reference,rend,reserve,resize,reverse_i-
terator,size,size_type,swap,value_type,vector)
Poate contine elemente cu lungime variabila,pe care le arhiveaza sub
forma de arii de date.Accesul la membri este de tip random si constant.
Accesul la membri este foarte rapid,deoarece nu trebuie sa parcurga toata
memoria ci acceseaza direct adresa,cu ajutorul pointerului.Cautarea unui
element prin comparatie,sau inserarea la o anumita adresa,este determinata
de dimensiunea conteinerului si de numarul de elemente parcurse pana la
identificarea adresei dorite.Alocarea si dealocarea spatiului pentru
fiecare element se face automat,printr-un obiect de alocare (alocator).
Daca un container de tip vector este realocat (s-a depasit capacitatea
maxima) atunci toti iteratorii si pointerii anteriori devin invalizi
(deoarece se schimba adresele de memorie ale elementelor).
Tipul vector este cel mai frecvent utilizat,deoarce permite facil
accesul la orice membru al containerului cu ajutorul unui iterator de
tip random-acces si a functiei at().Prin comparatie,containerele de tip
stiva functioneaza dupa principiul LIFO(Last In First Out) sau FIFO (First
In First Out).Pentru a putea avea acces la un anumit membru din stiva,
trebuie extrasi pe rand toti membri situati intre capatul stivei si cel
cautat,dupa care,dupa efectuarea operatiei dorite,toti membri extrasi
trebuiesc introdusi inapoi in stiva,in ordinea in care au fost extrasi.
Este usor de imaginat faptul ca o astfel de operatie poate sa dureze
destul de mult timp,mai ales atunci cand se lucreaza cu mii si zeci de
Dostları ilə paylaş: |
|
|