Sunteți pe pagina 1din 135

****************************

* Limbajul C++ si OOP *


* ABC-doar *
****************************

**********************************
* autor RUSU MIRCEA AUREL VALER *
**********************************

********************************************************
* MOTTO: *
* "Tot ceea ce nu putem sa indeplinim singuri *
* ne limiteaza libertatea." *
* C. PAVESE *
********************************************************

De ce C++ ?
Pentru streamuri si pointeri,pentru tabele virtuale,pentru clase
template si obiecte predefinite,pentru fereastra CPU,pentru
multitudinea de formate...sau poate pentru abilitatea de a lucra
cu memoria RAM,cu memoria de operare,cu memoria fixa sau virtuala,
pentru zeci de biblioteci DLL si ActiveX,sau pur si simplu pentru
a comunica cu calculatorul si/sau cu alte persoane.
C O N T I N U T

Generalitati . . . . . . . . . . . . . . . . . . 1

Concepte fundamentale . . . . . . . . . . . . . 3

Elementele limbajului C++ . . . . . . . . . . . 8

Concepte de structurare a limbajului . . . . . .10

Clase si obiecte . . . . . . . . . . . . . . . .14

Clase derivate . . . . . . . . . . . . . . . . 23

Module,file,containere,unitati,biblioteci . . . 29

Biblioteca OWL (Object Windows Library). . . . 57

Prelucrarea mesajelor Windows (dispatch). . . .107

VDBT (Visual Database Tools) . . . . . . . . . 110

Concluzii . . . . . . . . . . . . . . . . . . 120
-1-
GENERALITATI
Lucrarea fundamentala in care este descris limbajul C++ apartine
autorilor Margaret Ellis si Bjarne Stroustrup si se intituleaza:
"Adnotated C++ Reference Manual".Aceasta lucrare reprezinta standardul
de referinta ANSI si este recomandata oricarui programator care doreste
sa utilizeze limbajul C++.O alta lucrare de referinta,este "C++ in a
Nutshell"" scrisa de Ray Lischner pentru O'Reilley,sau "An Introduction
to C++ Programming" scrisa de Bjorn Fahller.
Limbajul C++ nu este "o limba moarta" ci este in permaneta dezvoltare.
Fiecare autor completeaza acest limbaj cu noi biblioteci de functii sau
obiecte.Pentru a garanta compatibititatea totala,este necesar ca toti
acesti autori sa respecte standardul de limbaj,astfel incat noile dez-
voltari ale limabjului sa fie comprehensibile si usor de aplicat.
Limbajul C++ nu a fost proiectat si planificat meticulos ci a aparut
mai mult ca o necesitate,din dorinta de a tine pasul cu dezvoltarea
continua a resurselor hardware.Din acest motiv,aspectul general este
asemenator cu cel al unui morman de haine "second hand" in care fiecare
client isi cauta marimea,culoarea sau pretul care-i convine.Limbajul
are la baza nucleul C,la care s-au adaugat structuri speciale capabile
sa organizeze datele sub forma de module,denumite "obiecte"(sau clase).
In epoca de pionierat,procesorul principal lucra direct cu memoria
inscriptibila de pe disc.Apoi s-a adaugat un modul de memorie de operare
in care datele erau stocate temporar,pentru a putea fi utilizate mult
mai eficient.Acest modul de memorie a inceput de la 8 si 16 biti,apoi
a inceput sa creasca exponential pana la 4M,8M,16M si 32M (megabiti),
pentru ca in etapa actuala sa ajunga pana la 1 GByte.Ca rezultat,pro-
cesorul principal a inceput sa lucreze cu fragmente de memorie din ce
in ce mai mari,fara sa fie necesar sa apeleze memoria de disc.Practic,
programul se incarca o singura data in memoria de operare,apoi se deru-
leaza ori de cate ori este necesar,sau se executa un numar infinit de
operatii.Cu cat fragmentele de memorie sunt mai mari,cu atat viteza de
operare si eficienta sistemului este mai mare.
Pentru a optimiza la maximum eficienta resurselor hardware,este
necesar ca programele sa fie subimpartite in astfel de fragmente de
memorie denumite module.Fiecare astfel de modul poate contine unul sau
mai multe obiecte,un anumit numar de variabile,constante,functii si pro-
ceduri si o serie de linii de comanda prin care organizeaza operatiile din
modul.Atunci cand se utilizeaza mai degraba obiecte,decat variabile simple
pentru a organiza datele,procedeul tehnic se numeste programare orientata
spre obiect,sau OOP (Object Oriented Programmming).Este destul de evident
ca programarea orientata spre obiect ofera viteze de lucru mult mai mari,
pentru volume impresionante de date.
La inceput,se utiliza mai degraba termenul de structuri de date.Aceste
structuri erau fie cele din tipul struct (din C),fie alte forme de orga-
nizare a datelor cum sunt inregistrarile: (record),tabelele (database),
ariile de date sau de biti,campurile de biti(Bitmap),matricele etc.
Deoarece datele structurate pot fi organizate in nenumarate forme si
formate,s-a impus necesitatea realizarii unui standard comun,astfel incat
datele dintr-un program sa fie compatibile cu cele din alte programe.In
caz contrar,fiecare program trebuie sa posede un set propriu de functii
si proceduri prin care opereaza asupra datelor.

-2-
Bjarne Stroustrup este cel care a propus standardul pentru structu-
rile de date de tip class(obiecte) si a depus un efort considerabil pentru
raspandirea acestui standard printre programatori.Un astfel de obiect
deriva din tipul struct prezent in limbajul C,dar organizeaza acest tip
de data dupa un set standardizat de reguli,astfel incat orice obiect
dezvlotat cu ajutorul acestui standard sa fie usor de inteles si
usor de aplicat sau transformat.Pentru a aplica sau pentru a dezvolta
astfel de obiecte,este necesar sa cunoasteti si sa intelegeti aceste
reguli elementare (incapsulare,mostenire,supraincarcare etc.).
Ca rezultat,datele pot fi organizate mult mai riguros,iar programa-
torii cu forme de expresie si experiente diferite,pot utiliza in comun
aceleasi structuri de date.Cel mai cunoscut exemplu in acest sens este
standardul ActiveX,prin care obiectele definite in limbaje diferite pot
fi apelate din programe scrise in alt limbaj (Exemplu: C++,Pascal,Delphi).
Mai mult decat atat,exista tendinta de a grupa obiectele din acelasi
tip in biblioteci specializate,sau de a crea module independente de pro-
gram,care pot fi apelate din orice alt program.Cele mai utilizate sunt
asa numitele file de resurse (aplicatii grafice,icons,file pentru sunet
sau imagine,file pentru interfata cu resursele hardware etc.).Astfel
de file pot fi apelate din limbaje diferite (Exemplu: Delphi exploateaza
un numar mare de resurse editate in C++).Practic,valoarea unui program
sau a unui sistem de operare este determinata tocmai de capacitatea de
a lucra cu orice format de date,in orice limbaj de programare.Sistemele
de operare moderne se numesc din acest motiv "medii de programare".
Limbajul C++ este ideal pentru realizarea unor astfel de "resurse
software" si reprezinta un instrument de mare valoare in bagajul de cu-
nostiinte al oricarui programator.In plus,C++ a contribuit substantial
la dezvoltarea conceptului de programare modulara si face parte incontes-
tabila din istoria si evolutia limbajelor de programare.
Acest manual nu isi propune o prezentare exhaustiva a acestui limbaj,
ci doar prezentarea cat mai explicita (gen abecedar) a principalelor
notiuni necesare pentru intelegerea si aplicarea acestui limbaj.Pentru
detalii,notiuni teoretice,referinte si date de specialitate exista o
numeroasa bibliografie.Cel mai practic este sa apelati la cartile gratuite
oferite on-line prin Internet.Datele din acest manual se bazeaza pe
Borland C++ Version 5.A Copyright @1991,1996 si pe Microsoft Visual C++
Copyright @ 1994-1998 versiunea 6.0,dar orice alta versiune si implemen-
tare este la fel de buna.
Acest manual este dedicat incepatorilor si celor care mai au inca
destule notiuni neclare,sau nesigure...sau doresc sa afle un alt punct
de vedere decat cel din manualele de specialitate.Parcurgerea acestui
manual nu confera drepturi stiintifice sau administrative,nu va asigura
nici un fel de credit si nici nu va asigura succesul in dezvoltarea unei
cariere tehnice.Acest manual are doar rolul de a va facilita intelegerea
si/sau aplicarea altor surse de informatie.

-3-
CONCEPTE FUNDAMENTALE
Limbajul C++ poate fi utilizat pentru a apela functii si proceduri,
prin care se solicita prelucrarea datelor.Operatiile executate asupra
datelor se pot efectua atat "la nivel inferior",adica la nivel de bit sau
byte,cat si "la nivel superior",adica datele sunt structurate si/sau
suprastructurate in variabile si constante,obiecte sau structuri de date,
file si arii de memorie,etc.
Limbajul C++ nu este cel mai fericit punct de pornire pentru incepatori
deoarece complexitatea notiunilor necesare pentru a intelege modul de
structurare a unui program poate determina o serie de confuzii.Acest
manual presupune existenta unor notiuni elementare de programare liniara,
cunoasterea notiunilor lexicale elementare (variabila,identificator,ope-
rator,cuvant cheie etc.).Pentru initiere puteti utiliza orice manual
pentru C si/sau Pascal (vezi si Limbajul C si C++ ABC-doar ).
Dintre elementele definitorii pentru C++,fac parte stream-urile si
pointerii.Prin stream-uri,programatorul C++ poate controla modul in care
circula datele in calculator,iar prin pointeri programatorul C++ are
acces nu numai la datele prelucrate ci si la adresele de memorie la care
sunt stocate datele respective.Aceste elemente,specifice pentru mediul
C si C++ confera programatorului mijloace de exprimare inexistente in
alte limbaje de programare(sau mult mai dificil de apelat).
Prin stream (curent,flux de date) se intelege in termeni simplisti
o secventa de biti sau un influx de informatie.In calculator,datele
circula sub forma de curenti electrici alternativi,in care fiecare osci-
latie se traduce prin valoarea unui bit.Un astfel de curent,poate fi
intrerupt imediat,sau poate fi extrem de lung.In functie de procedurile
utilizate pentru interpretarea unui astfel de curent,se poate face o
analiza "la nivel inferior",caz in care stream-ul este format dintr-o
secventa succesiva de elemente izolate de tip bit sau byte,sau se poate
face o analiza "la nivel superior" in care stream-ul este format din
elemente cu un format oarecare(numere int sau float,siruri de caractere,
arii de date sau matrice etc.).Ca rezultat,exista un numar extrem de
mare de variante de intrerpretare a datelor contiunute intr-un astfel
de curent(stream).In termeni generali,stream-ul este inteles cel mai
frecvent ca o fila virtuala,localizata in memoria de operare,care poate
fi utilizata fie ca sursa,fie ca destinatie,pentru un grup de biti(ordo-
nati in structuri formatate sau sub forma de sir de elemente).Spre deose-
bire de memoria inscriptibila (hard-disc),stream-urile au o valoare tem-
porara (au o existenta efemera) dar pot fi scrise si rescrise ori de cate
ori,fara a apela la unitati specializate si fara a uza capacitatea de
stocare a datelor (stream-urile nu uzeaza hardul).In calculatoarele vechi,
cu porturi exclusiv seriale,la un anumit moment dat putea exista un singur
stream activ si eventual cateva stream-uri aflate "pe linie de asteptare"
in memoria auxiliara.Pentru operatiile de intrare a datelor se utiliza
un stream de intrare denumit istream,iar pentru operatiile de iesire a
datelor se utiliza un stream de iesire denumit ostream (i si o provin de
la input si respectiv output).Nu se puteau executa simultan operatii de
intrare si iesire a datelor.In calculatoarele moderne,exista un numar
oarecare de porturi paralele.Ca rezultat,la un anumit moment dat,pot
coexista mai multe stream-uri de intrare si respectiv de iesire a datelor

-4-
din calculator astfel incat se pot executa simultan mai multe operatii.
Streamurile stau la baza operatiilor de multiprocesare paralela.Pentru
fiecare linie de executie (thread) exista cate un stream independent.
Un stream poate sa nu contina nici o data (stream nul) sau poate sa fie
practic infinit(stream neintrerupt).Practic,in momentul in care se
deschide un stream,se deschide la nivel de hardware un port paralel prin
care se vor putea primi sau transmite datele necesare,in momentul potri-
vit.Numarul acestor porturi este limitat.Din acest motiv,programatorul
trebuie sa tina o evidenta stricta a fiecarui stream utilizat.Fiecare
stream deschis si lasat "pe linie de asteptare" reduce din capacitatea
de procesare a unitatii centrale(pana cand eventual se blocheaza complet).
Programele evoluate,mentin automat evidenta stream-urilor cu ajutorul
unor grile de tip tabel si avertizeaza automat in momentul in care
numarul lor devine prea ridicat.In termeni generali,un stream poate fi
interpretat ca si cand ar fi un fragment de memorie,determinat electric
si existent doar temporar in memoria de operare a calculatorului.Modul
de ordonare si gestionare a stream-urilor face diferenta dintre un pro-
gramator calificat si unul incepator.
In C++,toate operatiile de intrare/iesire a datelor sunt mediate prin
streamuri specializate organizate sub forma de obiecte si definite in
unitati specializate.Astfel,comunicarea cu diferitele unitati de intrare
sau iesire a datelor se poate face extrem de simplu,prin apelarea unor
functii sau proceduri simple.Pentru a introduce date in stream se utili-
zeaza obiectul cin si operatorul >> iar pentru a extrage date din stream
se utilizeaza obiectul cout si operatorul <<.
La prima vedere,pare confuziv,deoarece << si >> sunt operatorii pentru
salt binar "left shift" si "right shift".Operatiile sunt posibile deoarece
in unitatea "iostream" utilizata pentru operatiile de tip I/O cei doi
operatori au fost redfiniti,astfel incat sa execute operatia de insertie
si respectiv extractie,pentru fiecare tip de data acceptat de C++.Ca re-
zultat,operatorii se pot utiliza pentru orice tip de data.Acest procedeu
tehnic se numeste "supraincarcarea operatorilor" si este specific pentru
limbajul C++.Daca se utilizeaza in operatii cu valori binare,cei doi
operatori isi mentin si semnificatia initiala si vor executa operatiile
de salt binar.Asadar,prin supraincarcarea operatorilor se intelege metoda
tehnica prin care un anumit operator poate avea semnificatie diferita in
functie de tipul de data cu care lucreaza.In mod similar,operatorul *
poate executa operatia de inmultire,desemneaza un poiner sau respectiv
citeste valoarea unui pointer,etc...
Prin acest artificiu software,functiile printf() si scanf() din uni-
tatea stdio.h (specifice limbajului C) pot fi inlocuite prin expresiile
cout << si respectiv cin >> care sunt mult mai simplu de apelat.Puteti
utiliza in continuare functiile C (printf(),scanf() etc.) dar este reco-
mandabil sa utilizati unitatea "iostream" si obiectele specializate cout
si cin,deoarece sunt proiectate special pentru arhitectura de 32 de biti.
Stream-urile standard stdin,stdout,stderr,stdaux si sdtprn au fost inlocu-
ite prin obiectele cin,cout,cerr etc.
Trebuie remarcat faptul ca in versiunea C++,obiectele cin si cout se
pot utiliza si cu tipuri de date inexistente in limbajul C,cum ar fi
wide string sau tipurile de date definite de catre utilizator.Prima ope-
ratie in C++ este afisarea unui text pe ecran:

-5-
EXEMPLU: (vezi si cplus1.cpp)
#include <iostream>
#include <dos.h>
main()
{ cout << "Hello world !";
sleep(5);
return 0;
}
Pentru a edita exemplul,alegeti din File optiunea New,apoi Project,iar
in fereastra New Target introduceti numele proiectului(in loc de proj0000)
si schimbati calea de acces(daca este necesar).Este bine sa creati un
director special in care sa arhivati toate proiectele,sau sa utilizati
cate un director nou pentru fiecare proiect.Apoi utilizati Browse pentru
a selecta calea de acces la directorul dorit.Confirmati cu OK.
C++ va crea automat filele .cpp,.def si .rc necesare pentru proiect.
Filele vor fi afisate in fereastra Project,sub forma de arbore de rami-
ficatie cu nodul principal in fila .exe (proiectul va utiliza cele trei
file pentru a compila si construi fila executabila).
In etapa actuala nu este necesara decat fila .cpp.Selecati cu un click
drept de mouse celelalte doua file si alegeti optiunea Delete node pentru
a sterge cele doua file din proiect.Apoi deschideti fila .cpp cu un
dublu click si editati textul de mai sus.Compilati,construiti proiectul
cu Build all apoi convertiti executabilul cu Make all.
Daca nu a intervenit nici o eroare,puteti executa proiectul cu Run,sau
cu butonul pe care este desenat un trasnet (butonul Run).
La executie,se va afisa o fereastra Dos in care se va afisa textul
"Hello world" timp de 5 secunde.Fereastra Dos nu are autostop,astfel
incat este necesara o functie de intarziere pentru a putea observa mesa-
jul dirijat catre ecran.Pentru unele compilatoare este necesar sa repor-
niti programul inainte de a putea fi executat.In acest caz (daca fereastra
de debug afiseaza zero erori),salvati fila,inchideti programul si apoi
lansati din nou C++ si deschideti fila cu Open.Aceasta operatie este
necesara pentru a realiza "link-ul",adica o cale de acces spre adresa de
memorie la care este arhivata fila.Daca nici in acest caz nu observati
fereastra Dos,micsorati fereastra Windows in care este deschis programul
C++ (este posibil ca fereastra sa se deruleze in spatele ferestrei Win-
dows -care are precedenta),sau verificati in Help definitia exacta pentru
functia Sleep() (in unele versiuni parametrul functiei este exprimat in
secunde in timp ce in versiunile recente este exprimat in milisecunde)
si ajustati valoarea astfel incat sa fie compatibila cu versiunea insta-
lata(Exemplu: in Visual C++ utilizati 5000 de milisecunde).
In continuare,puteti utiliza diferite tipuri de date.Incercati sa
utilizati cout impreuna cu numere de tip int,float,double etc.
Pentru a nu incurca cei doi operatori,puteti utiliza urmatoarea regula:
varful unghiului este orientat spre destinatie iar deschiderea unghiului
este orientata spre sursa.
EXEMPLU : cout << "Hello world !"
sursa este sirul de caractere "Hello world !" iar destinatia este
obiectul stream "cout" (care va dirija fluxul de informatii spre ecran).
In mod similar,streamurile intermediaza orice schimb de date dintre
procesor si uinitatile de executie din calculator.

-6-
EXEMPLU: (vezi si cplus2.cpp)
#include <iostream>
#include <conio.h>
main()
{
cout << "Introduceti raza cercului: \n";
cout << "Click in fereastra,apoi valoarea si Enter!) \n";
float numar=0;
cin >> numar;
cout << "Aria cercului este: \n";
numar=numar*numar*3.141592;
cout << numar;
cout << "\n\n Apasati orice tasta !";
getch();
return 0;
}
In exemplu de mai sus,se realizeaza deja o comunicare intre procesor,
tastatura si ecran.Pentru a introduce datele de la tastatura este
necesar un click de mouse care sa selecteze ecranul.Dupa ce datele intro-
duse sunt afisate pe ecran,tastati Enter pentru confirmarea lor.
Observati o alta noutate fata de limbajul C.In C++ variabilele si
constantele locale pot fi declarate in corpul functiei,in orice loc,
dar inainte de a fi apelate.Ca rezultat,programatorul are o libertate
de miscare mai mare,dar trebuie sa respecte aceleasi reguli ca si in C
(identificatorul trebuie sa fie unic,sa nu fie cuvant cheie etc...).
Pentru a realiza o functie de intrerupere,am utilizat in acest caz functia
getch(),care asteapta apasarea unei taste oarecare.
In cazul operatiilor de intrare/iesire,supraincarcarea operatorilor
<< si >> are un rezultat extrem de spectaculos si foarte practic.Ope-
ratiile de intrare-iesire sunt foarte numeroase si procedeul tehnic este
perfect justificat.Asta nu inseamna ca trebuiesc supraincarcati toti
operatorii dupa bunul plac al fiecarui programator.In mod normal se va
evita supraincarcarea si orice alt procedeu tehnic care se preteaza la
interpretari ambigue,sau la modalitati de expresie confuziva.Pentru
ca analiza si depanarea programelor sa poata fi facuta cu programe spe-
cializate,este bine sa respectati standardul de limbaj atunci cand editati
programe sau unitati de functii de interes general.
Chiar daca variabilele locale sunt foarte usor de introdus,nu inseamna
ca trebuie abuzat.Compilatorul plaseaza fiecare variabila nou declarata
la o anumita adresa de memorie si apoi citeste valoarea arhivata ori de
cate ori este necesar.Pentru a realiza acest deziderat,ori de cate ori
este apelata o variabila,procesorul va cauta in memorie pana cand gaseste
identificatorul si apoi returneaza valoarea.Cu cat sunt mai multe varia-
bile cu atat sunt mai multe operatii care trebuiesc executate.Cu cat
sunt mai multe variabile,cu atat durata de executie se prelungeste,uneori
absolut inutil.Unele sisteme de operare simplifica aceasta operatie prin
utilizarea unor tabele de pointeri in care fiecare variabila nou declarata
este introdusa in tabel impreuna cu adresa de memorie la care este arhi-
vata.In acest fel,procesorul nu va mai executa "un dans nebun" prin me-
morie ci va citi direct din tabela de pointeri adresa de memorie la care
se gaseste variabila respectiva (viteza de operare creste substantial).

-7-
Asadar,un pointer este o variabila care arhiveaza adresa de memorie a unei
alte variabile.Pentru a citi adresa de memorie a unei variabile se poate
utiliza si operatorul "&",dar,utilizarea pointerilor permite si accesul
direct la valoarea arhivata la adresa respectiva.Cu alte cuvinte,proce-
sorul nu mai trebuie sa caute o variabila prin toata memoria ci "sare"
direct la adresa la care este arhivata.
Pentru aplicatiile banale,in care se utilizeaza cateva variabile si
un numar limitat de operatii,utilizarea pointerilor nu se justifica si
nici nu ofera optimizari spectaculoase (un procesor modern executa cateva
miliarde de operatii pe secunda).In schimb,in cazul programelor mari si
al unitatilor de functii care urmeaza sa fie utilizate de un numar mare
de utilizatori,fiecare astfel de operatie se multiplica exponential,astfel
incat utilizarea pointerilor devine justficata sau chiar imperioasa.
Ca rezultat,majoritatea functiilor implementate in unitatile standard
lucreaza cu memoria prin intermediul pointerilor (chiar daca acest fapt
este sau nu este evident).Reciproc,in cazul in care doriti sa editati
programe si functii de uz general (sau comerciale),va trebui sa optimizati
la maximum operatiile executate asupra memoriei (inclusiv prin pointeri).
Pentru exercitiu,incercati sa va imaginati ce ar insemna cautarea unei
adrese de Internet fara sistemul de adrese TCP/IP.Acelasi lucru se poate
intampla si in cazul unui program complex,cu numeroase obiecte,care ape-
leaza mai multe baze de date.Fara un sistem riguros de ordonare a adrese-
lor,aceste operatii ar fi extrem de greu de realizat.
EXEMPLU: (vezi si cplus3.cpp)
#include <iostream>
#include <conio.h>
main()
{ float numar1,numar2;
cout << "Introduceti un numar oarecare! \n";
cin >> numar1;
cout << "Introduceti al doilea numar: \n";
cin >> numar2;
cout << "\n adresa de memorie este: " << &numar1;
float *pointer=&numar2;
cout << "\n si respectiv: " << pointer;
cout << "\n Valoarea pointerului este: " << *pointer;
cout << "\n\n Apasati orice tasta !";
getch();
return 0;
}
Asadar pentru declararea unui pointer se utilizeaza "*" iar pentru a
prelua valoarea pointerului (arhivata la adresa pointata) se utilizeaza
tot "*" urmat de identificator.Pointerul poate fi orientat spre un alt
pointer,caz in care se va exprima prin **pointer.Acest gen de exprimare
se poate preta si la confuzii,mai ales in operatiile aritmetice in care
se executa si operatii de inmultire,dar,cu putina rabdare,se poate des-
cifra orice expresie,idiferent cate semne "*" contine.
Stream-urile si pointerii sunt notiunile cele mai confuzive ale siste-
mului C++,dar ofera mijloace de exprimare exceptionale pentru operatiile
cu si asupra memoriei.Din acest motiv,trecerea de la C++ la alte limbaje
de programare se face mult mai greu decat de la C si Pascal la C++.

-8-
ELEMENTELE LIMBAJULUI C++
Elementele lexicale se utilizeaza pentru a forma declaratii,definitii,
instructiuni si comenzi etc.Principalele elemente care au semnificatie
pentru compilator sunt: token,comentariu,identificator,cuvant cheie,semn
de punctuatie,operator si constante.
Prin token se intelege cel mai mic element din program cu semnificatie
pentru compilator.Asadar,semnificatia unui anumit grup de caractere ASCII
poate fi diferita in functie de compilatorul utilizat.Pentru identificarea
fiecarui token,compilatorul include un program de analiza care separa
elementele unui program sursa in tokeni,prin comparare cu o lista sau un
tabel de elemente disticte,considerate a fi semnificative.De obicei,ele-
mentele sunt separate prin spatii goale,unice sau multiple si prin carac-
tere speciale de tip "new line","formfeed",tab-uri sau comentarii.Ca re-
zultat,se obtin elemente izolate,care pot fi:cuvinte cheie,identificatori
si semne de punctuatie,operatori etc.
In limbajul C++,doi tokeni pot fi grupati daca se utilizeaza grupul de
caractere ##.In acest caz,compilatorul va alipi cei doi tokeni si va in-
terpreta grupul de caractere rezultat ca pe un singur token.De la un
compilator la altul,interpretarea poate fi diferita,dar daca se respecta
cu strictete standardul de limbaj,toate compilatoarele vor produce rezul-
tate similare.Intelegerea acestor notiuni este importanta doar atunci cand
se utilizeaza programe de analiza sau de conversie a datelor de la un
sistem de operare la altul.
Comentariile sunt adnotatii de tip text,introduse in fila de cod,cu
intentia de a explica notiunile implementate.Compilatorul trateaza un
comentariu ca si cand ar fi un spatiu gol simplu(are doar rol de separator
intre tokeni),astfel incat comentariile nu influenteaza cu nimic executia
programului (dar consuma din memoria de operare). Un comentariu trebuie
introdus intre grupul de caractere /* si respectiv */ sau poate sa urmeze
dupa grupul de caractere //.
Exemple: /* Este un comentariu */
// Este tot un comantariu (pana ca capatul randului)
Dupa // tot ce urmeaza pana la capatul randului,va fi considerat de catre
compilator a fi un comentariu (si nu va fi executat).
Este bine sa explicati formulele savante si procedeele tehnice inovatoare,
atunci cand sunt greu de intuit la prima vedere,sau contin elemente rar
utilizate in programare.Comentariul trebuie sa contina datele utile pentru
depanare.Nu utilizati comentariile pentru literatura,sau aberatii savante.
Identificatorul este un grup de caractere prin care se declara in pro-
gram introducerea unui element de tip obiect sau variabila,a unei functii
sau a unei proceduri,a unei etichete,sau a unui tip de data,etc.
Pentru identificatori se pot utiliza caracterele alfanumerice si liniuta
de subliniere _.In versiunea Microsoft,un identificator poate avea maxim
247 de caractere (se recomanda sa utilizati mai putine).In C++ se tine
cont daca o litera este scrisa cu majuscula sau cu minuscula.
EXEMPLU: fileName este diferit de FileName
Identificatorii nu pot coincide cu cuvintele cheie,dar pot sa contina si
un cuvant cheie inclus intre alte caractere.Identificatorii care incep cu
doua liniute (__)sau o liniuta si o majuscula (_A,_B...etc) se utilizeaza
de obicei pentru a desemna constante si variabile predefinite in diversele
unitati.Evitati formarea de astfel de identificatori banali.
-9-
Cuvintele cheie sunt identificatori rezervati pentru o anumita semni-
ficatie precisa si nu pot fi utilizati in program cu un alt sens (nu pot
fi redefiniti).Cuvintele cheie C++ (inclusiv Visual C++) sunt:
asm,auto,bad_cast,bad_typed,bool,break,case,catch,char,class,const,
const_cast,continue,default,delete,do,double,dynamic_cast,else,enum,except
explicit,extern,false,finally,float,for,friend,goto,if,inline,int,long,
mutable,namespace,new,operator,private,protected,public,register,return,
reinterpret_cast,short,signed,sizeof,static,static_cast,struct,switch,
template,this,throw,true,try,type_info,typedef,typeid,typename,union,
unsigned,using,virtual,void,volatile,while
In versiunea Microsoft C++ se adauga si urmatorele cuvinte cheie:
allocate,__asm,__based,__cdcl,__declspec,dllexport,dllimport,__except,
__fastcall,__finally,__inline,__int8,__int16,__int32,__int64,__leave,
__multiple_inheritance,naked,nothrow,property,selectany,__stdcall,
__single_inheritance,thread,__try,uuid,__uuidof,__virtual_inheritane
Pentru semnificatia fiecaruia dintre ele consultati manualul Help.Nu
utilizati aceste cuvinte pe post de identificatori banali,chiar daca
in versiunea d-voastra nu sunt incluse sub forma de cuvinte cheie,deoarece
poate ca in viitor ve-ti upgrada la o versiune mai noua si doriti sa
pastrati compatibilitatea programelor realizate.
Semenele de punctuatie sunt:
! % ^ & * ( ) - + = { } | ~ [ ] \ ;
' : " < > ? , . / #
Punctuatorii (semnele de punctuatie),considerati izolat,nu au semnificatie
de operatori,dar au semnificatie pentru compilator (tokeni).
Pentru a primi semnificatie de operator trebuie sa fie utilizati intr-un
anumit context,impreuna cu un anumit tip de data,sau intr-o anumita
formula fixa de grupare a lor.
Operatorii sunt caractere sau grupri de caractere care determina executia
unei anumite operatii.
Principalii operatori (in ordinea precedentei sunt):
:: (scope resolution), :: (global), [] (array subscript), () (function
call), () (conversion), . (member selection), ->(member selection pointer)
++ (postfix increment), -- (postfix decrement), new (allocate object),
delete (deallocate object), delete[] (deallocate object), ++ (prefix in-
crement), -- (prefix decrement), * (dereference), & (address-of), + (plus)
- (arithmetic negation), ! (logical NOT), ~(bitwise complement), sizeof
(size of object), sizeof() (size of type), typeid() (type name), type
(type cast), const_cast (type cast), dynamic_cast (type cast),
reinterpret_cast (type cast), static_cast (type_cast), .* (apply pointer
to class member), ->* (dereferance pointer to class member), * (multipli-
cation), / (division), % (remainder-modulus), + (addition), - (substrac-
tion), << (left shift), >> (right shift), < (less than), > (greater than),
<= (less than or equal to), >= (greater than or equal to), == (equality),
!= (Inequality), & (Bitwise AND), ^ (Bitwise exclusive OR), | (Bitwise OR)
&& (logical AND), || (Logical OR), e1?e2:e3 (conditional), = (assigment),
*= (multiplication assigment), /= (division assigment), %= (modulus assig-
ment), += (addition assigment), -= (substraction assigment), <<= (left
shift assigment), >>= (right shift assigment), &= (Bitwise AND assigment),
|= (Bitwise inclusive OR assigment), ^= (Bitwise exclusive OR assigment)
, (comma).
-10-
In functie de numarul membrilor din ecuatie operatorii pot fi unari,
binari sau tertiari.Operatorii se asociaza fie cu expresia de la stanga
lor,fie cu expresia situata de la dreapta lor,fie cu nici una.In lista
prezentata,operatorii sunt ordonati de la cel cu precedenta cea mai mare
spre cel cu precedenta cea mai mica.Ultimii din lista,incepand cu cel
conditional (e1?e2:e3) se asociaza de la dreapta la stanga iar ceilalti
se asociaza de la stanga la dreapta (sau deloc).
In situatiile in care intr-o expresie sunt mai multi operatori cu
precedenta egala (de acelasi fel),vor fi interpretati in ordinea in care
apar in expresie,de la stanga la dreapta (daca nu exista si paranteze
care sa forteze o alta ordine de evaluare).
In general,este bina sa utilizati expresii cat mai scurte,si cat mai
clare,pentru a evita orice ambiguitate sau confuzie.Expresiile complicate
si alambicate sunt apanajul incepatorilor.
Este bine sa nu utilizati pointeri in formularea expresiilor matematice
decat daca este imperios necesar.Este preferabil sa preluati valorile de
la adresele de memorie inainte de a intra in expresia de calcul.Astfel
puteti evita evantualele "erori de precedenta".Nu uitati ca in versiuni
diferite ale programului compilatorul va utiliza tabele diferite de com-
paratie pentru identificarea elementelor de tip token.Cu formule de calcul
prea savante,riscati sa obtineti rezultate absolut imprevizibile.
Constantele (literals) sunt elemente de program invariabile.Constantele
pot fi de patru tipuri fundamentale: integer,character,floating-point si
string(intregi,caractere,numere in virgula mobila sau siruri de caractere)
Elementele prezentate formeaza limbajul C++,asa cum cuvintele din
dictionar formeaza vocabularul unei anumite limbi.Cu aceste elemente se
pot forma expresii,structuri de date,file si module de program,etc.

CONCEPTE DE STRUCTURARE A LIMBAJULUI

Limbajul C++ este o continuare a limbajului C.Toate regulile din C se


aplica si in C++.In plus,pentru structurile necesare programarii orientate
spre obiecte,C++ a introdus si o terminologie specifica.Pentru a simplifi-
ca explicatiile,principalii termeni utilizati vor fi prezentati pe scurt:
DECALRATION (declaratia) -este procedeul prin care se introduc in program
elemente noi.De cele mai multe ori declaratia coincide cu definitia
si caracterizeaza elementul introdus,dar nu este obligatoriu.In cazul
tipurilor de date abstracte,elementele sunt doar declarate,urmand sa
fie definite ulterior.
DEFINITION (definitia) - este procedeul prin care se specifica toate date-
le necesare prin care se caracterizeaza un anumit element de program
(variabila,functie,clasa,obiect etc).In urma definitiei compilatorul
aloca memoria necesara si genereaza codul necesar,astfel incat ele-
mentele respective devin din abstracte (sau virtuale) elemente reale.
LIFETIME (existenta) - este perioada de timp in care elementul respectiv
exista in program (intre creerea si eliberarea lui).
LINKAGE (legaturi) - specifica legaturile care exista intre elementele
unui program.Legaturile pot fi in cadrul aceluiasi modul (internal
linkage) sau intre elemente situate in module diferite (external
linkage).Uneori,elementele unui modul de program sunt definite in alt
modul de program,sau apeleaza la elemente definite in alt modul.

-11-
NAME (nume,element) - identifica un obiect,o functie,o valoare sau orice
alt element din program.Poate fi un identificator sau un tip de data.
OBJECT - este un tip de data structural in care se pot grupa atat date
din tipuri diferite,sub forma de membri cat si functii si proceduri,
sub forma de metode.Un obiect este o instanta a unei clase.
CLASS (clasa) - este definitia abstracta a unui tip de obiect.Contine
declaratia si definitia obiectului.Prin atribuirea de valori concrete
se formeaza un obiect real (o instanta a clasei respective).Obiectele
care provin din aceeasi clasa pot avea valori diferite (sunt instante
diferite ale aceluiasi tip de data.
SCOPE (vizibilitate) - descrie modulul sau fragmentul de program in care
o anumita data este interpretata cu valoarea declarata.Astfel,datele
declarate global sunt vizibile in tot programul,iar cele locale sunt
vizibile doar in cadrul functiei respective.Pentru obiecte exista
specificatorii de acces: "private","public" si "protected".
VIRTUAL FUNCTIONS -functiile virtuale sunt functii pentru care definitia
exacta urmeaza sa fie specificata la un alt moment dat.Acest gen de
functii sunt declarate in clasele abstracte,pentru a permite definirea
si redefinirea lor in obiectele derivate.

Ca o noutate absoluta a limbajului C++,fata de limbajul C apar notiunile


de clase si obiecte,si conceptul functiilor virtuale.
Obiectele sunt date de tip struct,in care se pot introduce si functii
sau proceduri cu vizibilitate locala.Astfel,se realizeaza un mic modul
de program,care poate functiona ca un mic program independent.In sistemele
cu multiprocesare paralela,mai multe obiecte pot opera simultan,in paralel
ca si cand ar fi programe care se deruleaza simultan.Se pot realiza un
numar practic infinit de astfel de obiecte,cu utilitate mai mica sau mai
mare,in functie de modul in care au fost implementate.Obiectele se pot
grupa in biblioteci de obiecte si pot simplifica substantial munca pro-
gramatorilor.In biblioteci,obiectele sunt arhivate sub forma unui sablon
al obiectului denumit clasa.Clasa contine toate definitiile necesare
pentru realizarea unui obiect,dar nu este un obiect real ci doar un obiect
virtual.Pentru a deveni obiect real,trebuiesc initializate cu valori toate
tipurile de date din interiorul obiectului (in caz contrar se obtin struc-
turi de date nule,care returneaza un mesaj de eroare).Pentru realizarea
operatiilor de initializare,exista de obicei o functie specializata,denu-
mita constructor.Pentru initializarea unui obiect este suficient sa fie
apelat "constructorul" obiectului respectiv.In mod similar,obiectele au
si o functie specializata denumita "destructor",care face toate operatiile
necesare pentru eliberarea memoriei (sterge obiectul din program).
Astfel,cu o simpla comanda,obiectul se construieste singur,sau se dis-
truge singur,in momentul indicat.Prin acest mecanism simplu,programul
poate sa apeleze si sa elibereze un numar mare de obiecte cu un consum
minim de memorie de operare.Ca rezultat,viteza de executie creste foarte
mult iar memoria de operare ramane libera pentru alte operatii.Prin me-
toda clasica,ar fi trebuit ca toate obiectele utilizate sa existe real
in memoria de operare (cu un consum foarte mare de memorie).Prin acest
procedeu tehnic,obiectele sunt arhivate in biblioteci si se materializeaza
(devin o instanta a obiectului) dar in momentul in care sunt necesare in
program,dupa care sunt eliberate pentru a face loc altor obiecte.

-12-
Mai mult decat atat,clasele de obiecte pot fi clase abstracte.In acest
caz,functiile obiectului sunt doar declarate,dar nu saut definite.Defi-
nitia acestor functii se va face in etapa de implementare in functie de
necesitatile de moment.Ca rezultat,se pot obtine instante extrem de
variate ale acestor clase de obiecte.Nu toate functiile unui astfel de
obiect sunt virtuale.Constructorul,destructorul si grupul principal de
functii sunt definite,astfel ca obiectul va fi usor de materializat si
apelat.Se includ functii virtuale doar pentru operatiile care urmeaza
sa fie executate in context diferit sau cu tipuri de date variabile de
la un utilizator la altul.Prin acest mecanism,se asigura o flexibilitate
extrem de mare in modul de apelare si utilizare al acestor clase de
obiecte.Practic,operatiile executate de procesor se reduc la minimum,in
schimb,creste volumul de munca in etapa de proiectare si programare.
Exista un numar destul de mare de unitati care contin astfel de clase
de obiecte(si numarul lor este in continua crestere).Clasele de obiecte
dezvoltate si acceptate de catre firma Microsoft sunt grupate intr-o
biblioteca denumita MFC (Microsoft Foundation Class Library).Daca doriti
sa dezvoltati astfel de biblioteci,respectati cu strictete standardul
de limbaj (si conventiile de notatie).
O alta notiune generatoare de confuzii este cea de vizibilitate(scope).
Vizibilitatea locala se refera la faptul ca un anumit tip de data nu poate
fi utilizat decat in blocul de program pentru care a fost declarat.Cel
mai clar exemplu sunt argumentele unei functii (parametrii).In mod similar
datele declarate in interiorul unei functii nu au vizibiliate in afara
functiei respective.
Etichetele au vizibiliate limitata strict la functia in care sunt
incluse.Nu se poate face salt la o astfel de eticheta din exteriorul
functiei sau din alt modul de program.
Vizibilitatea la nivel de fila de program se numeste vizibilitate glo-
bala.Toate datele declarate in afara functiilor au vizibilitate globala.
Membri unei clase sau cei ai unui obiect se pot apela la fel ca si
membri unei structuri,cu ajutorul operatorului de selectie (punctul . sau
.* si ->* in cazul in care membrii sunt din tipul pointer).Accesul la
membrii unui obiect este determinat si prin specificatorii de acces:
private=vizibilitate locala,public=vizibilitate globala sau protected=
vizibilitate locala si pentru clasele sau functiile declarate friend.
In mod implicit,membrii unui obiect au vizibilitate locala (se pot
apela intre ei dar nu pot fi apelati din afara obiectului.In cazul in
care o clasa sau o functie este declarata "friend",aceasta functie va
putea avea acces si la membri obiectului declarati private sau protected.
Ca rezultat,atunci cand nu exista decat vizibilitate locala,se poate uti-
liza acelasi identificator in mai multe blocuri independente de program
(Exemplu: variabila X poate fi utilizata in doua sau mai multe functii).
Limbajul C++ permite chiar si supraincarcarea identificatorilor.Cu alte
cuvinte,in program pot exista mai multe functii cu acelasi nume,cu condi-
tia sa utilizeze argumente diferite (parametri diferiti).
Exemplu: pot exista doua functii denumite MAX() daca prima opereaza cu
numere intregi Max(n:integer) iar cea de a doua opereaza cu numere in
virgula mobila Max(n:float).Compilatorul va recunoaste si va apela functia
corecta in functie de parametrul sau.Pe cat posibil,se va evita si supra-
incarcarea identificatorilor pentru a nu crea ambiguitati.

-13-
Un program poate fi continut in una sau mai multe file (module).In
urma compilarii,se creaza legaturile necesare dintre file (module) astfel
incat executia programului sa inceapa cu modulul care contine functia
main().Formula generala pentru functia main () este:
int main( int argc[,char *argv[][,char *envp[] ] ]);
In mod curent,toate codurile se introduc in corpul functiei main().Daca
programul contine si functii declarate in afara functiei main(),acestea
vor trebui apelate explicit (nu pot fi executate automatic).
Limbajul C++ accepta toate tipurile de date din limbajul C,la care se
adauga tipul class si toate tipurile derivate (clase derivate din cele
standard,clase obtinute prin combinarea claselor standard,clase nou
definite si inregistrate etc.).
In limbaj C++ se poate face conversia intre diferitele tipuri de date
(Exemplu: int in float,pointer in string etc.).Conversiile intre tipurile
fundamentale de date poarta numele de "Standard Conversions" (conversii
standard).Aceste conversii pot fi efectuate de compilator fara nici un
alt program auxiliar.Pentru conversiile realizate intre tipurile de date
definite de utilizator,este necesar si un program sau un algoritm special
care sa realizeze conversia (nu se poate face automat).
Prin gruparea de cuvinte cheie,identificatori,operatori si punctuatori
se formeaza expresii.Expresiile formate in limbaj C++ pot fi:
1.Expresii primare: -sunt formate dintr-un singur element de limbaj la
care se adauga operatorul scope (::).
2.Expresii postfix: -sunt expresii primare urmate de un operator
3.Expresii unare: -sunt expresii in care operatorii actioneaza asupra unui
operand unic.
4.Expresii binare: -sunt expresii in care operatorul actioneaza asupra a
doi operanzi.
5.Expresii ternare: -sunt expresii in care exista si un operator conditio-
nal,astfel incat operatorul ternar actioneaza asupra celor doi ope-
ranzi doar daca este indeplinita o anumita conditie.
6.Expresii constante: -sunt expresii formate exclusiv din date constante.
7.Expresii cu conversii explicite: -utilizeaza si conversiile standard.
8.Expresii cu pointer spre membrii unui obiect -utilizeaza si pointeri.
9.Expresii de tip casting: -conversii de tip Type-Cast Conversions
10.Expresii tip Run-Time Type Information -sunt expresii in care tipul de
data sau tipul unui anumit obiect este determinat doar in timpul
executiei programului.
Formula generala a unei expresii este de genul:
Specificator Modificator Tip de data Identificator Operator ....

Expresiile se utilizeaza pentru a forma instructiuni si comenzi.Pot fi


identificate mai multe tipuri de instructiuni:
1. Instructiuni expresie :-evalueaza rezultatul unei expresii.
2. Instructiuni nule: -tin locul unei instructiuni viitoare.
3. Instructiuni compuse:-contin expresii complexe ordonate prin paranteze.
4. Instructiuni de selectie: -selecteaza un anumit cod pentru executie.
5. Instructiuni iterative: -determina repetarea unei operatii.
6. Instructiuni de salt: -transfera executia la alta locatie din program .
7. Instructiuni declaratie: -introduc elemente noi in program.
8. Instructiuni de tratare a erorilor: executa exceptiile pentru erori.

-14-
CLASE SI OBIECTE

Acest manual are ca scop prezentarea simplista a conceptului de pro-


gramare orientata spre obiect si a modului de implementare a acestui
concept in cazul limbajului C++.
Obiectele sunt structuri de date complexe,de dimensiuni variabile,
utilizate in programare pentru a permite operatii cu blocuri mari de
date,sau pentru a utiliza un grup de functii specializate care opereaza
asupra unui bloc de date.Nu are rost sa utilizati obiecte,doar de dragul
obiectelor.Exemplele din manual,vor prezenta doar elemente sumare ale
obiectelor si notiunile strict elementare.Pentru informatii suplimentare
puteti consulta manualele de specialitate.
Pentru formarea structurilor de date se pot utiliza tipurile: union,
struct si class.Union si struct au fost prezentate impreuna cu limbajul C
(vezi si manualul: "Limbajul C si C++ ABC-doar ").
Tipul de data class,este asemanator cu tipul struct,dar permite si
includerea unor functii locale in corpul structurii.In mod obisnuit(dar
nu obligatoriu),o astfel de structura de date va include unul sau mai
multi membri banali (diverse tipuri de date) si trei sau mai multe functii
specializate,care opereaza asupra datelor din structura.
Dintre functii,cea care initializeaza obiectul poarta numele de con-
structor iar cea care sterge obiectul din memorie poarta numele de des-
tructor.Restul functiilor executa operatiile necesare asupra datelor.
EXEMPLU: (vezi si clpus4.cpp)
#include <iostream>
#include <dos.h>
class obiect1
{ public: int a; };
int main()
{ obiect1 a;
a.a=10;
cout << "valaorea returnata este: ";
cout << a.a;
sleep(3);
return 0;
}
Exemplul de mai sus contine cel mai simplu obiect posibil.Clasa obiect1
este declarata cu un singur membru,denumit "a",care este o variabila de
tip int.Clasa nu are nici constructor,nici destructor,nici alte functii,
ci doar un singur membru de tip variabila.Practic este aproape identic
cu o variabila de tip int.Pentru a putea utiliza acest tip de data,trebuie
creata o instanta a clasei,adica un obiect.In blocul functiei main() am
declarat on obiect din clasa obiect1 denumit tot a.Acest lucru este po-
sibil deoarece variabila a din clasa obiect1 este locala si nu are vizi-
bilitate in afara structurii.Apoi am utilizat operatorul "punct" pentru
a apela membrul obiectului si am atribuit acestuia o valoare oarecare.
Pentru a afisa pe ecran valoarea atribuita,am utilizat cout si opera-
torul de insertie in stream-ul de iesire.
Acest exemplu prezinta cea mai simpla operatie cu o data de tip obiect.
Chiar si in aceasta forma elementara,obiectul a respecta toate regulile
si toate conventiile referitoare la clase si obiecte.

-15-
Prin declararea unei clase noi,se introduce in program un nou tip de
data.Numele fiecarei clase trebuie sa fie unic,cel putin pentru programul
in care se va utiliza clasa respectiva.O clasa poate fi inclusa in alta
clasa,sub forma de membru al clasei respective.In acest caz,clasa va
avea vizibilitate doar in interiorul clasei in care este continuta.Dupa
delcararea unui clase,aceasta va funtiona ca orice alt tip de data din
program.Prin declararea unei variabile din tipul respectiv de data se
obtine o instanta a clasei respective,adica un obiect.
Membrii unei clase pot fi: date simple din orice tip,functii,alte clase,
enumerari,campuri de tip BitMap,clase si functii de tip friend sau tipuri
de date nou declarate (fara instante materializate).
Daca membrul unei clase este de tip functie,se va putea apela la fel
ca orice alt membru al clasei prin operatorul punct (.) sau (->).Daca o
clasa contine mai multe functii,acestea se vor putea apela intre ele
direct,fara a specifica numele clasei si operatorul de selectie,dar numai
in interiorul clasei respective.Pentru a putea fi apelate si din exterio-
rul obiectului,functiile trebuiesc declarate cu specificatorul de acces
"public".Exceptie fac functiile si clasele declarate "friend",care vor
putea avea acces si la mebrii protejati ai claei respective.
Declararea unui membru de tip functie se face la fel ca si pentru membrii
de tip data:
EXEMPLU: (vezi si cplus5.cpp)
#include <iostream>
#include <conio.h>
class obiect1
{ public: int a;
int arie()
{ a=a*a;
return a;
};
}
int main()
{
obiect1.b;
b.a=7;
b.a=b.arie();
cout << "membrul apelat are valoarea: \n";
cout << b.a;
getch();
}
In exemplu de mai sus,clasa obiect1 are un membru simplu si un membru de
tip functie.Am declarat obiectul b din clasa obiect1 si am initializat
valoarea lui a(deoarece clasa nu are un constructor implict).Apoi am
apelat functia si am returnat valoarea obtinuta.
Pentru a simplifica operatiile asupra obiectelor,se obisnuieste ca
fiecare clasa sa contina si doua functii denumite constructor si respectiv
destructor.Constructorul are rolul de a initializa datele din obiectul
declarat,iar destructorul are rolul de a efectua operatiile de eliberare
a memoriei,in momentul in care obiectul nu mai este necesar.Constructorul
este apelat automat in momentul in care este creat un obiect din clasa
respectiva,astfel incat nu mai este necesar apelul explicit.

-16-
Pentru declararea constructorului se utilizeaza acelasi nume ca si
pentru clasa sa.Orice functie declarata cu numele clasei va fi interpre-
tata de compilator drept constructor al clasei respective.O clasa poate
avea unul sau mai multi constructori.Pentru a face diferenta dintre
acestia,este necesar sa utilizeze parametrii diferiti (pentru a permite
procedeul de supraincarcare a identificatorilor).Datorita faptului ca au
identificator comun cu cel al clasei lor,apelul constructorilor in afara
clasei se va face cu operatorul de scope :: prin care se specifica faptul
ca se doreste accesul la variabila globala cu numele respectiv.
Atunci cand o clasa are constructor,orice obiect derivat din clasa
respectiva va apela automat constructorul pentru a fi initializat cu
valorile respective(in momentul crearii).
EXEMPLU: ( vezi si cplus6.cpp )
#include <iostream>
#include <conio.h>
class obiect1
{ public: int a;
obiect1(){ a=9; };
};
int main()
{
obiect1 b;
cout << "membrul apelat are valoarea: \n";
cout << b.a;
getch();
}
In exemplul de mai sus,se observa ca variabila a este gata initializata
in momentul formarii obiectului b si poate fi utilizata direct,ca atare.
In mod curent,constructorul nu este apelat decat in momentul in care se
construieste un obiect din tipul respectiv.Daca in clasa respectiva exista
un numar mare de date,din tipuri diferite,se poate utiliza cate un con-
structor diferit pentru fiecare tip de data,astfel incat obiectul derivat
sa contina toate datele gata initializate.
In mod curent,un obiect poate fi creat global (in afara functiilor),
local (in interiorul unei functii sau a unui modul),dinamic (cu ajutorul
operatorului New),temporar (cu existenta efemera),sub forma de membru al
unei alte clase,sau ca obiect drivat din alta clasa ( la care se adauga
date si functii noi).
Pentru obiectele utilizate doar de d-voastra,nu este necesar sa res-
pectati toate regulile de administrare a memoriei.Daca realizati insa
clase de obiecte si biblioteci DLL care contin clase de obiecte,pe care
doriti sa le poata utiliza si alti programatori,este esential ca obiectele
create din clasa respectiva sa fie initializate (aplicabile direct) si
respectiv sa fie eliberate complet din memorie cu ajutorul destructorului.
Destructorul este cealalta functie speciala a unei clase,utilizata tot
pentru organizarea si administrarea memoriei.Destructorul are rolul invers
fata de constructor,adica desface obiectul in datele componente si ape-
leaza functiile necesare pentru eliberarea memoriei.Destructorul se decla-
ra prin conventie,tot cu numele clasei,dar precedat de semnul tilda (~).
Daca nu exista un destructor declarat,compilatorul va crea un destructor
implicit care este apelat automat la eliberarea obiectului.

-17-
EXEMPLU: (vezi si cplus7.ccp)
#include <iostream>
#include <conio.h>
class obiect1
{ public: int a;
obiect1() { a=9; };
~obiect1() { a=0; );
};
int main()
{
obiect1 b;
b.~obiect1();
cout << "membrul apelat are valoarea: \n";
cout << b.a;
getch();
}
In exemplul de mai sus,constructorul initializeaza a la valoarea 9 iar
destructorul aduce a la valoarea zero.Exemplul prezinta si modul de
apelare a destructorului.In mod normal,destructorul nu este apelat ca
o functie oarecare ci este apelat pentru a elibera memoria.In exemplul
de mai sus,pentru a elibera memoria se poate utiliza o functie de genul
free() sau delete().In acest caz,dupa apelarea destructorului nu se
mai pot apela membrii obiectului deoarece se va returna un mesaj de
eroare (valoare nedefinita).
Daca obiectul este complex,se pot utiliza etape succesive pentru
dealocarea fiecarui membru.
Nici constructorul si nici destructorul nu returneaza valori.Nu se
utilizeaza acest tip de functii pentru operatii obisnuite (chiar daca
este posibil) pentru a nu deruta compilatorul si pentru ca programele sa
poata fi verificate si depanate automat(exista programe de analiza care
apeleaza automat constructorul sau destructorul obiectelor pentru a
verifica ce efect au asupra memoriei de operare).
Este posibil ca un constructor sa fie doar declarat,iar definitia sa
fie introdusa in afara clasei.In acest caz,trebuie utilizat operatorul
scope (::) pentru a face distinctia dintre clasa si constructorul sau:
EXEMPLU: ( vezi si cplus8.cpp )
#include <iostream>
#include <conio.h>
class obiect1
{ public: int a;
obiect1();
};
obiect1::obiect1() { a=33; };
int main()
{ obiect1 b;
cout << "membrul apelat are valoarea: \n";
cout << b.a;
getch();
}
Acest gen de declaratie,urmat de definitie,este utilizat mai ales atunci
cand definitia este destul de lunga (pentru a vedea unitar tot obiectul).
-18-
Orice declaratie si definitie inclusa in constructor va fi executata
la crearea fiecarui obiect.Din acest motiv,este bine ca expresiile uti-
lizate in constructor sa fie cat mai scurte si sa introduca doar datele
absolut necesare.Nu se vor utiliza comentarii sau explicatii si operatii
asupra valorilor initializate.Daca este necesar,se va adauga o functie
speciala,pentru a executa operatia respectiva:
EXEMPLU: (vezi si cplus9.cpp)
#include <iostream>
#include <conio.h>
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 };
}
int main()
{
obiect1 numar;
numar.c=numar.fun1();
cout << "membrul apelat are valoarea: \n";
cout << numar.c;
numar.~obiect1();
getch();
}
In mod similar,pentru un obiect care opereaza cu date de tip text:
EXEMPLU: (vezi si cplus10.cpp )
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <conio.h>
class obiect1
{ public: char *text;
obiect1() { text="Cuvant_de_control"; };
~obiect1() { text=""; };
};
int main()
{
obiect1 t1;
cout << "Introduceti un cuvant oarecare: \n";
cin >> t1.text;
cout << "cuvantul inversat este: \n";
cout << strrev(t1.text);
cout << " \n\n Apasati orice tasta ! ";
getch();
t1.~obiect1();
}
In mod similar,puteti dezvolta obiecte complexe,pentru efectuarea de
operatii matematice sau pentru prelucrarea textelor.Este bine sa verifi-
cati intotdeauna daca destructorul a eliberat complet memoria.Nu va bazati
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 <iostream>
#include <conio.h>
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 <iostream>
#include <conio.h>
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 <iostream>
#include <conio.h>
template <class type1,class type2>
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 T,int i> 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 <iostream>
#include <conio.h>
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 <iostream>
#include <conio.h>
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 <iostream>
#include <conio.h>
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 <iostream>
#include <conio.h>
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
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 <iostream>
#include <conio.h>
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 <iostream>
#include <conio.h>
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 <iostream>
#include <conio.h>
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 <iostream>
#include <conio.h>
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 <stdio.h>;
#include <dos.h>; };
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 <stdio.h>;
#include <conio.h>; }
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: <bitset>,<dequeue>,
<list>,<map>,<queue>,<set>,<stack>,<vector>.
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<tipul de data> identificator;
EXEMPLU: vector<int> 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
mii de inregistrari.In toate situatiile in care se doreste accesul aleator
la un membru al conteinerului se va prefera tipul vector.Tipul stiva se
va utiliza preferential atunci cand nu se executa decat operatii care
intereseaza intreaga serie de obiecte din container (stiva ocupa mai putin
spatiu de memorie si nu se preteaza la erori de adresare).

-33-
Cel mai simplu container contine doar valori de tip numeric:
EXEMPLU: (vezi si cplus24.cpp )
#include <iostream>
#include <vector.h>
#include <conio.h>
using namespace std;
vector<int> v;
int main()
{
randomize();
cout << "Elementele vectorului sunt: ";
for (int i=0;i<10;i++)
{ int num=(int) rand() %100;
cout << num << " ";
v.push_back(num);
};
cout <<"\n Vectorul contine: " << v.size() << " elemente";
cout <<"\n Primul element este: " << *v.begin();
cout <<"\n Localizat la adresa: " << v.begin();
cout <<"\n Al saptelea element este: " << v.at(6);
cout <<"\n Ultimul element este: " << *v.end();
cout <<"\n Localizat la adresa: " << v.end();
getch();
}
Primul element din container este arhivat la adresa zero.Astfel,atunci
cand se apeleaza un element oarecare prin metoda at() se va specifica
valoarea (n-1) pentru numarul de ordine al elementului.Observati ca datele
de tip INT sunt arhivate in format de 16 de biti (2 bytes). Diferenta
dintre adresa ultimului element si cea a primului element va fi de 20 de
bytes.
Pentru operatiile asupra mebrilor se utilizeaza iteratorii (pointeri).
Pe langa metodele proprii ale fiecarui tip de container,se pot efectua
operatii asupra datelor din container si cu ajutorul unor algoritmi pre-
definiti in biblioteca STL,intr-o fila denumita <algorith.h>.Fiecare
fila care defineste containerul incarca automat in memorie si fila
<algorith.h>,astfel incat toti acesti algoritmi sunt accesibili in orice
moment.Ca rezultat,un container este incomparabil mai versatil decat un
obiect similar de tip struct sau class,in care fiecare algoritm trebuie
redefinit pentru a putea opera asupra membrilor.
In exemplul precedent am utilizat o functie repetitiva simpla,pentru
a genera zece valori numerice aleatoare,pe care le-am introdus in con-
tainer cu ajutorul metodei push_back().
In continuare,putem efectua diverse operatii automate de cautare si/sau
sortare a elementelor.De exemplu,putem cauta primul element care respecta
o anumita conditie,sau ultimul element care respecta o anumita conditie.
Deasemenea,putem sa excludem selectiv o parte dintre membri si putem sa
formam un nou container,care va contine doar elemenetele selectate.
De exemplu,pentru a cauta primul element par din seria de numere,va
trebui scris un algoritm care parcurge containerul incepand cu primul ele-
ment si imparte la 2 fiecare element.Daca rezultatul este tot un numar de
tip INT,numarul este par (numerele impare returneaza zero ).

-34-
EXEMPLU: (vezi si cplus25.cpp )
#include <iostream>
#include <vector.h>
#include <conio.h>
using namespace std;
vector<int> v;
int main()
{
randomize();
cout << "Elementele vectorului sunt: ";
for (int i=0;i<10;i++)
{ int num = random(100);
cout << num << " ";
v.push_back(num); };
vector<int>::iterator iter1 = v.begin();
while(iter1 != v.end() && *iter1 %2 !=0){iter1++};
cout << "\n Primul numar par este: " << *iter1;
vector<int>::iterator iter2 = v.end();
do {iter2--}
while(iter2 != v.begin() && *iter2 % 2 != 0 );
cout << Ultimul numar par este: " << *iter2;

cout << "\n vectorul nou este: \n";


if ( iter1 != v.end() && iter2 != v.begin() }
{ vector <int> v2(iter1,iter2);
for(int i=0;i<v2.size();i++){ cout <<v2.at(i) << " ";}};
}
Observati ca am declarat doi iteratori,cu care declarat doi algoritmi de
cautare a primului si a ultimului numar par.Pentru a sorta datele din
container se poate utiliza algoritmul de sortare sort()(din <algorith.h>)
EXEMPLU: (vezi si cplus26.cpp )
#include <iostream>
#include <vector.h>
#include <conio.h>
using namespace std;
vector <int> v;
int main()
{
randomize();
cout << "Elementele vectorului sunt: ";
for (int i=0;i<10;i++)
{ int num=(int) rand() %100;
cout << num << " ";
v.push_back(num); };
sort(v.begin(),v.end() );
cout << "\n Dupa sortare,elementele sunt: \n";
for (int i=0;i<10;i++) cout << v.at(i) << " ";
getch();
}
Sortarea s-a facut prin apelul unei singure proceduri simple.

-35-
Pentru a utiliza structuri de date mai complexe,se declara tipul de
data,apoi un vector din tipul respectiv:
EXEMPLU: (vezi si cplus27.cpp )
#include <iostream>
#include <vector.h>
#include <conio.h>
using namespace std;
struct date { int varsta;
char *nume; };
date d1,d2,d3,d4;
vector<date> v;
int main()
{
d1.varsta=65;d1.nume="Ion";
d2.varsta=73;d2.nume="Vasile";
d3.varsta=44;d3.nume="Marin";
d4.varsta=22;d4.nume="Mihai";
v.push_back(d1);v.push_back(d2);
v.push_back(d3);v.push_back(d4);
cout << "datele arhivate sunt: \n\n";
for(int i=0;i<4;i++)
{ cout << v.at(i).nume<<" "<<v.at(i).varsta << "\n";};
getch();
}

Tipul de conteiner: list

METODE (allocator_type,assign,back,begin,clear,const_iterator,const_refe-
rence,const_reverse_iterator,difference type,empty,end,erase,
front,get_allocator,insert,iterator,list,max_size,merge,pop_back,
pop_front,push_back,push_front,rbegin,reference,remove,remove_if,
rend,resize,reverse,reverse_iterator,size,size_type,sort,splice,
swap,unique,value_type )
EXEMPLU: (vezi si cplus28.cpp )
#include <iostream>
#include <list.h>
#include <conio.h>
using namespace std;
typedef list<int> v;
int main()
{ v lista; v::iterator numar; randomize();
for (int i=0;i<10;i++) { lista.push_front(random(100)) ; };
cout << " datele arhivate sunt: \n\n";
for (numar=lista.begin();numar != lista.end();numar++)
cout << *numar << " ";
cout << " \n datele sortate sunt: \n\n";
lista.sort();
for (numar=lista.begin();numar != lista.end();numar++)
cout << *numar << " ";
getch()
}

-36-
Tipul list arhiveaza datele sub forma de lista.Se pot utiliza la fel
ca si vectorii.Prin comparare cu tipul vector,tipul list are timpii de
insertie si excludere a datelor mai scurti,dar are timpul de acces la
fiecare membru mai lung.Pentru aplicatiile in care arhiva se schimba
foarte frecvent prin introducerea si stergerea datelor,se va prefera tipul
list,iar pentru cele in care datele vor fi doar consultate frecvent,se va
prefera tipul vector.Tipul list nu are functia at().Ca rezultat apelarea
datelor se va face obligatoriu cu ajutorul unui iterator (sau pointer).
Elementele incluse pot avea dimensiuni variabile iar deplasarea se poate
face bidirectional.

Tipul map

METODE ( allocator_type,begin,clear,const_iterator,const_reference,count,
const_reverse_iterator,difference_type,empty,end,equal_range,
erase,find,get_allocator,insert,iterator,key_comp,key_compare,
key_type,lower_bownd,map,max_size,operator[],rbegin,reference,
referent_type,rend,reverse_iterator,size,size_type,swap,
upper_bound,value_comp,value_compare,value_type )
EXEMPLU: (vezi si cplus29.cpp )
#include <iostream>
#include <map.h>
#include <conio.h>
using namespace std;
typedef map<int,int,less<int> > m1;
int main()
{
m1 zi;
m1::iterator it1;
zi[1]=35;
zi[2]=2;
zi[3]=44;
zi[4]=17;
zi[5]=55;
zi[6]=23;
zi[7]=15;
cout << "\n Conteinerul poate accepta: " <<zi.size() <<" perechi";
cout << "\n Conteinerul contine urmatoarele perechi: \n\n";
for (int i=0;i<8;i++)
{ it1=zi.find(1);
cout << (*it1).first << " " << (*it1).second << "\n";
};
getch();
}
Tipul map opereaza cu colectii de date sortate asociativ sub forma de
perechi de tipul key/value (cod/valoare).Astfel datele de un anumit tip
pot fi sortate in functie de cel de al doilea tip.In versiunile mai noi
de program (care accepta si tipul "string" ) se utilizeaza pentru perechi
de tipul string/int sau string/float etc.Primul element din pereche
trebuie sa fie de tip constant ( type pair <const Key, T> ) si va fi uti-
lizat pentru sortarea celui de al doilea element.
-37-
In tipul map se pot introduce sau sterge elemente,la orice pozitie.
Introducerea unui element nou nu invalideaza iteratorii preexistenti,iar
stergerea unui element nu invalideaza decat iteratorul spre elementul
sters (restul raman valabili).Accesul la membri se poate face doar prin
iteratori (pointeri).In tipul map,elementul Key trebuie sa fie unic.In
tipul multimap elementul Key nu este obligatoriu sa fie unic (pot exista
doua sau mai multe perechi in care key este diferit dar contin aceeasi
valoare).In rest,multimap este identic cu map.

Tipul deque (double ended queue)

METODE ( allocator_type,assign,at,back,begin,clear,const_iterator,
const_reference,const_reverse_iterator,deque,difference_type,empty
end,erase,front,get_allocator,insert,iterator,max_size,operator[],
pop_back,pop_front,push_back,push_front,rbegin,reference,rend,
resize,reverse_iterator,size,size_type,swap,value_type )
EXEMPLU: (vezi si cplus30.cpp )
#include <iostream>
#include <deque.h>
#include <conio.h>
using namespace std;
typedef deque<char> text1;
void scrie(deque<char> n);
void main()
{
text1 a(5,'A');
scrie(a);
a.insert(a.begin(),'X');
scrie(a);
a.insert(a.end(),'Y');
scrie(a);
a.insert(a.begin()+4,3,'M');
cout<< "\n\nDimensiunea stivei este: " <<a.size() <<"\n";
cout<< "Dimensiunea maxima a stivei este: "<<a.max_size()<<"\n";
scrie(a);
getch();
}
void scrie(deque<char> n)
{
text1::iterator it1;
text2::reverse_iterator it2;
cout << "\n Elementele stivei sunt: \n";
for (it1=n.begin();it1 != n.end(); it1++ )
cout << *it1 << " ";
cout << "\n In ordine inversa sunt: \n";
for (it2=n.begin();it2 != n.rend(); it2++)
cout << *it2 << " ";
}
Tipul deque nu trebuie confundat cu tipul queue.Chiar daca este tot
sub forma de stiva,stiva cu doua capete seamana mai mult cu tipul vector.
Accesul este aleator,la orice membru,inclusiv prin functia at() sau prin
-38-
iteratori si pointeri.Insertia este mai rapida la capetele stivei si mai
lenta in mijlocul stivei.Inserarea unui element nou invalideaza itera-
torii existenti.Pentru a afisa datele in mod repetat,am definit o functie
speciala in care am utilizat atat iteratorul incremental cat si pe cel
decremental.Observati ca pentru citirea prin decrementare se utilizeaza
rbegin() si rend(),astfel incat datele sa fie parcurse de la celalalt
capat al stivei.Se poate utiliza in aceleasi situatii ca si tipul vector,
dar cu particularitatile pe care le prezinta tipul stiva.Pentru accesul
rapid la membri se va prefera intotdeauna tipul vector.Acest tip de data
este recomandabil mai ales pentru compatibilitatea cu programele foarte
vechi,in care datele sunt arhivate in stive si pot fi preluate foarte
usor cu ajutorul unei alte stive.

Tipul set

METODE (allocator_type,begin,clear,const_iterator,const_reference,
const_reverse_iterator,count,difference_type,empty,end,equal_range
erase,find,get_allocator,insert,iterator,key_comp,key_compare,
key_type,lower_bound,set,max_size,rbegin,reference,rend,
reverse_iterator,size,size_type,swap,upper_bound,value_comp,
value_compare,value_type )
EXEMPLU: ( vezi si cplus31.cpp )
#include <iostream>
#include <set.h>
#include <conio.h>
using namespace std;
typedef set<int,less<int> > set1;
int main()
{
set1 s1;
set1::reverse_iterator it1;
set1::iterator it2;
s1.insert(333);
s1.insert(17);
s1.insert(54);
s1.insert(888);
cout << "\n valorile in ordine inversa sunt: \n";
for (it1=s1.rbegin();it1 != s1.rend(); it1++)
cout << *it1 << " ";
cout << "\n valorile in ordinea introducerii: \n";
for (it2=s1.begin();it2 != s1.end(); it2++)
cout << *it2 << " ";
getch();
}
Tipul set este asemantor cu tipul map,dar contine serii de elemente unice
de dimensiune variabila.Elementele ( type const Key ) sunt de tip Key,dar
spre deosebire de tipul map,fiecare element este si key si value.Ca re-
zultat sortarea se va face utilizand valoarea elementului.Se pot introduce
sau sterge elemente la orice pozitie.Timpul per operatie depinde de nu-
marul de elemente din serie (logarthmic time).Insertia de elemente nu
invalideaza iteratorii preexistenti.Stergerea invalideaza doar iteratorul

-39-
elementului eliminat.Pentru tipul set,fiecare element trebuie sa fie unic
(nu pot exista doua elemente cu aceeasi valoare).Tipul multiset permite
mai multe elemente identice,astfel incat pot exista mai multe inregistrari
cu aceeasi valoare.

Restul tipurilor de conteinere au o valoare mai limitata si se utilizeaza


mai ales pentru compatibilitatea cu programele mai vechi,in care datele
au fost arhivate in astfel de structuri.Nu se vor prezenta exemple,deoa-
rece nu se recomanda utilizarea acestor tipuri pentru aplicatiile dez-
voltate in etapa actuala (mecanismul de tip LIFO si FIFO executa un
numar extrem de mare de operatii inutile,care uzeaza procesorul fara nici
o justificare moderna).

Tipul stack
METODE (empty,pop,push,size,top)
Tipul stack(stiva) utilizeza mecanismul LIFO (Last In First Out).Fiecare
element trebuie introdus cu PUSH si poate fi scos cu POP.Nu se poate
ajunge la un element central decat dupa ce au fost scoase toate elemente-
le anterioare (capul stivei).Elementele care urmeaza dupa cel selectat
formeaza coada stivei.Pentru operatii se utilizeaza o stiva auxiliara.

Tipul queue
METODE (back,empty,front,pop,push,size,top)
Tipul queue este asemanator cu stack dar utilizeaza mecanismul FIFO
(First In First Out).Fiecare element trebuie introdus cu PUSH si poate fi
extras cu POP.Primul element introdus este si primul care va fi extras.
Stiva a fost reordonata automat cu ajutorul unei stive auxiliare.

Tipul bitset
METODE (any,count,flip,none,reset,set,size,test,to_string,to_ulong)
Contine secvente de biti.Grupurile de biti se pot manipula cu operatorii
binari AND,OR etc.Grupurile de biti se pot si formata sub forma de siruri
de caractere sau de numere de tip unsigned INT daca se apeleaza cele
doua metode specializate "to_string" si "to_ulong".Acest tip de data
este foarte util pentru programele de analiza ("parsing") si permite o
abordare complexa a fiecarui grup de biti dintr-un program.Poate fi
utilizat cu succes si pentru a scrie programe care formateaza datele sau
convertesc un format in alt format de date.Nu se recomanda incepatorilor.
Orice eroare elementara prin care "se pierde sau se adauga un bit " poate
determina transformari ireversibile.Se poate utiliza si pentru aplicatiile
care opereaza cu imagini digitale,sunete,arii Bitmap etc. dar cu prudenta
necesara pentru operatiile "la nivel inferior".

Toate containerele au acces si la fila <algorith.h> care contine un


grup de circa 70 de algoritmi predefiniti (dintre care sort() a fost
utilizat in exemple).Acesti algoritmi acopera majoritatea operatiilor ce
pot fi executate asupra datelor din structuri.Consultati si acesti algo-
ritmi inainte de a va lansa in operatii complicate de definire a unor
algoritmi noi.Este foarte probabil sa gasiti exact ceea ce aveti nevoie
si sa scutiti astfel un efort inutil.Daca realizati algoritmi valorosi,
de interes general,arhivati acesti algoritmi intr-o fila similara.

-40-
La fel ca si containerele,filele sunt structuri ordonate de date,cu un
nume identificator distinct,organizate sub forma de documente si utilizate
pentru a stoca grupuri mari de date.Datele continute intr-o fila pot fi
de acelasi tip (Exemplu : fila tip text),sau pot fi organizate in doua sau
mai multe tipuri de date distincte.Sistemele de operare si programele di-
ferite,vor genera o mare varietate de file cu continut si extensie dife-
rita de la un program la altul.Astfel de file pot fi: file tip text,tabele
si baze de date tip Fox,Oracle sau Excel,file tip hipertext(HTML),file
care contin date binare(binary data),file care contin arii de biti(file
tip BitMap.bmp),diverse tipuri de imagini digitale(.jpg sau .avx),file
care contin inregistrari muzicale sau sunete(.wav,.mp3) etc.Programul C++
poate deschide orice tip de fila,dar prelucrarea si interpretarea datelor
trebuie sa fie facuta cu ajutorul unor obiecte care recunosc formatul in
care au fost editate datele.
Nu exista o solutie unica.Cu ajutorul functiilor si al obiectelor pre-
definite se pot concepe o larga varietate de solutii,in functie de tipul
de data analizat si de necesitatile de moment.In plus,in permanenta apar
noi biblioteci de clase si obiecte,care sunt specializate pentru operatii
cu file de un anumit fel.Turbo C++ nu contine obiecte specializate pentru
filele HTML,.JPG.AVI etc. dar poate deschide si prelua datele din orice
tip de fila.Pentru interpretarea lor trebuie sa definiti obiectele si
procedurile care prelucreaza datele.Visual C++ (prin MFC) contine deja
o serie de obiecte specializate.De exemplu,pentru fielele de tip HTML
exista clasa CHtmlView care este conceputa special pentru a putea edita
file de tip HTML prin care programele scrise in C++ "posteaza" informatii
pe Internet.Exista si o serie intreaga de biblioteci de obiecte create
special pentru operatii cu si asupra filelor specializate.
Programul C++ ofera solutii diferite pentru filele de tip DOS,fata de
cele de tip Windows.Prima solutie posibila este sa utilizati functiile
mostenite din C (stdio.h):
EXEMPLU: (vezi si cplus32.cpp )
#include <stdio.h>
#include <conio.h>
main()
{ FILE *fila1;
char buf[180];
fila1=fopen("TEXTC.txt","r");
rewind(fila1);
while ( ! feof(fila1) )
{ fread(buf,180,1,fila1);
printf("%s \n",buf);
}
fclose(fila1);
getch();
return 0;
}
In exemplul de mai sus,fila TEXYC.txt este o fila oarecare in care am
introdus un text exemplificativ si pe care am arhivat-o in acelasi direc-
tor cu aplicatia.Pentru a citi o fila situata in alt director,se va uti-
liza calea completa de acces (gen "C:\\calea de acces\numele filei" ).
Am utilizat stream-ul FILE si un tampon de memorie de tip char.

-41-
Solutia este simpla si eficienta pentru filele de tip text care nu
contin decat date de tip text si nu necesita nici un fel de prelucrare.
In C stream-ul este un tampon de memorie simplu,in care datele vor fi
preluate caracter cu caracter.
Solutia prezinta si o serie de inconveniente.Astfel,daca fila preluata
contine si date de tip numeric sau de tip data calendaristica,functia
printf() nu va putea prelua aceste date.Astfel,o simpla scrisoare datata
poate bulversa "programul de citire".Se pot adauga o serie intreaga de
IF -uri in care sa se trateze toate variantele posibile,dar aplicatia va
contine un numar mare de coduri inutile si va fi destul de laborioasa.
C++ spre deosebire de C utilizeaza stream-uri organizate sub forma
de obiecte.Pe langa tamponul de memorie utilizat pentru preluarea datelor,
streamu-urile din C++ contin si o serie de metode proprii,operatori de
preluare sau postare a datelor etc.Ca rezultat,stream-urile I/O din C++
pot accepta toate tipurile standard de date din C++,fara nici o alta
operatie de conversie si contin constructorul,destructorul si functiile
necesare pentru alocare si dealocarea memoriei.Astfel,pentru operatiile
de intrare/iesire(I/O),C++ contine o biblioteca specializata denumita
"iostrem" in care sunt definite stream-uri specializate pentru fiecare
tip de operatie.Pentru operatiile de preluare a datelor sunt definite
clasele istream,ifstream si istrstream din care se pot deriva sub forma
de obiecte streamu-urile cu care se va lucra in program.
Istream este destinat pentru operatii de intrare secventiale sau aleatorii
si contine constructorul istream(),destructorul ~istream() si metodele:
ipfx(),isfx(),get(),getline(),read(),ignore(),peek(),gcount(),eatwhite(),
putback(),sync(),seekg(),tellg() precum si operatorul >>.
Ifstream este o varianta asemenatoare si este specializat pentru operatii-
le de preluare a filelor de pe unitatea de disc (C:).Contine constructorul
ifstream(),destructorul ~ifstream(),metodele: open(),close(),setbuf(),
setmode(),attach(),rdbuf(),fd() si is_open().
Istrstream este specializat pentru operatii cu date structurate sub forma
de arii de caractere.Pentru a construi un astfel de obiect(stream) trebuie
alocata o arie de date de tip caracter ce va fi utilizata pe post de
tampon pentru preluarea datelor (trebuiesc specificate coordonatele ariei)
Contine constructorul istrstream(),destructorul ~istrstream,metodele:
rdbuf() si str().
Toate cele trei tipuri de stream-uri se utilizeaza pentru acelasi scop,
dar contin un grup diferit de metode proprii prin care pot sa actioneze
specializat asupra datelor din filele preluate.In functie de necesitati,
se va utiliza obiectul care are metodele cele mai adecvate.
In mod similar,pentru operatiile de iesire a datelor exista trei tipuri
de clase specializate: ostream,ofstream si ostrstream,din care se pot de-
riva obiecte specializate:
Ostream este obiectul standardizat pentru operatii succesive sau aleatorii
de iesire a datelor.Are atasat un obiect derivat din clasa streambuf cu
care lucreaza sincron.Obiectul derivat din ostrem realizeaza formatul iar
cel derivat din streambuf realizeaza operatiile de nivel inferior (bit cu
bit).Contine constructorul ostrem(),destructorul ~ostream() si metodele:
opfx(),osfx(),put(),write(),flush(),seekp(),tellp() precum si operatorul
de export a datelor <<.Operatorul "<<" este supraincarcat astfel incat
este similar cu cel utilizat impreuna cu "cout".

-42-
Ofstream este specializat pentru a scrie file pe disc.Contine constructo-
rul ofstream(),destructorul ~ofstream(),metodele: open(),close(),setbuf(),
setmode(),attach(),rdbuf(),fd() si is_open().
Ostrstream scrie datele sub forma de arii de caractere.Pentru a preciza
formatul se va aloca o arie de caractere utilizata ca sablon la scrierea
datelor.Obiectul lucreaza sincron cu un obiect de tip strstreambuf,care
utilizeaza un pointer pentru a avansa pas cu pas dupa scrierea fiecarei
arii.Contine constructorul ostrstream(),destructorul ~ostrstream() si
metodele: pcount(),rdbuf() si str().
Exista si stream-uri specializate atat pentru operatii de intrare cat
si pentru operatii de iesire.Aceste stream-uri acumuleaza toate metodele,
de la ambele tipuri de stream-uri si pot fi utilizate pentru ambele tipuri
de operatii.Sunt cele mai practice si cele mai frecvent utilizate (chiar
daca ocupa putin mai multa memorie).Cele doua tipuri de stream-uri care
executa atat intrari cat si iesiri sunt: fstream si strstream.
Fstream executa orice operatie de intrare iesire.Utilizeaza pe post
de tampon un obiect de tip filebuf. Contine: constructor,destructor si
metodele: open(),close(),setmode(),attach(),fd() si is_open().Este cel
mai prectic si cel mai usor de utilizat.
EXEMPLU: (vezi si cplus33.cpp )
#include <fstream.h>
#include <conio.h>
main()
{ fstream fila1("TEXTC.txt",ios::in);
char buf[180];
while ( ! fila1.eof() )
{ fila1.getline(buf,200);
cout << "\n" << buf;
}
fila1.close();
getch();
return 0;
}
Observati ca este la fel de usor de editat ca si exemplul scris in C.In
plus,prezinta o serie de avantaje.Metoda cout si operatrorul << vor
prelua din fila orice tip de date (inclusiv float,double,long,date etc.).
Nu mai este necesara fila header "stdio.h".Obiectul derivat contine si
toate metodele necesare pentru preluarea datelor.Daca nu se vor mai
executa nici un fel de operatii asupra filei,corect este sa apelam si
destructorul ~fila1() pentru a elibera complet memoria de operare.
Pentru declararea obiectului de tip fstream am utilizat numele filei
ce urmeaza sa fie alocata in tamponul de memorie si modul in care urmeaza
sa fie utilizata fila respectiva.Pentru formarea obiectului am apelat
direct constructorul (odata cu declararea obiectului).
Constructorul fstream() are urmatoarea forma generala:
fstream ( const char* szName,int nMode,int nProt=filebuf::openprot); unde
szName -contine calea de acces la fisier si numele filei care urmeaza
sa fie deschisa.Daca fila este in acelasi director,se poate
utiliza doar numele fieli si extensia (ca in exemplu).
nMode -sunt enumeratori ai obiectului ios.Se pot combina prin opera-
torul binar OR.Ios este ancestorul tuturor stream-urilor I/O.

-43-
Parametrul nMode accepta urmatoarele valori:
ios::app -fiecare byte nou scris in fila va fi adaugat la sfarsitul
filei (append) chiar daca pozitia cursorului a fost mo-
dificata cu o alta functie
ios::ate -primul byte nou va fi scris la sfarsitul filei,dar urmatorii
se vor scrie la pozitia curenta.Daca se deplaseaza cursorul
cu ostream::seekp fila poate fi suprascrisa
ios::in -este operatorul pentru input.Suprascrie toate datele
ios::out -este operatorul pentru output.Permite citirea datelor
ios::trunc -sterge toate datele dar pastreaza fila (o trunchiaza)
ios::nocreate -returneaza eroare daca fila precizata nu exista
ios::noreplace -returneaza eroare daca exista fila precizata
ios::binary -deschide fila in modul binar (citeste biti in loc de
caractere ASCII -implicit fila este deschisa in mod text)

nProt -este specificatorul pentru modul de protectie.Accepta valorile:


filebuf::openprot -este valoarea implicita a sistemului de operare
filebuf::sh_none -nu permite partajarea filei (no sharing)
filebuf::sh_read -permite partajarea doar pentru citirea datelor
filebuf::write -permite partajarea filei pentru scriere
-daca nu se specifica se utilizeaza valoarea implicita
Obiectul de tip fstream contine inca doi constructori supraincarcati,care
pot fi utilizati optional,in anumite conditii.Primul astfel de constructor
are formula generala: fstream (filedesc fd);
unde: -fd este un descriptor al filei ce urmeaza sa fie returnat de catre
o functie de genul _open() sau _sopen()
Al doilea constructor optional are formula generala:
fstream(filedesc fd,char*pch,int nLength);
unde: -fd este ca mai sus
-pch este pointerul spre o arie de date de lungimea nLength alocata
anterior (va fi utilizata pe post de tampon,daca are valoarea NULL
stream-ul rezultat va fi netamponat)
-nLength este lungimea in bytes a ariei rezervate pentru tampon
Dupa cum se observa,nu exista o singura solutie ci un numar oarecare
de solutii alternative.La prima vedere pare destul de complicat si confuz,
dar de fapt este foarte simplu.Stream-ul pentru operatiile I/O se utili-
zeaza la fel ca orice alt obiect.Se declara tipul de data,se apeleaza
constructorul,apoi se apeleaza metodele obiectului.Nu mai sunt necesare
alte functii auxiliare sau alte file header si biblioteci.Obictul contine
toate functiile necesare.
Pentru a intelege mecanismul de mostenire a metodelor fiecarui stream,
este utila urmatoarea schema:
|- istream -------| |-fstream
ios- -iostream---|-strstream
|-ostream---------| |-stdiostream
Se observa clasa ios care este ancestorul comun.Din istream deriva toate
stream-urile de intrare a datelor iar din ostream deriva cele de iesire a
datelor.Stream-urile mixte,mostenesc atat clasa istream cat si clasa
ostream,precum si clasa ancestoare ios.Ca rezultat,stream-urile mixte
sunt cele mai complexe si pot utiliza toate metodele ancestorilor.
In exemplul urmator,fstream mediaza toate operatiile I/O:

-44-
EXEMPLU: ( vezi si cplus34.cpp )
#include <fstream.h>
#include <conio.h>
main()
{ float numar=345.6789;
fstream scrie1("TEXTS.txt",ios::out);
scrie1 << "Text introdus in fila specificata: ";
scrie1 << "\n";
scrie1 << "\n Primul rand din textul introdus...";
scrie1 << "\n numarul este: " << numar <<" etc. ";
scrie1.close();
fstream fila1("TEXTS.txt",ios::in);
char buf[180];
while ( ! fila1.eof() )
{ fila1.getline(buf,200);
cout << "\n" << buf; }
getch();
return 0;
}
Din exemplul de mai sus se observa ca am utilizat clasa fstream atat
pentru obiectul scrie1,care este stream-ul de output,cat si pentru obiect-
ul file1,care este stram-ul de input.Dupa ce am deschis fila pentru scriere
datele se pot introduce pur si simplu cu operatorul << care va transfera
datele in stream.Este foarte important ca dupa epuizarea trensferului sa
se inchida fila cu functia close().In caz contrar,toate operatiile urma-
toare se vor desfasura tot in interiorul stream-ului de output.Apoi am
derivat un obiect pentru iesirea datelor si am transferat datele preluate
din fila spre ecran.Observati ca fila scrisa contine atat date de tip
text cat si valoarea numarica de tip float (numar).Operatiile cu text si
valori numrice se efectueaza extrem de usor,cu aceleasi functii si cu
ajutorul acelorasi operatori.Pentru unele operatii matematice se utili-
zeaza si o serie de manipulatori.In acest caz,va trebui incarcata in
etapa de precompilare si fila iomanip.h ( cu #include <iomanip.h> ).
Pentru practica curenta si pentru incepatori,se recomanda clasa fstream
si un numar cat mai limitat de operatii de intrare/iesire,pentru a evita
eventualele erori si/sau exceptii,care pot deranja organizarea datelor
de pe disc.Daca nu a-ti inteles perfect sistemul de apelare a metodelor
si a functiilor,este mai bine sa utilizati doar solutii standardizate si
verificate anterior.Nu toate versiunile de program contin si descrierea
claselor I/O.In rezumat,metodele acestor clase sunt:
ipfx ( int need = 0 ); verifica conditia de eroare inainte de extractie
isfx () ; -este apelata dupa operatia de extractie (input suffix)
get() -are urmatoarele variante supraincarcate:
int get(); &
istream& get (char *pch,int nCount,char delim= '\n');
istream& get (unsigned char* puch,int nCount,char delim='\n');
istream& get (signed char* psch,int nCount,char delim='\n');
istream& get (char& rch);
istream& get (unsigned char& ruch);
istream& get (signed char& rsch);
istream& get (streambuf& rsb,char delim='\n');

-45-
unde: pch,puch,psch sunt pointeri spre arii de caractere
nCount este numarul maxim de caractere
delim este caracterul utilizat ca delimitator (capatul de rand)
rch,ruch,rsch este un pointer spre un caracter (referinta)
rsb este un pointer spre un obiect de tip streambuf
-in toate variantele,get() extrage caractere din stream pana ce intalne-
ste delimitatorul specificat prin delim.
getline() - are urmatoarele variante supraincarcate
istream& getline(char *pch,int nCount,char delim='\n');
istream& getline( unsigned char* puch,int nCount,char delim='\n');
istream& getline( signed char* psch,int nCount,char delim= '\n');
-la fel ca si get() extrage caractere din stream
read() -are urmatoarele variante supraincarcate
istream& read(char* pch,int nCount);
istream& read(unsigned char* puch,int nCount);
istream& read(signed char* psch,int nCount);
-functia read() extrage nCount date din stream
ignore( int nCount = 1,int delim = EOF); extrage si sterge caractere
peek() -returneaza caracterul imediat urmator,fara sa-l extraga din stream
int gcount() const -numara caracterele extrase la ultima operatie
eatwhite() -sterge spatiul gol de la inceputul streamu-ului
putback (char ch) -repune in stream ultimul caracter extras
int sync() -sincronizeaza tamponul intern cu sursa externa de caractere
seekg() -are urmatoarele variante supraincarcate:
istream& seekg( streampos pos);
istream& seekg( streamoff off,ios::seek_dir dir);
unde : pos si off sunt de tip long
dir specifica directia de deplasare si poate fi:
ios::beg -cauta de la inceputul stream-ului
ios::cur -cauta de la pozitia cursorului
ios::end -cauta de la sfarsitul stream=ului
-modifica pozitia pointer-ului pentru functia get()
tellg() -citeste valoarea pointerului get.
Operatorul >> este supraincarcat pentru toate tipurile char,numerice si
respectiv streambuf*,istream& si ios&.
open(const char* szName,int nMode=ios::in,int nProt=filebuf::openprot);
-a fost descrisa impreuna cu constructorul pentru fstream,la exemplul
dela pagina 42.Metoda deschide o fila si ataseaza continutul in tamponul
intern al stream-ului.
close() -inchide stream-ul deschis anterior.
streambuf* setbuf( char* pch,int nLength); -ataseaza aria de memorie
specificata in tamponul intern de memorie al stream-ului
int setmode(int nMode = filebuf::text); seteaza modul text sau modul
binar pentru tamponul stream-ului.Accepta valorile : filebuf::text si
respectiv filebuf::binary.Se poate apela numai dupa ce fila a fost
deschisa
attach(filedesc fd) -ataseaza fila specificata in tamponul streamului
filebuf* rdbuf() const; returneaza un pointer spre tamponul intern
filedesc fd() const -returneaza descriptorul filei
int is_open() const -returneaza nonzero daca streamul este asociat la o
fila identificata prin descriptorul fd.In caz contrar returneaza 0.

-46-
char* str() -returneaza un pointer catre aria de caractere din stream
opfx() - verifica conditiile de eroare inainte de insertia datelor
osfx() - este apelat dupa insertie pentru a elibera tamponul intern
put(char ch) - insera un singur caracter in stream
write() -are supraincarcate urmatoarele variante:
ostream& write(const char* pch,int nCount);
ostream& write(const unsigned char* puch,int nCount);
ostream& write(const signed char* psch,int nCount);
unde : pch,puch,psch sunt pointeri spre arii de caractere
nCount este numarul de caractere ce vor fi scrise
-insera in stream o serie de bytes (nCount bytes)
flush() - elibereaza tamponul intern (sterge toate datele)
seekp( stream pos); sau seekp( stream off,ios::seek_dir dir)
-este la fel ca seekg() (vezi pagina anterioara),adica deplaseaza
pointerul de insertie pentru functia put()
streampos tellp() -citeste valoarea pointerului pentru functia put()
operatorul << este operatorul de insertie si este supraincarcat la fel
ca si cel de extractie pentru tipurile char,numerice...etc.
pcount() - returneaza numarul de bytes arhivati in tamponul intern
CONSTRUCTORII SI DESTRUCTORII
Unele stream-uri au supraincarcati mai multi constructori.Expresiile
generale sunt:
ISTREAM
istream( streambuf* psb) si respectiv destructorul ~istream()
IFSTREAM
ifstream();
ifstream( const char* szName,int nMode = ios::in,
int nProt=filebuf::openprot);
ifstream( filedesc fd);
ifstream( filedesc fd,char* pch,int nLength);
si respectiv destructorul implicit ~ifstream();
ISTRSTREAM
istrstream (char* psz);
istrstream (char* pch,int nLength);
si destructorul ~istrstream();
OSTREAM
ostream (streambuf* psb); si destructorul ~ostream();
OFSTREAM
ofsteam();
ofstream(const char* szName,int nMode=ios::out,
int nProt=filebuf::openprot);
ofstream( filedesc fd);
ofstream( filedesc fd,char* pch,int nLength);
si respectiv destructorul ~ofstream();
OSTRSTREAM
ostrstream();
ostrstream (char* pch,int nLength,int nMode= ios::out);
si destructorul ~ostrstream();
Pentru stream-urile mixte,constructorul este identic cu ifstream sau
ofstream,respectiv cu istrstream/ostrestream (in functie obiectul care
va fi derivat din clasa de baza).
-47-
In cazul in care urmeaza sa utilizati frecvent in programare aceste
clase de obiecte,este bine sa desenati fiecare clasa,impreuna cu membrii
si metodele sale,pe cate o plansa mare,afisata pe perete:
EXEMPLU: class istrstream
{ istrstream( char* pch,int nLength);
~istrstream();
public:
strstreambuf* rdbuf() const;
char* str();
}
Se observa constructorul,destructorul si cele doua metode.In momentul
in care declarati un stream,sau depanati o aplicatie,puteti utiliza
plansa pentru a va orienta rapid asupra metodelor sau asupra tipului de
data al fiecarui parametru.Este mult mai usor decat pare la prima vedere.

Solutiile prezentate mai sus,sunt utile doar pentru a deschide filele


in timpul executiei,sau dupa un anumit algoritm.Pentru a consulta pur si
simplu o fila de tip text,nu este necesar sa programati nici o aplicatie.
Este suficient sa utilizati din meniul File optiunea Open.Alegeti fila
dorita si confirmati cu OK.
O alta modalitate,extrem de simpla, este sa utilizati o fereastra de
tip Windows,care are un meniu complet pentru manipularea filelor de tip
text.Din meniul File,alegeti New si apoi AppExpert.Introduceti numele
aplicatiei (De exemplu: Fila1 -eventual in interiorul directorului Debug
pentru a fi mai usor de transferat in Recycle Bin) si confirmati cu Open,
apoi din ferestra "AppExpert Application Generation Options" alegeti
optiunea MainWindow si confirmati cu butonul Generate,apoi cu OK.Fara
nici o alta linie de cod,deschideti fila1app.cpp,compilati aplicatia,
apoi construiti proiectul cu Build All si Make all.
Puteti ignora avertizarea care va atentioneaza ca fila de definitie
are un tampon de memorie mai mic de 64K si va fi inlocuit automat cu un
tampon implicit de 1M.Apasati butonul Run (cel cu un trasnet) si execu-
tati aplicatia.Proiectul va deschide automat o fereastra de tip Windows
cu un meniu complet.In aceasta ferestra,puteti deschide in timppul exe-
cutiei orice fila de tip text,cu ajutorul optiunii Open din meniul File.
Este bine sa utilizati directorul Debug,pentru proiectele temporare,
sau pentru exercitiile de programare care nu urmeaza sa fie pastrate
definitiv.In momentul in care nu mai sunt necesare,pur si simplu puteti
muta tot directorul in Recycle Bin si deschideti un nou director Debug.
Acest exercitiu,deplaseaza aplicatiile C++ din mediul DOS in mediul
Windows.Bineinteles ca aplicatiile editate sub Windows sunt mult mai
complexe si spectaculoase,dar consuma incomparabil mai multa memorie de
operare si respectiv memorie fizica pentru arhivarea programelor.In
functie de necesitatile de programare si de hard-ul instalat,puteti alege
ce tip de aplicatii doriti sa generati.
Exista o serie intreaga de clase de obiecte destinate pentru operatii
cu file de un anumit tip.Aceste clase,vor fi prezentate impreuna cu
biblioteca din care fac parte,sau urmeaza sa le descoperiti si descifrati
singuri.Principiul de lucru este acelasi: se incarca fila sursa,se decla-
ra un obiect din tipul respectiv,se apeleaza constructorul si apoi meto-
dele specializate.
-48-
Bibliotecile sunt un alt tip de structuri de date,utilizate pentru a
arhiva variabile,obiecte si functii sau algoritmi de executie.Se utili-
zeaza atunci cand unele dintre structurile unui program urmeaza sa fie
utilizate de mai multe ori,de catre mai multi utilizatori sau de catre
mai multe aplicatii si programe diferite.Bibliotecile extind extrem de
mult paleta de optiuni a unui programator si reprezinta,probabil,cel mai
valoros concept de programare structurata,dintre cele dezvoltate pana in
prezent.
In functie de modul in care sunt incluse intr-un program,bibliotecile
pot fi statice (adica exista permanent in memoria de operare) sau pot fi
legate dinamic(DLL=Dinamic Link Library),adica nu sunt incarcate in
memoria de operare decat in momentul in care se apeleaza datele incluse
in biblioteca respectiva.
In functie de tipul datelor continute,bibliotecile pot fi biblioteci de
tip arhiva,biblioteci de functii,biblioteci de obiecte,biblioteci de pre-
setare sau preconfigurare a unitatilor hardware,biblioteci de resurse,
biblioteci pentru analiza sau depanarea programelor etc.In functie de
modul de operare asupra datelor,bibliotecile pot fi biblioteci de import.
biblioteci de export sau mixte (import/export).
Exista o terminologie foarte bogata referitoare la biblioteci,dar,
trasatura caracteristica cea mai importanta este cea referitoare la modul
in care interactioneaza cu procesorul (cu memoria de operare).Din acest
punct de vedere,bibliotecile se impart in biblioteci statice(header files)
si biblioteci cu legare dinamica (DLL).
Bibliotecile statice prezinta o serie de avantaje:
-sunt foarte usor de editat (se editeaza o fila de tip text)
-sunt foarte usor de depanat (codul este expus direct,fara conversie)
-sunt foarte usor de modificat,actualizat,inlocuit etc.
-ocupa un spatiu fix de memorie (nu exista riscul de a "dopa" memoria)
-pot fi transferate automat de la un program la altul (fara link)
-sunt foarte usor de exploatat (date se apeleaza direct)
-pot fi arhivate separat de programul care le utilizeaza
-nu creaza conflicte de identificator
-nu exista nici un hazard in gestionarea si adminisrarea memoriei RAM
Acest gen de biblioteci sunt utilizate pentru toate filele header(cele
care se introduc in program cu #include) si au fost concepute si dezvol-
tate cu mult inaintea celor dinamice.Pentru realizarea unei astfel de
biblioteci se utilizeaza acelasi limbaj de programare ca pentru o fila
de cod oarecare.Se poate utiliza orice editor de text,iar fila se va arhi-
va cu extensia .h .
EXEMPLU: //dosar1.h
class dosar
{ public:
dosar(){ };
~dosar() { };
char nume[200];
char prenume[200];
};
Fila de mai sus se va arhiva cu numele: dosar1.h.Pentru a putea fi apela-
ta direct,fara a specifica si calea de acces,se va arhiva in directorul
INCLUDE,impreuna cu filele header standard.

-49-
Daca este inclusa in directorul INCLUDE,fila dosar1.h va putea fi
inclusa un orice program cu comanda de precompilare: #include<dosar1.h>.
Daca realizati un program oarecare in care fila header va fi inclusa in
alt director,apelul va include calea de acces completa pana la fila res-
pectiva.(EXEMPLU: daca fila este inclusa in directorul MANUAL,va putea
fi apelata cu: #include<MANUAL/dosar1.h> )
Pe primul rand al bibliotecii se introduce sub forma de comentariu
numele bibliotecii (Nu este strict necesar,dar poate fi extrem de util
atunci cand in timpul unui proces de depanare deschideti toate filele
din program si doriti sa identificati cat mai usor fila respectiva).
In exemplul de mai sus,biblioteca statica nu contine decat o clasa,
denumita "dosar".Clasa contine constructorul,destructorul si membrii
clasei.Pentru a putea utiliza clasa "dosar" in programe,nu trebuie decat
sa includeti si fila header dosar1.h (dupa ce a fost arhivata in INCLUDE).
EXEMPLU: (vezi si cplus35.cpp )
#include <iostream>
#include <conio.h>
#include <dosar1.h>
main()
{ dosar d1;
cout << "Introduceti numele: \n";
cin >> d1.nume;
cout << "Introduceti prenumele: \n";
cin >> d1.prenume;
cout << "\n\n Numele introdus este: " << d1.nume;
cout << "\n Prenumele este: " << d1.prenume;
getch();
return 0;
}
Am derivat un obiect din clasa "dosar",denumit d1,suficient pentru
a putea introduce direct datele in obiect.Acest gen de implementare este
util atunci cand o anumita clasa urmeaza sa fie utilizata foarte frecvent.
In general,bibliotecile statice se utilizeaza pentru date care se uti-
lizeaza frecvent sau repetat intr-un program,astfel incat sa se justifice
prezenta lor permanenta in memoria de operare.
Pentru a edita o biblioteca statica se pot include in memorie si alte
biblioteci statice sau dinamice,dar nu este recomandabil.Orice byte inclus
in biblioteca statica va fi pastrat in memoria RAM pentru intrega durata
de executie a programului.Nu se vor include in biblioteci date inutile,
comentarii,artificii tehnice sau artificii de prezentare si afisare a
datelor,etc. decat daca acesta este scopul specific al bibliotecii respec-
tive.Cu cat o biblioteca este mai mica,cu atat este mai usor de manevrat
si mai usor de inclus in orice aplicatie.Inainte de a include o biblioteca
statica,verificati cat spatiu de memorie aveti la dispozitie(pentru a nu
depasi memoria RAM).Daca doriti sa arhivati mai multe tipuri de date si
functii care sunt necesare frecvent,dar nu neaparat impreuna,este mai bine
sa editati mai multe biblioteci independente,decat sa introduceti toate
datele in aceeasi biblioteca (o parte nu vor fi utilizate de loc in
program).Este bine sa nu grupati in aceeasi fila header decat datele care
se utilizeaza de obicei impreuna,sau functiile care opereaza sinergic sau
simultan asupra aceluiasi tip de data.

-50-
Toate datele incluse in biblioteca statica vor fi introduse in memoria
de operare in etapa de precompilare.Orice greseala in editarea unei file
header blocheaza procesul de compilare.Verificati cu atentie fiecare
byte si fiecare functie din biblioteca inainte de a incepe exploatarea
filei respective.Pentru a optimiza munca procesorului,este bine ca filele
cele mai mari sa fie incluse primele,iar cele mai mici sa fie incluse
ultimele.Dupa ce a-ti inclus filele header,inainte de a incepe editarea
propriu zisa a programului,puteti verifica cat spatiu de memorie ocupa
filele header.Bibliotecile statice nu pot fi utilizate simultan de catre
mai multi utilizatori.Fiecare utilizator va trebui sa detina o copie
separata a bibliotecii respective (in momentul incarcarii in meorie fila
va fi blocata pentru scriere sau citire).
Este bine sa pastrati toate bibliotecile statice editate.Cu cat aveti
o colectie mai bogata de astfel de file,cu atat programele d-voastra vor
fi mai versatile si mai usor de editat.Nu editati biblioteci doar de
dragul de a scrie ceva.Bibliotecile trebuie sa contina date utile,functii
obiecte sau resurse,sau cel putin solutii tehnice care va usureaza mult
munca viitoare (Exemplu: algoritmi de sortare a datelor,algoritmi de
calcul al unor parametrii specializati etc.).
Bibliotecile DLL,sunt un alt tip de module independente ce pot fi
exploatate pentru a simplifica munca de programare.Bibliotecile DLL pre-
zinta urmatoarele avantaje:
-nu consuma memorie de RAM decat in momentul in care sunt apelate datele
-pot fi utilizate simultan de catre mai multi utilizatori (pentru fiecare
utilizator se va crea in memoria RAM o instanta a filei DLL)
-pot fi utilizate simultan in mai multe aplicatii
-actualizarea filei DLL,va actualiza automat toate aplicatiile in care
este apelata
-fila este criptata si nu poate fi evaluata decat daca se adauga si fila
sursa (sau cu un program de decompilare)
-fila DLL poate fi utilizata pentru a executa o serie de operatii si
setari independente de programul din care este apelata(se executa
strict in memoria de operare fara riscul de a corupe datele din
programul sursa)
-programul apelant poate fi compilat si depanat chiar daca fila DLL
contine erori,deoarce fila nu se incarca in memorie decat in momentul in
care este apelata o data inclusa in lista de export a filei DLL
-fila DLL nu poate fi utilizata decat dupa ce a fost link-ata de programul
apelant (nu poate fi "incarcata accidental")
-fila DLL poate importa date din alte file DLL,sau poate exporta date
in diverse alte module si programe(cu conditia sa existe un link valabil)
-fila DLL poate fi utilizata ca un subprogram independent (poate ini-
tializa sau prelucra date care nu sunt vizibile din programul apelant)
Filele DLL prezinta urmatoarele dezavantaje:
-sunt mai greu de editat si administrat
-este destul de greu de gestionat spatiul de memorie RAM atunci cand se
utilizeaza simultan mai multe file DLL
-filele DLL pot "dopa" memoria de RAM pentru a genera inevitabilul mesaj
de eroare "Out of memory !"
-filele DLL link-ate activ creaza un spatiu virtual de memorie in care
pot apare conflicte de identificator,supraincarcari etc.

-51-
Nu exista o metoda unica pentru editarea bibliotecilor DLL.Cea mai
simpla metoda este urmatoarea:
EXEMPLU (vezi si directorul DLLTest)
Salvati un director nou,pentru programul ce urmeaza sa fie editat
(de exemplu DLLTest).Din meniul File alegeti New,apoi Project si apoi
optiunile: Application (.exe) Target Model= Console si Frameworks =
Class Library.Cu butonul Browse selectati directorul dorit (DLLTest) si
introduceti numele aplicatiei (Test1.cpp).Confirmati cu OK.
Din proiectul dezvoltat,stergeti filele .def si .rc si utilizati fila
.cpp pentru a realiza scheletul viitoarei aplicatii:
#include<iostream>
#include<conio.h>
main()
{
cout << "text";
getch();
return 0;
}
Compilati,construiti cu Build All apoi cu Make All.Proiectul va contine
fila executabila (.exe) si fila sursa (.cpp) afisate astfel:
-test1.exe [.exe]
|
-test1.cpp [.cpp]
Salvati fila .cpp si apoi inchideti toate filele generate.Pentru a gene-
ra o fila DLL se va utiliza un mecanism similar.
Din meniul File,alegeti New,apoi Project si apoi: Dynamic Library [.dll]
Target Model = Console,Framework = Class Library.Cu butonul Browse se-
lectati tot directorul DLLTest,apoi adaugati numele filei DLL1.
Se va genera un proiect asemanator cu cel precedent,dar prima fila din
proiect va fi intitulata dll1.dll iar celelalte vor fi .cpp,.def si .rc.
Cu un click drept de mouse,alegeti Delete node si eliminati din proiect
filele .def si .rc.Deschideti fila .cpp si editati urmatorul text:
extern int _export Max1(int a){ a=a*a; return a; };
extern int _export Min1(int a){ a=a-1; return a; );
int WEP(int b)
{
b=1;
return b;
}
Compilati,construiti cu Build All,apoi cu Make All.Salvati fila,apoi
inchideti toate filele si programul principal (Borland C++) pentru ca
fila dll nou generata sa fie inclusa in lista programului principal.
Se observa ca pentru a exporta a functie se utilizeaza cuvantul cheie
"_export".Cuvantul cheie "extern" este necesar pentru a specifica daca
elementul respectiv va avea vizibilitate si in alt modul decat cel actual.
In rest,fila contine si o functie oarecare,denumita functie de intrare,
care are rolul de asigura iesirea din fila DLL.Aceasta functie poate sa
execute si o serie de operatii cu vizibilitate locala,dar este preferabil
sa nu execute nici o operatie care incarca memoria de operare.Pentru
aplicatiile Windows se utilizeaza functii specializate: DLLMain,LibMain,
sau WEP iar pentru restul aplicatiilor se poate utiliza orice functie.

-52-
Daca utilizati un utilitar de tipul Norton Commander si deschideti
directorul DLLTest,puteti observa ca programul a generat automat urma-
toarele file: dll1.dll,dll1.dsw,dll1.ide,dll1.lib,dll1.mbt,dll1.mrt,
dll1.obj si dll1.obr cu dimensiuni variabile.Fila dll1.dll ocupa circa
9541 bytes iar fila dll1.lib ocupa 1024 bytes.Aceste doua file sunt
necesare pentru a realiza link-ul.
Pentru a lega(linka) fila DLL1 de proiectul Test1,reporniti programul
C++,deschideti proiectul Test1 cu Project si Open,urmat de View si Project
si apoi utilizati un click de mouse drept si Add Node pentru a adauga
fila dll1.dll la fila test1.cpp.Apoi puteti adauga si fila dll1.cpp la
fila dll1.dll.In final,proiectul va arata in felul urmator:
test1.exe [.exe]
|
test1.cpp [.cpp]
|
dll1.dll [.dll]
|
dll1.cpp [.cpp]
Efectuati un nou click drept de mouse si alegeti optiunea Link.
Inchideti proiectul si apoi din meniul File deschideti fila Test1.cpp.
Pentru a putea exploata functiile exportate din fila DLL1,modificati
fila Test1.cpp astfel:
#include <iostream>
#include <conio.h>
#pragma comment (lib,"DLL1.LIB")
extern _import Max1(int a);
extern _import Min1(int a);
main()
{
int b;
b=Max1(5);
int c;
c=Min1(5);
cout << "Functia Max1() returneaza: \n";
cout << b;
cout << "\nFunctia Min1() returneaza: \n";
cout << c;
getch();
return 0;
}
Compilati,construiti si executati aplicatia.
Observati ca importul functiilor se face cu cuvantul cheie _import.Pentru
a specifica explicit link-ul spre fila DLL se utilizeaza directiva de
precompilare #pragma comment(lib,"numele filei .LIB generata de DLL).
Observati ca mecanismul de editare este mai complex decat pentru biblio-
tecile statice.Sunt necesare operatii succesive si se genereaza un numar
destul de mare de file.Pentru cele doua functii (a caror definitie ocupa
cateva zeci de bytes) s-au generat mai multe file care ocupa cateva zeci
de kilobytes.Este usor de observat cu nu are rost sa generati o biblioteca
DLL doar pentru cateva functii utilizate sporadic.Pentru o astfel de apli-
catie este mult mai usor de utilizat o biblioteca statica.

-53-
Exemplul prezinta insa cel mai simplu mecanism de editare a unei
file DLL.O alta metoda este prin utilizarea filei .DEF in care se va
specifica numele filei dupa LIBRARY iar elementele exportate vor fi
incluse in lista dupa EXPORTS.In acest caz,functiile exportate pot fi
asociate si cu un cod numeric,astfel incat apelul lor sa poata fi facut
prin numarul de ordine.Pentru exemplul de mai sus,fila .DEF ar trebui sa
arate cam asa:
LIBRARY DLL1
EXPORTS
Max1 @1
Min1 @2
Se poate utiliza oricare dintre metode,sau o combinatie a lor.
O biblioteca DLL se justifica atunci cand arhiveaza zeci sau sute de
functii,constante si variabile.In acest caz,este destul de greu de tinut
evidenta fiecarei functii sau variabile.Pentru a simplifica munca de
programare,se poate forma o biblioteca de import in care sa fie arhivate
toate datele exportate.Biblioteca de import se creaza sub forma de fila
header si se incarca in etapa de compilare.In aplicatie,functiile pot fi
apelate direct,ca si cand ar fi preluate din biblioteca statica:
EXEMPLU: (vezi si directorul DLLTest2 )
Se procedeaza identic ca mai sus.Se deschide directorul,se formeaza
schita proiectului,se arhiveaza,apoi se deschide un nou proiect de tip
Dynamic Library [.dll] si in fila de tip .cpp se introduce un text de
genul:
extern int _export Maxim(int a,int b){ if (a>b) return a; else return b;};
extern int _export Minim(int a,int b){ if (a<b) return a; else return b;};
int WEP (int b)
{
b=1;
return b;
}
Compilati,construiti cu Build All si Make All,apoi salvati si inchideti
programul pentru actualizare.
Redeschideti programul si realizati link-ul la fel ca in exemplul de mai
sus. Pentru importul datelor editati o biblioteca statica de genul:
//test2.h
#pragma comment(lib,"DLL2.LIB")
extern _import Maxim(int a,int b);
extern _import Minim(int a,int b);
Salvati fila cu numele de Test2.h in directorul INCLUDE.Pentru utilizarea
celor doua biblioteci,editati o aplicatie de genul:
#include <iostream>
#include <conio.h>
#include <test2.h>
main()
{
cout << "Maximum dintre 12 si 7 este: "<<Maxim(12,7)<<"\n";
cout << "Minimum dintre 12 si 7 este: "<<Minim(12,7)<<"\n";
getch();
return 0;
}

-54-
O alta metoda destul de practica este cea prin care biblioteca statica
realizeaza atat exportul cat si importul datelor din fila DLL.Pentru
acest scop,in fila DLL se declara o variabila oarecare cu ajutorul careia
biblioteca statica va putea face distinctia dintre fila DLL si fila
aplicatie.Daca variabila este definita,biblioteca va exporta datele iar
daca variabila nu este definita biblioteca va importa datele:
EXEMPLU: (vezi si directorul DLLTest3)
In acest caz,primul pas este reprezentat de editarea filei header:
//Test3.h
#ifdef MODULDLL
#define IMPEXP __declspec(dllexport)
#else
#pragma comment(lib,"DLL3.LIB")
#define IMPEXP __declspec(dllimport)
#endif
IMPEXP int fnTest(int a);
Salvati fila cu numele Test3.h in directorul INCLUDE.
In rest se procedeaza identic,dar fila dll3.cpp va arata astfel:
#define MODULDLL
#include <test3.h>
IMPEXP int fnTest(int a){ a=a+33; return a;};
int WEP(int b)
{
b=1;
return b;
};
Iar fila .cpp din aplicatia test3.cpp va arata astfel:
#include <iostream>
#include <conio.h>
#include <test3.h>
main()
{
cout << "text \n";
int b;
b=fnTest(15);
cout << "Functia fnTest() returneaza: " << b;
getch();
return 0;
}
Observati ca in loc de "_export" s-a utilizat: "__declspec(dllexport)" iar
in loc de "_import" s-a utilizat: "__declspec(dllimport)".
Pentru versiunile noi,cele doua cuvinte cheie au fost inlocuite prin
formulele de mai sus,care asigura o mai buna compatibilitate cu formatul
de 32 de biti.Pentru toate DLL-urile Windows si pentru cele nou create
se recomanda utilizarea acestor formule.Cuvintele cheie vor fi rezervate
doar pentru compatibilitatea cu programele mai vechi.
Asadar,cele doua tipuri de biblioteci pot fi combinate cu succes pentru
a beneficia de avantajele oferite de fiecare tip in parte.
Exemplele de mai sus,ofera doar o imagine simplista a modului in care
se pot crea sau apela diversele tipuri de biblioteci.Solutia optima de-
pinde de necesitatile programului si de experienta programatorului.

-55-
Adevaratele probleme apar atunci cand se utilizeaza mai mult decat o
singura biblioteca DLL.Fiecare biblioteca DLL,in momentul in care este
"linkata" la o aplicatie,creaza un fel de "spatiu virtual" in care toate
elementele exportabile sunt vizibile in mod egal.Daca in doua sau mai
multe biblioteci DLL exista date care au fost denumite cu acelasi iden-
tificator,apar fenomene de supraincarcare sau conflicte de identificator.
Daca nu detineti fila sursa a tuturor filelor DLL,depanarea unui astfel
de program este extrem de dificila sau chiar imposibila.Acest gen de
situatie a fost denumit "DLL HELL" (infernul DLL-urilor).In aceste situ-
atii,in timpul executiei se va returna fie un mesaj de eroare,fie o
valoare eronata preluata din prima biblioteca linkata in care exista o
definitie valabila.In plus,alocarea de memorie de operare se va face dupa
un alt algoritm decat cel proiectat si pot interveni diverse tipuri de
incompatibilitati,suprascrieri,supraincarcari etc.Din acest motiv este
bine sa nu utilizati file DLL pentru care nu detineti si fila sursa,
sa nu utilizati excesiv de multe file DLL in spatiu de memorie limitat,
sa verificati cu atentie memoria libera in urma fiecarui apel posibil,din
fiecare fila,cat si al tuturor combinatiilor posibile de apel intersectat
al mai multor biblioteci.Acest gen de programare,incepe sa semene tot
mai mult cu un joc de tipul "PronoExpres" si nu se recomanda decat celor
cu notiuni avansate de gestionare a memoriei.Lucrurile se simplifica
foarte mult daca utilizati programe automate de gestioanre si analiza a
memoriei.Acest gen de programe vor apela si evalua toate apelurile posi-
bile si vor semnala eventualele incompatibilitati.Regula de aur este urma-
toarea: " nu va lansati in operatii pe care nu puteti sa le controlati cu
resursele proprii".O solutie extrem de simpla pentru evitarea conflictelor
de identificator este sa utilizati spatii denumite ("namespace") in edita-
rea bibliotecilor.In acest fel,puteti avea un spatiu special pentru toate
bibliotecile DLL editate de d-voastra,care nu va interfera cu celelalte
biblioteci DLL programate de alti programatori.Pentru importul datelor
utilizati si formula "using namespace NUMELE" (vezi si inceputul capito-
lului).Visual C++ utilizeaza un astfel de spatiu standardizat denumit
std.Pentru aplicatiile d-voastra puteti utiliza un astfel de standard,cu
un numa cat mai discriminativ (eventual numele si prenumele personal).
Nu va lansati in procese sofisticate de "depanare" a filelor DLL cu
provenienta nesigura sau necunoscuta.Daca exista orice fel de suspiciuni
este mai usor sa abandonati biblioteca respectiva si sa editati una noua.
Atunci cand actualizati o fila DLL,trebuie sa aveti in vedere ca mo-
dificarea efectuata va interesa toate aplicatiile care utilizeaza fila
respectiva.De cele mai multe ori,este preferabil sa pastrati fila veche
si a creati si o noua fila care contine toate modificarile necesare.Astfel
fiecare aplicatia va putea apela fila DLL necesara.
Daca realizati si distribuiti biblioteci DLL,este bine sa distribuiti si
fila sursa (fila .cpp din care a fost creata).Este un obicei bun sa ada-
ugati fila .cpp in proiect,impreuna cu fila dll.dll (la fel ca in exemple-
le prezentate).De obiecei fila sursa este destul de mica si nu ocupa prea
mult spatiu.Nu este bine sa includeti file header in filele DLL decat daca
este absolut necesar.Nu are rost se creati o fila DLL de tip Windows in
care sa includeti fila "windows.h" doar pentru a defini o clasa sau o
functie care realizeaza operatii elementare.Fiecare byte conteaza.Fila DLL
poate contine insa,nelimitate operatii cu vizibilitate locala.
-56-
Fila de tip DLL se compileaza si se construieste la fel ca orice fila
executabila.Exista si biblioteci cu legare dinamica,care au alta extensie
decat .DLL(Exemple: .EXE sau .drv).Pentru aplicatiile Windows,filele DLL
pot fi incarcate si explicit,cu functia LoadLibrary(),urmata de GetProc-
Address() pentru a obtine adresa functiei dorite si de FreeLibrary()
pentru eliberarea memoriei.
Biblioteca DLL poate contine functii interne si poate sa execute o
serie de operatii,la fel ca orice program executabil.Nu se recomanda
utilizarea acestei facilitati decat pentru operatii indispensabile.In
restul situatiilor,se pot utiliza module de program independente,care pot
fi puse in executie cu functii usor de apelat (fara incarcarea memoriei):
EXEMPLU WINDOWS: (vezi si cplus36.cpp)
#include <windows.h>
#include <iostream>
#include <conoi.h>
main()
{
WinExec("cplus2.exe",5);
getch();
return 0;
}
EXEMPLU C++ : (vezi si cplus37.cpp)
#include<iostream>
#include<stdlib.h>
#include<conio.h>
main()
{
cout << "text din fila cplus37 \n";
cout << "urmeaza apelul modulului cplus2.exe: \n";
system("cplus2.exe");
getch();
return 0;
}
Pentru programele recente,se recomanda utilizarea functiilor moderne,gen
CreateProcess(),etc.
Bibliotecile DLL pot fi utilizate pentru a arhiva filele de resurse.
In acest caz,la crearea proiectului se va utiliza AddNode pemtru a include
in proiect toate filele de tip .RC necesare.Dupa compilare,rezulta o fila
DLL.Fila DLL poate fi manipulata monobloc,pentru a asigura toate resursele
necesare pentru interfata grafica: meniuri,icons,imagini,sunete etc.
O alta aplicatie frecventa o reprezinta suportul multilingual.Pentru
fiecare limba oferita se distribuie si fila DLL necesara,in care sunt
incluse toate particularitatile versiunii respective.
In mod similar,se creaza si distribuie resursele software necesare
pentru implementarea si exploatarea unor resurse hardware.In acest caz,
de obicei filele vor avea extensia .drv,dar exista si drivere cu extensia
clasica .dll.
O privire sintetica asupra acestui capitol,atrage atentia asupra posi-
bilitatilor de suprastructurare a aplicatiilor si programelor realizate de
d-voastra.Prezentarea nu este exhaustiva,este doar un pretext pentru a va
stimula dorinta de a lectura si alte manuale de specialitate.
-57-
BIBLIOTECA OWL (Object Windows Library)

Biblioteca OWL contine un set de 125 de file header,in care sunt defi-
nite clase si constante,utile pentru a realiza programe si aplicatii ce
utilizeaza o intefata grafica formata din obiecte de tip Windows.OWL este
o alternativa pentru API-Windows (API exploateaza fila windows.h).
Biblioteca OWL nu este prezenta in toate versiunile de C++.In Visual C++
a fost inlocuita prin MFC(Microsoft Foundation Classes) iar in versiunea
C++ Builder a fost inlocuita prin VCL(Visual Component Library).
In esenta,toate aceste variante realizeaza acelasi lucru: definesc o
serie de clase,din care se vor deriva obiectele necesare pentru a realiza
o interfata grafica.Fiecare obiect,detine unul sau mai multi constructori,
un destructor si o serie de metode proprii specializate.
Versiunea Borland C++ 5.A,nu contine explicatii destul de clare despre
modul in care poate fi utilizata aceasta biblioteca,dar contine o serie
destul de bogata de exemple si programe si un tutorial destul de clar (in
directorul Examples/Owl).Exemplele si obiectele nu sunt compilate si
link-ate.Pentru a putea utiliza aceste exemple,deschideti proiectul,apoi
construiti fiecare nod cu BuildNode(click drept de mouse) si in final
asigurati legaturile dintre modulele componente executand un click de
mouse drept pe fila de tip .exe si selectand optiunea Link.Executati apoi
fiecare exemplu si aplicatie sau program si observati modul in care au
fost realizate.Fiecare dintre aceste exemple si programe poate reprezenta
punctul de plecare pentru un program realizat de d-voastra.Un tutorial
foarte bun si o serie de informatii utile despre OWL se gasesc si la
adresa: http://owlnext.
Cea mai simpla aplicatie posibila este deschiderea unei ferestre de tip
Windows:
EXEMPLU: (vezi si OWL_ABC / owl1)
Deschideti un nou proiect,cu New si Project.Eliminati fila .rc si cea .def
cu DeleteNode,apoi adaugati cu AddNode fila default.def din directorul
LIB si o fila de tip .rc care contine urmatorul text:
#include <owl/owlapp.rc>
#include <owl/except.rc>
#include <owl/window.rh>
Apoi deschideti fila de tip .cpp si introduceti urmatorul text:
#include <owl/pch.h>
#include <owl/applicat.h>

int
OwlMain(int, char* [])
{
return TApplication("Titlul Ferestrei !").Run();
}
Compilati,construiti nodul cu build node,apoi selectati fila .exe,constru-
iti nodul cu Build Node si adigurati legaturile cu Link (dupa click drept
de mouse).Proiectul este finalizat si poate fi executat.
Un exercitiu identic este si la adresa Examples\Owl\Apps\Hello.
Puteti utiliza direct exercitiul "Hello" ca punct de pornire.Nu trebuie
decat sa schimbati textul pentru titlul ferestrei.In general,nu trebuie
sa evitati sa folositi modulele realizate anterior.

-58-
Daca analizam putin exemplul de mai sus,observam ca include doua file
header,dupa care apeleaza functia Run() pentru un obiect de tip TAppli-
cation(functia Run() apeleaza constructorul clasei,care realizeaza un
obiect din tipul respectiv).
Biblioteca <owl/applicat.h> contine definita clasei TApplication.Nu
exista in manualul Help o descriere a claselor de obiecte din OWL,astfel
incat va trebui sa va obisnuiti sa lucrati direct cu fila header,sau sa
extrageti pe niste planse toate obiectele impreuna cu definitia lor.
Daca deschideti fila "applicat.h" (din INCLUDE\OWL) observati ca printre
alte definitii,contine si definitia pentru clasa TApplication.
In rezumat,definitia este cam asa:
class _OWLCLASS TApplication : virtual public TEventHandler
public Tmodule
public TMsgThread
{ public:
TApplication( ...parametrii primului constructor...);
TApplication( ...parametrii constructorului secund...);
~TApplication();
....o serie de functii Get si Set
CanClose();
Run();
Start();
}
Asadar,clasa TApplication mosteneste clasele TEventHandler,TModule si
TMsgThread,are doi constructori,un destructor si o serie de metode,dintre
care se disting: Run(),CanClose() si Start().
Nu este o regula absoluta,dar foarte multe programe au ca punct de
pornire un obiect derivat in clasa TApplication,deoarece prin simpla
apelare a metodei Run() se va apela automat si constructorul implicit al
obiectului.Daca doriti,puteti sa nu utilizati un astfel de obiect,dar
in acest caz va trebui sa apelati explicit fiecare constructor,sa faceti
toate operatiile de initializare si sa gestionati singur si toate opera-
tiile de eliberare a memoriei.Obiectul TApplication,prin destructorul
implicit asigura si eliberarea automata a memoriei.
Observati ca fila "applicat.h" a inclus automat in memorie si filele
header <owl/module.h>,<owl/eventhan.h> si <winsys/msgthread.h> necesare
pentru derivarea clasei Taplication si respectiv <services/preclass.h>
necesara pentru definitile generice ale optiunilor de compilare.
Cealalata fila header <owl/pch.h> include automat fila <owl/owlcore.h>.
Fila "owlcore.h" incarca in memorie principalele file necesare pentru o
aplicatie obisnuita(adica defs.h,module.h,applicat.h,dc.h,menu.h,window.h
mdi.h,mdichild.h,decmdifr.h,dialog.h,control.h).
Pentru exemplul de mai sus,aceste clase nu sunt necesare,astfel incat
se poate renunta la ele.Fila header <owl/pch.h> a fost inclusa in acest
exemplu doar pentru a va atrage atentia ca exista si aceasta optiune.
Fila "pch.h" este foarte comoda,atunci cand incepeti editarea unui proiect
si nu stiti inca exact ce anume doriti sa faceti.Pentru a evita consulta-
rea permaneta a tuturor filelor,puteti utiliza aceasta optinue,sau respec-
tiv fila "owlall.h> care incarca toate filele din biblioteca OWL.Dupa
ce proiectul este finalizat,puteti sa alegeti cu strictete doar filele
strict necesare,astfel incat executabilul construit sa fie cat mai mic.

-59-
Daca va place sa gestionati singur memoria,renuntati la <owl/pch.h> si
includeti doar filele strict neceare.Construiti executabilul si faceti
comparatia pentru volumul de memorie consumata.Pentru programele mari,
difernta va fi din ce in ce mai nesemnificativa si va fi mai simplu sa
apelati direct "owlall.h".Decizia este in functie de dimensiunea memoriei
de RAM a hard-ului instalat si in functie de importanta programului,sau
in functie de mediul de operare in care lucrati (ce alte programe ruleaza
in paralel).Pe cat posibil se va cauta intotdeauna solutia cea mai eco-
nomica (mai ales daca utilizati mai multe programe in paralel).
Primul program,este extrem de simplu,dar nu lasa prea mult loc pentru
dezvoltare.Se pot utiliza si functiile Start() pentru a efectua o serie
de operatii pregatitoare inainte de a apela constructorul,sau respectiv
CanClose(),pentru a executa o serie de operatii inainte de a apela des-
tructorul.Prin inchiderea ferestrei(cu butonul x),destructorul este
apelat automat si se efectueaza eliberarea automata a memoriei.
Pentru a putea introduce o serie intrega de functii si proceduri noi,
fereastra generata automat de catre constructorul implicit trebuie sa
fie redefinita.Pentru acest scop se va deriva o clasa de tip TFrameWindow
in care se pot apoi introduce procedurile specifice:
EXEMPLU: (vezi si OWL_ABC / owl2 )
Deschideti un proiect nou,ca mai sus,dar in fila .cpp introduceti un text
de genul:
#include <owl/pch.h>
#include <owl/applicat.h>
#include <owl/framewin.h>
class Fer1 : public TWindow {
public:
Fer1(); };
Fer1::Fer1() : TWindow(0,0,0) { };
class Aplicatie1 : public TApplication {
public:
Aplicatie1() : TApplication() { };
void InitMainWindow();
};
void Aplicatie1::InitMainWindow()
{
MainWindow = new TFrameWindow(0,"Test",new Fer1);
}
int
OwlMain(int, char* [])
{
return Aplicatie1().Run();
}
Compilati,construiti si executati.Rezultatul este identic cu cel din exer-
citiul precedent,adica o fereastra simpla de tip Windows.De aceasta data
insa am materializat un obiect din clasa TApplication si am utilizat o
clasa denumita Fer1,derivata din TWindow,la care am redefinit constructo-
rul.Apoi am utilizat functia InitMainWindow pentru a deschide o fereastra
de tipul Fer1,cu ajutorul constructorului implicit al obiectului de tip
TApplication.Constructia pare complicata,dar este foarte prcatica pentru
redefinirea proprietatilor ferestrei in care vom executa operatii.

-60-
Deschideti fila "framewin.h" si observati definitia pentru clasa _OWLCLASS
TFrameWindow (are un numar foarte mare de metode implicite).
Fila <owl.framewin.h> a fost inclusa automat de <owl/pch.h>,dar a fost
specificata si explicit pentru a va atrage atentia asupra acestei file.
In continuare,se pot defini noi metode pentru fereastra Fer1.
EXEMPLU: (vezi si OWL_ABC / owl3)
Deschideti un nou proiect,iar in fila .cpp introduceti un text de genul:
#include <owl/pch.h>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/dc.h>
class Fer1: public TWindow {
public:
Fer1();
void EvLButtonDown(uint, TPoint&);
void EvRButtonDown(uint, TPoint&);
DECLARE_RESPONSE_TABLE(Fer1); };
DEFINE_RESPONSE_TABLE1(Fer1,TWindow)
EV_WM_LBUTTONDOWN,
EV_WM_RBUTTONDOWN,
END_RESPONSE_TABLE;
Fer1::Fer1():TWindow(0,0,0) { };
void Fer1::EvLButtonDown(uint,TPoint& point)
{
TClientDC dc1(*this);
dc1.Rectangle(point.x-50,point.y-50,point.x+50,point.y+50);
dc1.TextOut(point,"Text:",5);
};
void Fer1::EvRButtonDown(uint,Tpoint&)
{
MessageBox("Butonul drept a fost apasat !","Text:",MB_OK);
};
class Aplicatie1 : public TApplication {
public:
Aplicatie1() : TApplication() { };
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{
MainWindow = new TFrameWindow(0,"Test",new Fer1);
};
int
OwlMain(int,char* [])
{
return Aplicatie1().Run();
}
Compilati,construiti,link-ati si apoi executati aplicatia.In fereastra
Test executati alternativ click-uri de mouse cu butonul drept sau stang.
Nu este necesar sa rescrieti toata fila.Puteti copia fila precedenta si
apoi adaugati doar metodele nou introduse pentru clasa Fer1.In general,
programele si aplicatiile se pot dezvolta prin modificarea,actualizarea
sau transformarea unor aplicatii precedente.

-61-
Observati elementele noi din program: fila header "owl/dc.h",cele
doua functii destinate pentru controlul butoanelor mouse si tabela de
control a evenimentelor Windows denumita RESPONSE_TABLE.
Fila header <owl/dc.h> asigura tot ce este necesar pentru a putea
executa functii si operatii de tip grafic.Deschideti fila si observati
clasele definite in aceasta fila.Cea mai importanta este clasa TDC in
care sunt incluse atat contextul de dispozitiv grafic cat si principalele
functii grafice.Celelalate clase (TWindowDC,TDesktopDC,TClientDc,TPaintDC,
TMetaFileDc,TCreatedDC,TIC,TMemoryDC,TDibDC,TPrintDC) contin seturi de
metode specializate pentru diverse situatii speciale.Trebuie remarcat
faptul ca pentru fiecare clasa,contextul de dispozitiv este detectat auto-
mat de catre constructorul clasei.Dupa derivarea unui obiect nu mai este
necesar sa tineti evidenta acestui factor.Toate functiile membru se pot
apela direct.In exemplul de mai sus,au fost utilizate doar functiile
Rectangle() si TextOut().
Urmatorul element de noutate il reprezinta modul de interpretare si
control al evenimentelor de tip Windows.Toate programele care realizeaza
o interfata grafica de tip Windows exploateaza mesajele sistemului de
operare Windows.Pentru fiecare operatie executata in sistem,Windows emite
un mesaj specializat.Mesajele Windows sunt denumite dupa operatia care a
determinat emiterea mesajului la care se adauga prefixul WM_.Principalele
mesaje Windows sunt: WM_ACTIVATE,WM_CANCELMODE,WM_CHAR,WM_CHILDACTIVATE,
WM_CLEAR,WM_CLOSE,WM_COMMAND,WM_COPY,WM_CREATE,WM_DELETEITEM,WM_ENABLE,
WM_GETTEXT,WM_INITMENU,WM_KEYDOWN,WM_KEYUP,WM_LBUTTONDOWN,WM_LBUTTONUP,
WM_MENUSELECT,WM_MOUSEMOVE,WM_MOVE,WM_PAINT,WM_PASTE,WM_POWER,WM_QUIT,
WM_RBUTTONDOWN,WM_RBUTTONUP,WM_SIZE,WM_TIMER,WM_UNDO,WM_USER,WM_VSCROLL.
Lista completa este mult mai lunga.Manualul Help din C++ nu contine lista
acestor evenimente,dar puteti utiliza manualul Help din Borland Pascal,sau
orice alta sursa de documentare.Modul de interceptare si interpretare al
acestor mesaje este diferit de la un program la altul.In Pascal trebuie
sa scrieti o bucla continua in care sa fie preluate toate mesajele si sa
adaugati o conditie oarecare de selectie.In Delphi,eveniemetele sunt
rezolvate integral cu ajutorul utilitarului Object Inspector,iar in Visual
C++ evenimentele sunt ordonate sub forma unei grile denumita "message map".
In OWL,evenimentele sunt ordonate sub forma de tabel de corespondenta,
denumit RESPONSE_TABLE,in care la fiecare eveniment de tip Windows consi-
derat a fi semnificativ se asociaza o anumita functie de raspuns prin care
se declanseaza o anumita serie de operatii specifice.In acest fel,dintre
toate evenimentele Windows,se vor selecta doar cele care au semnificatie
pentru aplicatia respectiva.De exemplu,daca in exercitiul de mai sus exe-
cutati diverse deplasari ale indicatorului mouse,se vor declansa o serie
intreaga de mesaje de tip WM_MOUSEMOVE.Aceste mesaje vor fi ignorate de
catre aplicatie.Singurele mesaje cu semnificatie sunt WM_LBUTTONDOWN si
respectiv WM_RBUTTONDOWN,adica cele care au fost incluse in RESPONSE_TABLE
Pentru fiecare astfel de evenimet,trebuie sa fie definita o functie
de raspuns la eveniment si un macro de identificare a evenimentului.Prin
conventie,functia de raspuns la evenimet are acelasi nume ca si mesajul
Windows corespunzator,la care se adauga si prefixul "Ev".Exemplu: functia
care raspunde la mesajul WM_PAINT va fi denumita EvPaint().Dupa prefix,
prima litera din numele mesajului va fi scrisa cu majuscula,iar restul
vor fi scrise cu litere mici.

-62-
Pentru identificarea fiecarui mesaj,se va defini un macro specializat.
Numele acestui macro va fi format din numele mesajului,la care se va
adauga si prefixul "EV_".In acest caz toate literele vor fi scrise cu
majuscule.Exemplu: pentru mesajul WM_PAINT macroul de identificare va fi
EV_WM_PAINT.
Asadar,pentru fiecare mesaj Windows se poate scrie un macro care il
identifica si o functie care executa o serie de comenzi in momentul in
care mesajul a fost receptionat.
Editarea tabelei de raspuns la evenimentele Windows se face in patru
etape succesive:
1.Se declara tabela cu: DECLARE_RESPONSE_TABLE(numele clasei)
2.Definitia incepe cu: DEFINE_RESPONSE_TABLEx(clasa,clase receptive)
3.Se introduc macrourile de identificare a mesajelor (gen EV_WM_PAINT)
4.Se termina definitia tebelei cu: END_RESPONSE_TABLE
In etapa a doua,x reprezinta numarul de parametri,adica numarul de clase
care vor receptiona mesajul respectiv.Primul paramatru este clasa in care
este definit tabelul de raspunsuri,iar cel de al doilea parametru este
clasa care va receptiona mesajul.In exemplu,tabelul este definit in Fer1,
iar mesajul va fi receptionat de catre TWindow.In acest caz,exista o
singura clasa receptiva,adica un singur parametru,astfel incat definitia
va fi: DEFINE_RESPONSE_TABLE1(Fer1,TWindow).In cazul in care ar fi
existat si o alta clasa,in care exista o tabela de raspuns si in care se
vor receptiona mesajele,de exemplu o fereastra denumita TWindow2,definitia
ar fi fost de genul: DEFINE_RESPONSE_TABLE2(Fer1,TWindow,Twindow2).
Asadar,pentru fiecare mesaj exista o functie de raspuns si un macro de
identificare.In exemplu,daca se apasa butonul mouse stang,sistemul de
operare Windows va emite un mesaj de tip WM_LBUTTONDOWN.Acest mesaj va fi
interceptat si identificat de catre macroul EV_WM_LBUTTONDOWN,care va
declansa automat functia de raspuns EvLButtonDown().
Practic tabela de raspunsuri tine locul unei proceduri in care toate
mesajele emise de sistemul Windows sunt receptionate si filtrate prin
macrourile definite.Daca un mesaj corespunde cu macro-ul,se va declansa
automat functia corespunzatoare.
Prin acest mecanism,puteti programa orice fel de suita de algoritmi de
raspuns autoamt la un eveniment oarecare.Observati faptul ca un singur
mesaj Windows poate declansa simultan mai multe functii de raspuns,in
cazul in care exista mai multe clase(obiecte) care asteapta evenimentul
respectiv.De exemplu,o simpla apasare de buton poate declansa o serie
intreaga de functii de raspuns.Reciproc,este posibil ca raspunsul unei
anumite functii sa nu poata fi declansat decat in urma unei combinatii de
mesaje Windows(fiecare mesaj activeaza o functie care concura cu alte
functii pentru a forma raspunsul final).
Ca rezultat,fata de alte programe,OWL permite o interpretare extrem
de discriminativa a mesajelor Windows,fara riscul de a se declansa si
"raspunsuri accidentale".
Alegeti cu atentie mesajul Windows utilizat pentru declansarea unei
anumite functii.Exista si mesaje nediscriminative,care pot fi eliberate
de catre mai multe evenimente disticte.Daca RESPONSE_TABLE este mai ampla,
este bine sa faceti si o mica schema cu fiecare functie de raspuns si cu
fiecare macro care identifica mesajul,astfel incat sa nu existe mesaje
incrucisate sau macro-uri fara functie de raspuns.

-63-
Tot o tabela de comenzi se utilizeaza si pentru a monitoriza optiunile
unui meniu:
EXEMPLU: (vezi si OWL_ABC \ owl4 )
Deschideti un proiect nou,iar in fila .cpp introduceti un text de genul:
#include <owl/pch>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/editfile.h>
#include <owl/editfile.rh>
#include <services/cstring.h>
class Fer1 : public TApplication {
public:
Fer1() : TApplication("Editor:"){};
protected:
void InitMainWindow();
void CmFileNew();
void CmFileopen();
DECLARE_RESPONSE_TABLE(Fer1); };
DEFINE_RESPONSE_TABLE1(Fer1,TApplication)
EV_COMMAND(CM_FILENEW,CmFileNew),
EV_COMMAND(CM_FILEOPEN,CmFileOpen);
END_RESPONSE_TABLE;
void
Fer1::InitMainWindow()
{
MainWindow = new TFrameWindow(0,Name,new TEditFile);
MainWindow -> AssignMenu(IDM_EDITFILE);
MainWindow ->Attr.AccelTable = IDA_EDITFILE;
MainWindow ->SetIcon(this,201);
};
void Fer1::CmFileNew()
{
TEditFile* fila=TYPESAFE_DOWNCAST(MainWindow->GetClientWindow(),TEditFile);
CHECK(fila);
fila->NewFile();
};
void Fer1::CmFileOpen()
{
TEditFile* fila=TYPESAFE_DOWNCAST(MainWindow->GetClientWindow(),TEditFile);
CHECK(fila);
fila->Open();
};
int
OwlMain(int,char* [])
{
return Fer1().Run();
}
Pentru definitia meniului propriu zis,este necesara o fila de resurse
de tip .rc.Executati un click de mouse drept si stergeti din proiect fila
.def si fila .rc cu optiunea DeleteNode.Deschideti o fila noua(din File,cu
New si TextEdit) si introduceti urmatorul text:

-64-
#ifndef WORCKSHOP_INVOKED
#include <windows.h>
#endif
#include <owl/editfile.rc>
#include <owl/editsear.rc>
#include <owl/docview.rc>
Salvati fila cu numele de owl4.rc,apoi includeti fila in proiect cu
Add Node.Compilati,construiti si link-ati proiectul,apoi executati apli-
catia.Programul de mai sus,deschide un editor de text.Puteti deschide sau
edita orice fila de tip ASCI.
Pentru exemplul de mai sus,am ales un meniu gata definit in filele
editfile.rc si editfile.rh.In directorul INCLUDE\OWL exista mai multe
astfel de file cu extensia .rc si .rh.Puteti utiliza aceste file pentru
a utiliza meniurile predefinite.In program,meniul se incarca cu comanda
AssignMenu().
Pentru a atribui cate o functie la fiecare optiune din meniu,se va
declara si defini o tabela de raspunsuri.Toate optiunile exploateaza
mesajul Windows WM_COMMAND,la care se adauga cate o comanda specifica
pentru fiecare optiune.Numele comenzii se scrie cu majuscule si se adauga
prefixul "CM_".De exemplu,pentru FileOpen comanda va fi: CM_FILEOPEN.
Pentru fiecare comanda se va asocia si o functie de raspuns care va
executa o serie de operatii,in momentul in care se receptioneaza mesajul
WM_COMMAND + comanda specifica (Exemplu: WM_COMMAND + CM_FILEOPEN).Functia
va avea acelasi nume cu comanda optiunii + prefixul "Cm".Prima litera din
mumele functiei se va scrie cu litera mare (majuscula) iar restul litere-
lor se vor scrie cu litere mici.Exemplu: pentru comanda CM_FILEOPEN se
va utiliza functia de raspuns CmFileopen().In definitia tabelei de raspuns
fiecare astfel de pereche comanda/functie de raspuns va fi introdusa cu
o formula formata din prefixul EV_COMMAND + (comanda,functia).De exemplu,
pentru comanda CM_FILEOPEN si functia CmFileOpen() se va utiliza formula:
EV_COMMAND(CM_FILEOPEN,CmFileOpen)
Functia de raspuns trebuie sa fie de tip void (fara parametri).Nu este
absolut necesar,dar se prefera ca functiile de raspuns sa fie declarate
cu specificatorul "protected:",pentru a nu avea vizibilitate si in afara
clasei in care au fost declarate (se evita coruperea datelor).
Pentru a stabili un punct de intrare prioritar in tabela de raspunsuri
la mesaje,se poate utiliza o formula de genul: EV_COMMAND_ENABLE().In
acest caz,punctul de intrare respectiv va avea prioritate fata de cele-
lalte si va fi selectat implict la initializarea obiectului.De exemplu,
daca o bara de meniu contine mai multe optiuni,se poate utiliza aceasta
formula pentru ca una dintre optiuni sa fie selectata automat in momentul
constructiei obiectului respectiv.
Puteti utiliza exemplul owl4 pentru a deschide orice fila de tip text.
Alegeti optiunea File/Open iar pentru Files of type selectati "All Files"
si apoi deschideti orice fila de pe disc (preferabil cu extensia .txt).
Pentru a utiliza un meniu personalizat,se procedeaza in mod similar,dar
in fila de resurse va trebui sa definiti toate comenzile si sa introduceti
definitia meniului.Puteti utiliza si meniuri care au fost editate sub
forma de file de resurse,dar in alte programe sau limbaje de programare{
De exemplu : resurse Visual C++,resurse Pascal,resurse Delphi etc.).
Pentru a realiza un meniu grafic,puteti utiliza un program de genul:

-65-
EXEMPLU: (vezi si OWL_ABC \ owl5 )
Fila .cpp:
#include <owl/pch>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/dc.h>
#define CM_PATRAT 201
#define CM_CERC 202
class Fer1 : public TWindow {
public:
Fer1();
protected:
void CmPatrat();
void CmCerc();
DECLARE_RESPONSE_TABLE(Fer1); };
DEFINE_RESPONSE_TABLE1(Fer1,TWindow)
EV_COMMAND(CM_PATRAT,CmPatrat),
EV_COMMAND(CM_CERC,CmCerc),
END_RESPONSE_TABLE;

Fer1::Fer1():TWindow(0,0,0){};
void Fer1::CmPatrat()
{
TClientDC dc1(*this);
TPen pen1(RGB(250,0,0),10);
dc1.SelectObject(pen1);
dc1.Rectangle(50,50,200,200);
MessageBox("Patrat","text:",MB_OK);
}
void Fer1::CmCerc()
{
TClientDC dc2(*this);
TPen pen2(RGB(0,0,250),10);
dc2.SelectObject(pen2);
dc2.Ellipse(70,70,180,180);
MessageBox("Cerc","text:",MB_OK);
};
class Aplicatie1 : public TApplication {
public:
Aplicatie1() : TApplication(){};
void InitMainWindow();
};
void Aplicatie1::InitMainWindow()
{
MainWindow = new TFrameWindow(0,"Meniu 2",new Fer1);
GetMainWindow() -> AssignMenu("GRAFIC");
};
int OwlMain(int,chat* [])
{
return Aplicatie1().Run();
};
-66-
Eliminati fila .def cu DeleteNode.
Inlocuiti fila .rc cu urmatoarea fila:
#define CM_PATRAT 201
#define CM_CERC 202

#ifdef RC_INVOKED

GRAFIC MENU
{
POPUP "&DESEN"
{
MENUITEM "&Patrat", CM_PATRAT
MENUITEM "&Cerc", CM_CERC
};
};
#endif
Compilati,construiti proiectul,verificati legaturile cu Link,apoi execu-
tati aplicatia.Puteti utiliza cele doua optiuni ale meniului,pentru a
desena fie un patrat,fie un dreptunghi.Observati ca fata de exemplul owl3
am introdus si cate un obiect de tip TPen,pentru a putea modifica culoarea
si grosimea penitei de desenare.
In mod similar,puteti edita orice meniu.Puteti utiliza exemplul de
mai sus,sau orice alt exemplu functional,pentru a edita aplicatii moi.Nu
strica sa fiti putin "escroci" si sa efectuati doar operatiile necesare
pentru a transforma un exemplu anterior,dar numai dupa ce a-ti inteles
perfect modul de constructie pentru fiecare aplicatie.
In situatiile in care programul este editat "perfect" si totusi nu
functioneaza,trebuie reeditata fila .ide(fila proiect).Pentru acest scop,
deschideti un nou proiect,alegeti acceasi cale de acces si acelasi nume
pentru proiect,apoi suprascrieti proiectul.Proiectul nou deschis va
contine aceleasi file ca si proiectul anterior,dar va avea o fila .ide
noua.Compilati si construiti din nou,asigurati legaturile cu "Link" si
apoi executati aplicatia.
Pentru functiile grafice,regulile sunt aceleasi ca pentru API Windows,
dar sunt grupate intr-un singur obiect grafic,astfel incat sa fie cat mai
usor de apelat.Deschideti fila header <owl/dc.h> si observati cu atentie
metodele obiectului TDC.Penita grafica este controlata de obiecte deri-
vate din clasa TPen,iar pensula(utilizata pentru a umple suprafetele)
este controlata de obiecte derivate din TBrush.Alte obiecte utile sunt
cele derivate din clasele TFont,TBitmap,TPallete,TIcon,TCursor,TRegion,
TDib,TMetaFilePict si TEnhMetaFilePict.
Pentru a putea utiliza un obiect de tip TPen,TBrush sau TFont se va
utiliza metoda SelectObject() iar pentru a reveni la valorile implicite
se pot utiliza functiile: RestorePen(),RestoreBrush(),RestoreFont()etc.
Pentru culoarea de fond se pot utiliza functiile: GetBkColor() si SetBk-
Color() iar pentru text GetTextColor() si SetTextColor(),etc.
Exista si o serie de functii care permit salvarea imaginilor grafice in
memoria clipboard si apoi utilizarea lor la nevoie,sau permit operatii
complexe cu metafile in care sunt arhivate datele necesare.Prin combinarea
acestor functii,se obtin efecte de animatie.Alte functii opereaza cu
resurse de tip Icon,Cursor,BitMap etc.
-67-
Unul dintre cele mai utilizate elemente ale unei interfete grafice este
butonul simplu.Obiectul de tip buton este definit in fila <owl/button.h>
si poate fi apelat ca un obiect oarecare.Pentru a crea oricare dintre
elementele interfetei trebuie apelat constructorul obiectului.Constructo-
rul poate fi apelat direct,sau cu ajutorul operatorului "new".
EXEMPLU: (vezi si OWL_ABC / owl6)
Deschideti un prooiect nou si introduceti in fila .cpp:
#include <owl/pch>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/button.h>
const uint16 ID_BUTTON = 101;
class Fer1 : public TWindow {
public:
Fer1();
protected:
void HandleButtonMsg();
DECLARE_RESPONSE_TABLE(Fer1);
};
DEFINE_RESPONSE_TABLE1(Fer1,TWindow)
EV_COMMAND(ID_BUTTON,HandleButtonMsg),
END_RESPONSE_TABLE;

Fer1::Fer1(): TWindow(0,0,0) {
new TButton(this,ID_BUTTON,"Buton 1",50,50,150,30,false);
};
void fer1::HandleButtonMsg()
{
TClientDC dc1(*this);
TBrush br1(RGB(0,250,0));
dc1.SelectObject(br1);
dc1.FillRect(100,100,200,200,br1);
MessageBox("Button1","text:",MB_OK);
};
class Aplicatie1 : public TApplication {
public:
Aplicatie1() : TApplication() {};
void InitMainWindow();
};
void Aplicatie1::InitMainWindow()
{
MainWindow = new TFrameWindow(0,"Test",new Fer1);
};
int OwlMain(int,char* [])
{
return Aplicatie1().Run();
};
Pentru controlul operatiilor declansate de apasarea butonului se declara
o tabela de raspuns,la fel ca pentru orice alt eveniment.Functia de ras-
puns este declansata la aparitia mesajului WM_COMMAND + ID_BUTTON.La fie-
care apasara de buton,Windows emite automat cele doua mesaje.

-68-
In exemplu,apasarea butonului genereaza un mesaj de raspuns si executa
o operatie grafica de umplere a unei suprafete,folosind un obiect de tip
TBrush.In constructor,valorile numerice specifica pozitia si dimensiuni-
le butonului.Pentru detalii,studiati fila <owl/button.h>.
Pentru a va simplifica munca de programare,este bine sa extrageti din
filele header obiectele cu care lucrati frecvent,impreuna cu definitia
constructorilor si metodele pe care le utilizati frecvent.Este bine sa
aveti si o schita de ansamblu cu arborele genealogic al tuturot obiectelor
OWL,pentru a urmarii cat mai usor metodele mostenite de la ancestori.
(copiati schita de pe Internet,sau desenati o schita proprie).
Un alt element de interfata,extrem de comun,este textul static(TStatic).
Se utilizeaza pentru a introduce diverse etichete explicative.Este definit
in fila <owl/static.h>
EXEMPLU: (vezi si OWL_ABC / owl7 )
Deschideti un proiect nou si complectati fila .cpp astfel:
#include <owl/pch.h>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/static.h>
const uint16 ID_STATIC1 = 101;
const uint16 ID_STATIC2 = 102;
const uint16 ID_STATIC3 = 103;
class Fer1 : public TWindow {
public:
Fer1(); };
Fer1::Fer1():TWindow(0,0,0) {
new TStatic(this,ID_STATIC1,"Text static 1 (eticheta)",50,50,250,20);
new TStatic(this,ID_STATIC2,"Al doilea text static" ,50,100,250,50,50);
new TStatic(this,ID_STATIC3,"Eticheta cea mai lata",50,200,250,100,50);
};
class Aplicatie1 : public TApplication {
public:
Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{
MainWindow = new TFrameWindow(0,"Test",new Fer1);
};
int OwlMain(int,char* [])
{
return Aplicatie1().Run();
};
Codul intern de identificare a elementului (ID_STATIC) se utilizeaza doar
pentru a putea identifica automat elementul de interfata dorit.Daca nu
va fi necesar in program,se poate utiliza orice valoare numerica int (-1).
Valorile numerice din constructor descriu pozitia si dimensiunile.
In toate exemplele am pastrat si fila <owl/pch.h> pentru ca sa fie cat
mai usor de transformat.Toate filele header standard au conditii de pre-
compilare astfel concepute incat sa nu se incarce de doua ori in memorie.
Puteti transforma exemplele dupa bunul plac.Restul filelor header va atrag
doar atentia asupra filelor sursa in care sunt definite obiectele.

-69-
Un alt element de interfata este butonul CheckBox.Este definit in fila
<owl/checkbox.h> si este format dintr-un buton de selectie si un element
static de tip text.Se utilizeaza pentru a selecta sau pentru a seta un
grup de date sau de valori,sau pentru a declansa un set de operatii.
EXEMPLU: ( vezi si OWL_ABC / owl8 )
Deschideti un nou proiect si editati fila .cpp astfel:
#include <owl/pch.h>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/checkbox.h>
const uint16 ID_CHECKBOX1 = 101;
const uint16 ID_CHECKBOX2 = 102;
class Fer1 : public TWindow {
public: Fer1();
protected:
void HandleCheckBox1Msg();
void HandleCheckBox2Msg();
TCheckBox* CB1;
TCheckBox* CB2;
DECLARE_RESPONSE_TABLE(Fer1); };
DEFINE_RESPONSE_TABLE1(Fer1,TWindow)
EV_COMMAND(ID_CHECKBOX1,HandleCheckBox1Msg),
EV_COMMAND(ID_CHECKBOX2,HandleCheckBox2Msg),
END_RESPONSE_TABLE;
Fer1::Fer1():TWindow(0,0,0) {
CB1=new TCheckBox(this,ID_CHECKBOX1,"Control 1",50,50,150,20,0);
CB2=new TCheckBox(this,ID_CHECKBOX2,"Control 2",50,100,150,20,0);
void Fer1::HandleCheckBox1Msg()
{ if (CB1->GetCheck() == BF_CHECKED)
MessageBox("Control 1 = SELECTAT","text:",MB_OK);
else
MessageBox("Control 1 = Neselectat","text:",MB_OK); };
void Fer1::HandleCheckBox2Msg()
{ if (CB2->GetCheck() == BF_CHECKED)
MessageBox("Control 2 = SELECTAT","text:",MB_OK);
else
MessageBox("Control 2 = Neselectat","text:",MB_OK); };
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{
MainWindow = new TFrameWindow(0,"Test",new Fer1);
};
int OwlMain(int,char* [])
{
return Aplicatie1().Run();
};
Observati ca cele doua controale pot fi apelate prin pointeri (CB1 si CB2)
iar cu ajutorul operatorului -> (selectia membrului dintr-un pointer) se
pot apela metodele obiectului pointat.

-70-
Un alt tip de buton,este butonul "radio",definit in fila "radiobut.h".
Este asemanator cu butonul "checkbox",dar,atunci cand exista mai multe
butoane radio incluse in acelasi spatiu de vizibilitate(in acelasi obiect)
selectarea unuia dintre butoane le deselecteaza automat pe celelalte.
EXEMPLU: (vezi si OWL_ABC / owl9)
#include <owl/pch>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/radiobut.h>
const uint16 ID_RADIOB1 = 101;
const uint16 ID_RADIOB2 = 102;
class Fer1 : public TWindow {
public: Fer1();
protected:
void HandleRadioB1Msg();
void HandleRadioB2Msg();
TRadioButton* RB1;
TRadioButton* RB2;
DECLARE_RESPONSE_TABLE(Fer1); };
DEFINE_RESPONSE_TABLE(Fer1,TWindow)
EV_COMMAND(ID_RADIOB1,HandleRadioB1Msg),
EV_COMMAND(ID_RADIOB2,HandleRadioB2Msg),
END_RESPONSE_TABLE;
Fer1::Fer1():TWindow(0,0,0) {
RB1=new TRadioButton(this,ID_RADIOB1,"Radio 1",50,50,150,20,0);
RB2=new TRadioButton(this,ID_RADIOB2,"Radio 2",50,100,150,20,0); };
void Fer1::HandleRadioB1Msg()
{
MessageBox("Butonul Radio 2","text:",MB_OK); };
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
void InitMainWindow();
};
void Aplicatie1::InitMainWindow()
{
MainWindow = new TFrameWindow(0,"Test",new Fer1);
};
int OwlMain(int,char* [])
{
return Aplicatie1().Run();
};
Compilati,construiti,link-ati si apoi executati aplicatia.Pentru a selecta
un buton,executati un click de mouse,in orice loc din aria butonului.
Observati ca selectarea unui buton,deselecteaza automat celalat buton
(in cazul in care era selectat anterior).Prin urmare,acest gen de butoane
se poate utiliza atunci cand se doreste selectia unei singure optiuni,
dintr-un grup oarecare de optiuni.Pentru a forma mai multe grupuri de
optiuni,care sa nu se excluda reciproc,trebuie ca fiecare grup de butoane
sa fie inclus intr-un obiect distinct.Pentru acest scop,se poate utiliza
un obiect simplu,denumit "groupbox" si definit in fila "groupbox.h".
Obiectul groupbox,limiteaza spatiul de vizibilitate si asigura si cateva

-71-
metode auxiliare (GetText(),SetText(),GetNotifyParent() etc.).
EXEMPLU: (vezi si OWL_ABC / owl10)
deschideti un nou proiect si editati fila .cpp:
#include <owl/pch.h>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/radiobut.h>
#include <owl/groupbox.h>
const uint ID_RADIOB1 = 101;
const uint ID_RADIOB2 = 102;
const uint ID_GROUPBOX = 103;
class Fer1() : public TWindow {
public: Fer1();
protected:
void HandleGroupBoxMsg(uint);
TRadioButton* RB1;
TRadioButton* RB2;
TGroupBox* Grup1;
DECLARE_RESPONSE_TABLE1(Fer1); };
DEFINE_RESPONSE_TABLE(Fer1); };
EV_CHILD_NOTIFY_ALL_CODES(ID_GROUPBOX,HandleGroupBoxMsg),
END_RESPONSE_TABLE;
Fer1::Fer1():TWindow(0,0,0) {
Grup1=new TGroupBox(this,ID_GROUPBOX,"Grup 1",20,20,200,200);
RB1=new TRadioButton(this,ID_RADIOB1,"Radio 1",50,50,150,20,Grup1);
RB2=new TRadioButton(this,ID_RADIOB2,"Radio 2",50,100,150,20,Grup1); };
void Fer1::HandleGroupBoxMsg(uint)
{
if( RB1->GetCheck() == BF_CHECKED)
MessageBox("Butonul Radio 1 este SELECTAT","text:",MB_OK);
if( RB2->GetCheck() == BF_CHECKED)
MessageBox("Butonul Radio 2 este SELECTAT","text:",MB_OK); };
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
void InitMainWindow();
};
void Aplicatie1::InitMainWindow()
{
MainWindow = new TFrameWindow(0,"Test",new Fer1);
};
int OwlMain(int,char* [])
{
return Aplicatie1().Run();
}
Compilati,construiti,link-ati si apoi executati aplicatia.In acest caz,
cele doua butoane radio vor fi incluse in caseta denumita "Grup1".Se pot
introduce in fereastra "Fer1" mai multe astfel de grupuri de optiuni,
astfel incat fiecare grup sa fie independent de celelalte.Observati cele
doua modificari din program: 1.constructorul celor doua butoane radio are
ca ultim paramteru obiectul care le contine(Grup1) si 2. tabela de raspuns
utilizeaza comanda CHILD_NOTIFY_ALL_CODES pentru a prelua mesajele de la

-72-
obiectul din interior(obiectul "child") in loc de a prelua mesajele de la
fereastra principala.Prin acest tip de compartimentare,mesajele fiecarui
grup de butoane nu pot corupe mesajele unui alt grup.Compartimentarea se
poate face utilizand orice alt tip de obiecte(de exemplu:doua ferestre
diferite) dar volumul de memorie ocupata per obiect va fi mult mai mare.
Un obiect asemanator este "checklist" definit in fila "checklst.h".
Obiectul permite selectarea unei optiuni,sau a unui anumit grup de optiuni
dintr-o lista.Se utilizeaza pentru a realiza combinatii complexe intre
mai multe grupuri de optiuni sau elemente distincte.
EXEMPLU: (vezi si OWL_ABC /owl11 )
deschideti un proiect nou si completati fila .cpp astfel:
#include <owl/pch.h>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/checklst.h>
#include <string.h>
const uint16 ID_CHECKLIST = 201;
class Fer1 : public TWindow {
public: Fer1();
protected: bool CanClose();
TCheckList* Lista;
TCheckListItem* Items; };
Fer1::Fer1():TWindow(0,0,0) {
Items= new TCheckListItem[4];
Items[0].SetText("Primul element din lista");
Items[1].SetText("Al doilea element din lista");
Items[2].SetText("Al treilea element din lista");
Items[3].SetText("Ultimul element din lista");
Items[0].Toggle();
Items[1].SetIndeterminate();
Items[2].SetThreeStates(true);
Lista=new TCheckList(this,ID_CHECKLIST,40,40,400,200,Items,4); };
bool Fer1::CanClose() {
char tampon[400];
char text[40];
tampon[0]=0;
for (int i=0;i<4;i++) {
if (Items[i].IsChecked() || Items[i].IsIndeterminate())
{ Items[i].GetText(text,40);
strcat(tampon,text);
strcat(tampon,"\r\n"); }}
MessageBox(tampon,"Elementele selectate sunt: ",MB_OK);
return TRUE; };
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication(){};
void InitmainWindow(); };
void Aplicatie1::InitMainWindow()
{ MainWindow = new TFrameWindow(0,"test",new Fer1); };
int OwlMain(int,char* [])
{ return Aplicatie1().Run(); }
Compilati,construiti si link-ati (eventual cu F7).

-73-
Observati ca fiecare element din lista este la randul sau un obiect de
tip TCheckListItem.Deschideti fila <owl/checklst.h> si observati definitia
celor doua clase.
In constructorul ferestrei am declarat la inceput un obiect de tip
TCheckListItem si am initializat obiectul cu o serie de valori de tip
text,pentru fiecare element.Apoi am apelat cate o metoda(Toggle()...etc.)
pentru a determina aspectul casutei de selectie) si in final am utilizat
operatorul "new" pentru a construi un obiect din tipul TCheckList,la care
am utilizat in constructor obiectul TCheckListItem declarat anterior.
Fiecare element are o casuta de selectie,care poate avea trei aspecte
diferite(selectata,neselectata sau indeterminata=colorata in gri).Pentru
a seta automat casuta se pot utiliza cele trei metode ale obiectului de
tip TCheckListItem(Toggle(),SetIndeterminate() si SetThreeStates() ).
Un alt element nou in program este faptul ca nu am utilizat o tabela
de raspuns prin care sa se contabilizeze fiecare mesaj din fereastra,ci
am utilizat metoda CanClose(),prin care se va efectua doar bilantul final
al evenimentelor din fereastra inainte de inchiderea aplicatiei.
Pentru metoda CanClose() au utilizat o bucla iterativa in care se
verifica starea fiecarui element si se arhiveaza starea acestuia intr-un
tampon temporar de memorie de tip caracter.In final,tamponul de memorie
este afisat intr-o fereastra de tip MassageBox,pentru a semnala elementele
din lista care au fost selectate.
Obiectul de tip TCheckList contine si el o serie de metode proprii,
dintre care cele mai valoroase sunt: EvLButtonDown,EvChar si Update().
Cu ajutorul acestui tip de obiect se pot realiza conexiuni si interactiuni
complexe.Fiecare element din lista poate declansa o anumita actiune,sau
se pot realiza diverse combinatii,prin care doar o anumita combinatie de
elemente selectate sa determine o operatie oarecare.Mai mult,se pot uti-
liza in paralele mai multe obiecte de tip TCheckList,de la care se pot
utiliza si combina una sau mai multe combinatii de optiuni.
Pe masura ca aplicatia incepe sa contina tot mai multe obiecte dis-
tincte,este tot mai greu sa urmariti filiatia fiecarui obiect.Pentru o
orientare rapida se poate utiliza diagrama oferita de meniul View/Classes.
Un alt element extrem de important il reprezinta spatiul de vizibilitate
al fiecarui obiect din interfata.Atunci cand exista un singur obiect,
totul este extrem de simplu.Cand exista mai multe obiecte,este posibil sa
apara interferente intre obiecte,datorita faptului ca un anumit mesaj
Windows poate fi exploatat de catre mai multe obiecte concurente.Pentru
a evita astfel de situatii,trebuie sa alegeti cu atentie tipul de mesaj
care va fi exploatat de catre fiecare obiect in parte,sau sa izolati
obiectele care utilizeaza acelasi mesaj in compartimente diferite.De
obiecei,interferentele apar intre obiectele care utilizeaza un mesaj
extrem de comun(gen WM_ACTIVATE,WM_COMMAND,WM_LBUTTONDOWN etc.).
Pentru a evita orice gen de neplaceri,este bine sa utilizati cat mai
frecvent cate o tabela de raspunsuri,in care pentru fiecare comanda de
identificare se atribuie o anumita functie da raspuns.Se vor evita astfel
situatiile in care mesajele sunt corupte,sunt preluate sau exploatate de
catre alt obiect din interfata sau declanseaza raspunsuri multiple.
Pentru situatiile complexe,sau pentru depanarea unor aplicatii,se poate
desena o "harta de evenimente",pe care specificati in ordinea in care apar
toate evenimetele din program si mesajul Windows eliberat/sau exploatat.

-74-
Un alt element frecvent utilizat in interfata grafica este caseta de
dialog de tip "listbox".Clasa TListBox este definita in fila "listbox.h",
impreuna cu clasa TListBoxData.Clasa TListBox,contine un numar mare de
metode prin care se poate opera asupra datelor(vezi fila "listbox.h").
EXEMPLU: (vezi si OWL_ABC / owl12 )
deschideti un proiect nou si completati fila .cpp astfel:
#include <owl/pch.h>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/listbox.h>
#include <string.h>
const uint16 ID_LISTA1 = 201;
class Fer1 : public TWindow {
public: Fer1();
void SetupWindow();
protected:
TListBox* Lista; };
Fer1::Fer1():TWindow(0,0,0) {
Lista = new TListBox(this,ID_LISTA1,50,50,400,200);
};
void Fer1::SetupWindow()
{
Lista->Create();
Lista->AddString("Prima linie din lista");
Lista->AddString("A doua inregistrare");
Lista->AddString("...................");
Lista->AddString("Ultima inregistrare");
};
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie1 :: InitMainWindow()
{ MainWindow = new TFrameWindow(0,"Test",new Fer1); };
int OwlMain(int,char* [])
{ return Aplicatie1().Run(); }
Compilati,construiti si executati aplicatia.
Pentru a utiliza o astfel de caseta de dialog,se va utiliza operatorul
new si constructorul TListBox,in constructorul ferestrei principale(Fer1),
dupa care se va apela o functie oarecare,in care se pot efectua toate
operatiile de configurare a obiectului.Conventional,se utilizeaza o
functie denumita SetupWindow(),pentru a fi usor de identificat,dar puteti
utiliza orice alta denumire.Obiectul nu poate fi initializat cu valori
in constructorul ferestrei(se returneaza un mesaj de eroare).Pentru a
introduce date in caseta de dialog se pot utiliza metodele AddString(),sau
InsertString().Pentru a alege tipul de caseta listbox se poate utiliza
proprietatea Attr.Style.Casetele de dialog de tip listbox pot fi de trei
tipuri: simple,cu multiselectie sau cu afisare pe mai multe coloane.
Constructorul implicit creaza o caseta simpla in care datele din lista
sunt sortate automat in ordine alfabetica( vezi owl12).Pentru a afisa
datele nesortate,se poate apela prporietatea: Attr.Style ~LBS_SORT.
Toate setarile se vor efectua in functia de configurare(SetupWindow).
-75-
EXEMPLU: ( vezi si OWL_ABC / owl13 )
#include <owl/pch.h>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/listbox.h>
#include <string.h>
const uint ID_LISTA1 = 201;
class Fer1 : public TWindow {
public: Fer();
void SetupWindow();
protected:
TListBox* Lista; };
Fer1::Fer1():TWindow(0,0,0) {
Lista = new TListBox(this,ID_LISTA1,50,50,400,40); };
void Fer1::SetupWindow()
{
Lista->Attr.Style |= LBS_MULTICOLUMN;
Lista->AttrStyle &= ~LBS_SORT;
Lista->SetColumnWidth(100);
Lista->Create();
Lista->AddString("unu");
Lista->AddString("doi");
Lista->AddString("trei");
Lista->AddString("patru");
Lista->AddString("cinci");
};
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{ MainWindow = new TFrameWindow(0,"Test",new Fer1); };
int Owlmain(int,char* [])
{ return Aplicatie1().Run(); };
Compilati,construiti si executati aplicatia.
Observati ca in acest caz,datele sunt afisate pe mai multe coloane,
fara sa fie sortate alfabetic.Practic,datele vor fi afisate decrescator,
in ordinea in care sunt indexate,pana cand se ajunge la limita inferioara
a casetei,dupa care se trece la coloana urmatoare.Fiecare coloana afisata
va avea latimea egala cu cea specificata prin SetColumnWidth().Acest gen
de caseta se prefera atunci cand se lucreaza cu date care respecta un
anumit format (de exemplu siruri de numere cu valori apropiate).
Pentru a utiliza cel de al treilea tip de caseta listbox se poate uti-
liza proprietatea Attr.Style |= LBS_MULTIPLESEL.In acest cas,caseta de
dialog se prezinta la fel ca si cea simpla,dar permite selectarea simulta-
na a unui grup de date.Se utilizeaza pentru a efectua transferuri de date
din caseta listbox in alt obiect,sau pentru a putea realiza diferite
tipuri de setari in care se transfera grupuri de date catre un anumit
utilizator.Exista un numar mare de metode destinate acestui scop.Pentru
a prelua si transfera datele din caseta listbox,este necesara o procedura
dedicata si una dintre functiile Get...(exemple: GetSelString(),GetCount()
GetString(),GetItemData(),GetSelIndex() etc.).
-76-
EXEMPLU: (vezi si OWL_ABC / owl14 )
#include <owl/pch.h>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/listbox.h>
#include <owl/static.h>
#include <string.h>
const uint16 ID_LISTA1 = 201;
class Fer1 : public twindow {
public: Fer1();
void SetupWindow();
void EvRButtonDown(uint,TPoint&);
protected:
TListBox* Lista;
DECLARE_RESPONSE_TABLE(Fer1); };
DEFINE_RESPONSE_TABLE1(Fer1,TWindow)
EV_WM_RBUTTONDOWN,
END_RESPONSE_TABLE;
Fer1::Fer1():TWindow(0,0,0) {
Lista = new (this,ID_LISTA1,50,50,200,100); };
void Fer1::SetupWindow()
{
Lista->Attr.Style &= ~LBS_SORT;
Lista->Create();
Lista->AddString("Prima linie din lista");
Lista->AddString("A doua inregistrare ");
Lista->AddString(".....................");
Lista->AddString("Ultima inregistrare ");
};
void Fer1::EvRButtonDown(uint,TPoint& point)
{
char text1[80];
Lista->GetSelString(text1,20);
TClientDC dc1(*this);
dc1.TextOut(point,text1,20);
};
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{ MainWindow = new TFrameWindow(0,"Test",new Fer1); };
int OwlMain(int,char* [])
{ return Aplicatie1().Run(); }
Compilati,construiti si executati aplicatia.
Selectati una dintre inregistrari cu un click de mouse stang si apoi
executati un click de mouse drept,in orice loc in afara casetei listbox.
Textul selectat va fi transferat la locatia respectiva.Functia GetSel-
String() a preluat datele in tamponul temporar "text1",apoi functia
TextOut() a afisat continutul tampunului.In mod similar se poate utiliza
memoria Clippboard,un stream oarecare,sau tamponul de memorie alocat
unui alt obiect(se transfera datele direct in alt obiect),etc.

-77-
Un alt tip de caseta de dialog este caseta "combobox" definita in fila
"combobox.h" prin clasa TComboBox si prin clasa TComboBoxData.Este un
obiect compus dintr-un obiect de tip TListBox,combinat cu un obiect TEdit.
Ca rezultat,se poate utiliza atat pentru a introduce date,cat si pentru
a selecta o optiune,dintr-un set de optiuni prestabilite.
EXEMPLU: (vezi si OWL_ABC / owl15 )
#include <owl/pch.h>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/combobox.h>
#include <string.h>
const uint16 ID_COMBOBOX = 201;
class Fer1 : public TWindow {
public: Fer1();
void SetupWindow();
protected:
TComboBox* Caseta1; };
Fer1::Fer1() : TWindow(0,0,0) {
Caseta1 = new TComboBox(this,ID_COMBOBOX,50,50,400,200,CBS_SIMPLE,25);
};
void Fer1::SetupWindow()
{
Caseta1->Create();
Caseta1->AddString("Prima linie din lista");
Caseta1->AddString("A doua inregistrare ");
Caseta1->AddString(".....................");
Caseta1->AddString("Ultima inregistrare ");
};
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{ MainWindow = new TFrameWindow(0,"Test",new Fer1); };
int OwlMain(int,char* [])
( return Aplicatie1().Run(); }
Compilati,construiti si executati proiectul.
Observati ca obiectul de interfata contine o caseta de editare,in care
se pot introduce date,si o lista de optiuni prestabilite.Selectarea uneia
dintre optiuni copiaza automat textul in caseta de editare,ca si cand ar
fi fost introdus de la tastatura.Din caseta de editare,textul poate fi
preluat cu una dintre metode (GetText(),GetEditSel(),GetItemData(),etc.).
La fel ca si pentru TListBox,toate metodele se vor apela cu ajutorul
unei functii distincte,diferita de constructor (functia Setup).
Exista trei tipuri de caseta "combobox": simpla,cu defilare a listei
de optiuni si combinata.Pentru selectarea tipului de caseta se utilizeaza
in constructor unul dintre urmatorii parametri: CBS_SIMPLE (caseta simpla)
CBS_DROPDOWN(caseta cu defilarea listei) si CBS_DROPDOWNLIST(caseta combi-
nata).Caseta simpla afiseaza lista de optiuni permanent,iar celelalte doua
afiseaza lista doar in momentul selectiei.Alegerea tipului de caseta se
va face in functie de spatiul existent in interfata si in functie de
aspectul grafic dorit.

-78-
Pentru introducerea si editarea datelor exista si un obiect specializat
definit prin clasa TEdit in fila "edit.h".Contine o caseta de dialog in
care se pot introduce date activ,sau se pot prelua date din alta locatie
(dintr-o fila,dintr-un stream,dintr-un alt obiect,dintr-un port etc.).
EXEMPLU: (vezi si OWL_ABC /owl16 )
#include <owl/pch.h>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/edit.h>
#include <string.h>
const uint16 ID_EDIT = 201;
class Fer1 : public TWindow {
public: Fer1();
void SetupWindow();
protected:
TEdit* Editor1; };
Fer1::Fer1():TWindow(0,0,0) {
Editor1 = new TEdit(this,ID_EDIT1,"TEXT INITIAL:",50,50,400,200,TRUE);
};
void Fer1::SetUpWindow()
{
Editor1->Create();
};
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{ MainWindow = new TFrameWindow(0,"Test",new Fer1); };
int OwlMain(int,char* [])
{ return Aplicatie1().Run(); }
Compilati,construiti si executati proiectul.
Caseta de editare poate fi astfel dimensionata incat sa corespunda cat
mai bine scopului propus.Pentru ca in caseta sa se poata edita texte pe
mai multe randuri,este necesar ca parametrul "multiline" sa fie setat
TRUE (vezi definitia constructorului din fila edit.h).Daca acest parametru
lipseste,se va utiliza valoarea implicita,care este "FALSE",caz in care
caseta de editare nu va accepta decat un singur rand de text.
Prin metodele sale,caseta se poate utiliza atat pentru a introduce
date de la tastatura,cat si pentru a prelua date de la un alt obiect,sau
de la o alta locatie de memorie.Clasic,se utilizeaza pentru a prelua date
de la tastatura si a le arhiva intr-o fila,sau a le transmite catre un
alt obiect de interfata.
Obiectele utilizate pentru realizarea interfetei grafice pot fi inlan-
tuite intre ele,printr-o serie de proceduri,astfel incat datele introduse
intr-un obiect sa fie preluate,filtrate si procesate de mai multe alte
obiecte,pentru a obtine un anumit rezultat.
In mod similar,se pot combina datele preluate de la una sau mai multe
casete de dialog,cu date introduse de la tastatura,pentru a forma un text
final.De exemplu,se poate utiliza un obiect TEdit si pentru a grupa o
serie de date preluate din mai multe casete de tip ListBox si ComboBox,
pentru a forma o noua selectie,sau pentru a configura o unitate hardware.

-79-
EXEMPLU: (vezi si OWL_ABC / owl17 );
#include <owl/pch.h>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/listbox.h>
#include <owl/edit.h>
#include string.h>
class Fer1 : public TWindow {
public: Fer1();
void SetupWindow();
void EvRButtonDown(uint,TPoint&);
protected:
TListBox* Lista;
TEdit* Editor1;
DECLARE_RESPONSE_TABLE(Fer1);
};
DEFINE_RESPONSE_TABLE1(Fer1,TWindow)
EV_WM_RBUTTONDOWN,
END_RESPONSE_TABLE;
Fer1::Fer1():TWindow(0,0,0) {
Lista = new TListBox(this,201,50,50,200,100);
Editor1 = new TEdit(this,207,"",50,150,400,200,0,TRUE);
};
void Fer1::SetupWindow()
{
Editor1->Create();
Lista->Attr.Style &= ~LBS_SORT;
Lista->Create();
Lista->AddString("Prima linie din lista ");
Lista->AddString("A doua inregistrare ");
Lista->AddString("......................");
Lista->AddString("Ultima inregistrare ");
};
void Fer1::EvRButtonDown(uint,TPoint& point)
{
char text1[80];
Lista->GetSelString(text1,22);
Editor1->Insert(text1);
point.x=point.x; };
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie1::InitmainWindow()
{ MainWindow = new TFrameWindow(0,"Test",new Fer1); }
int OwlMain(int,char* [])
{ return Aplicatie1().Run(); }
Compilati,construiti si executati proiectul.
Puteti utiliza caseta "Editor1" pentru a edita textul dorit.Daca
doriti sa utilizati si date din caseta listbox "Lista",selectati cu un
click de mouse optiunea dorita si apoi executati un click de mouse drept
in afara casetei de editare.Similar,se pot inlantui mai multe obiecte.

-80-
Pentru a prelua,sau pentru a salva date,in si de pe file de tip text,
arhivate pe unitatea de disc,se poate utiliza o caseta de editare speciala
denumita TEditFile si definita in fila "editfile.h".Aceasta caseta de
editare are metode specializate pentru deschiderea si/sau salvarea datelor
pe disc (NewFile(),Open(),Read(),Save(),SaveAs(),SetFileName() etc.).
EXEMPLU: (vezi si OWL_ABC /owl18 )
#include <owl/pch.h>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/editfile.h>
#include <string.h>
const uint16 ID_EDIT1 = 201;
class Fer1 : public TWindow {
public: Fer1();
void SetupWindow();
protected:
TEditFile* Editor1;
};
Fer1::Fer1():TWindow(0,0,0) {
Editor1 = new TEditFile(this,ID_EDIT,"TEXT:",20,20,500,300,0,0); };
void Fer1::SetupWindow()
{
Editor1->Create();
Editor1->Open();
};
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{ MainWindow = new TFrameWindow(0,"Test",new Fer1); };
int OwlMain(int,char* [])
{ return Aplicatie1().Run(); }
Compilati,construiti si executati aplicatia.
Metoda Open(),deschide automat o caseta de dialog,in care puteti cauta
si selecta orice fila de tip .txt de pe disc.Alegeti fila dorita si apoi
confirmati cu OK.Fila va fi deschisa automat in caseta de editare Editor1.
Daca doriti,puteti adauga un set de butoane,sau un meniu si apoi puteti
atribui cate o functie specifica,pentru fiecare buton sau optiune din
meniu.
Exista si alte clase de obiecte,destinate pentru operatii cu si
asupra filelor de tip document(vezi filele:docmanag.h,docview.h,richedit.h
stgdoc.h etc.).
Nu exista o regula generala,fixa.Fiecare programator va alege tipul
de obiect care corespunde cel mai bine preferintelor sale.Alegerea se
poate face,fie in functie de memoria consumata,fie in functie de metodele
specializate pe care le ofera fiecare clasa sau obiect.Pentru fiecare tip
de operatie,exista un numar mare de variante posibile de implementare.
Acest manual nu ofera decat o schita simplista,in care se utilizeaza doar
exercitii elementare.Pentru a realiza programe profesionale,trebuie sa
depuneti un efort individual,sa va formati rutine si standarde proprii,sa
va obisnuiti sa exploatati la maximum resursele oferite de program.

-81-
Un alt tip de obiect Windows,utilizat pentru proiectarea intefetelor
grafice il reprezinta bara de defilare de tip ScrollBar.Acest tip de
obiect este prezent pe marginea inferioara si laterala a tuturor ferestre-
lor de tip Windows si permite navigarea intr-o fila de tip text cu dimen-
siuni mai mari decat fereastra in care este afisata.Bara de defilare de
tip "scrollbar" este definita in fila "scrollba.h" prin clasa TScrollBar.
EXEMPLU: (vezi si OWL_ABC / owl19 )
#include <owl/pch.h>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/scrollba.h>
const uint ID_SCROLL = 207;
const uint ID_SCROLL2 = 208;
class Fer1 : public TWindow {
public: Fer1();
void SetupWindow();
protected:
TScrollBar* Scroll1;
TScrollBar* Scroll2; };
Fer1::Fer1():TWindow(0,0,0) {
Scroll1 = new TScrollBar(this,ID_SCROLL,20,20,20,300,FALSE,0);
Scroll2 = new TScrollBar(this,ID_SCROLL2,50,150,400,30,TRUE,0);
};
void Fer1::SetupWindow()
{
Scroll1->Create();
Scroll2->Create();
};
class Aplicatie1 : public Tapplication {
public: Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{ MainWindow = new TFrameWindow(0,"Test",new Fer1); };
int OwlMain(int.char* [])
{
return Aplicatie1().Run();
}
Compilati,construiti si executati proiectul.
Deschideti fila owl/scrollba.h si observati constructorul clasei
_OWLCLASS TScrollBar.Pentru a crea o bara de defilare orizontala se va
utiliza pentru parametrul boolean valoarea TRUE,iar pentru a crea o bara
de defilare verticala se va utiliza valoarea FALSE.Valoarea implicita
este isHScrollBar(adica TRUE).
In obiectele utilizate pentru ediatrea unui text,barele de defilare
au rostul de a deplasa campul in care se face editarea sau citirea date-
lor.In aplicatiile realizate de d-voastra,barele de editare se pot utili-
za atunci cand doriti sa lucrati cu serii liniare de numere,pe care sa le
puteti selecta cu ajutorul indicatorului optic mouse.Prin metoda SetRange
se poate specifica domeniul de valori acceptabile(limita inferioara si
cea superioara) iar prin GetPosition() se poate determina pozitia butonu-
lui culisant,respectiv o anumita valoare din seria liniara de valori.

-82-
EXEMPLU: (vezi si OWL_ABC / owl20 ) Editati fila .cpp astfel:
#include <owl/pch.h>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/scrollba.h>
const uint16 ID_SCROLL = 207;
const uint16 ID_SCROLL2 = 208;
class Fer1 : public TWindow {
public: Fer1();
void SetupWindow();
void EvRButtonDown(uint,TPoint&);
protected:
TScrollBar* Scroll1;
TScrollBar* Scroll2;
DECLARE_RESPONSE_TABLE(Fer1); };
DEFINE_RESPONSE_TABLE;
EV_WM_RBUTTONDOWN,
END_RESPONSE_TABLE;
Fer1::Fer1():TWindow(0,0,0) {
Scroll1= new TScrollBar(this,ID_SCROLL,20,20,90,300,FALSE,0);
Scroll2= new YScrollBar(this,ID_SCROLL2,120,250,400,90,TRUE,0);
};
void Fer1::SetupWindow()
{
Scroll1->Create();
Scroll2->Create();
};
void Fer1::EvRButtonDown(uint,TPoint&)
{ Scroll1->SetRange(0,250);
Scroll2->SetRange(0,250);
int orizontal,vertical;
orizontal = Scroll2->GetPosition();
vertical = Scroll1->GetPosition();
SetBkgndColor(RGB(250,orizontal,vertical));
UpdateWindow(); };
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie1::InitmainWindow()
{ MainWindow = new TFrameWindow(0,"Test",new Fer1); };
int OwlMain(int,char* []);
{ return Aplicatie1().Run(); }
Compilati,construiti si executati aplicatia.
Deplasati butoanele de defilare,apoi executati un dublu click de mouse
cu butonul drept,in afara obiectelor.
In acest exemplu,butoanele au fost utilizate pentru a seta valori
numerice cuprinse intre 0 si 250,cu ajutorul carora se schimba culoarea
de fond,prin functiile RGB() si SetBkgndColor().In mod similar se poate
seta volumul sau frecventa unui dispozitiv de sunet,sau orice serie
liniara de valori numerice cuprinse intre limitele din SetRange().
Deschideti fila "scrolba.h" si evaluati toate metodele clasei TScrollBar.

-83-
Pentru a grupa mai multe controale si butoane,se pot utiliza obiecte
specializate cum sunt TControlBar si TStatusBar.Pentru aceste obiecte se
utilizeaza un tip diferit de fereastra denumit TDecoratedFrame,adica o
fereastra de tip TFrameWindow care accepta si un chenar in care se poat
grupa butoanele si obiectele necesare:
EXEMPLU: (vezi si OWL_ABC / owl21 ) Editati fila .cpp astfel:
#include <owl/pch>
#include <owl/button.h>
#include <owl/buttonga.h>
#include <owl/controlb.h>
#include <owl/controlg.h>
#include <owl/gadget.h>
#include <owl/decframe.h>
#define IDB_BITMAP1 1
#define ID_BUTTON 110
class Fer1 : public TWindow {
public: Fer1();
TButton* TB1;
protected: void HandleButtonMsg();
void Button1(){
::MassageBeep(-1);TDClient DC dc1(*this);
dc1.Ellipse(80,120,90,130);dc1.Ellipse(120,120,130,130);
dc1.Rectangle(70,105,90,120);dc1.Rectangle(92,95,140,120); };
DECLARE_RESPONSE_TABLE(Fer1); };
DEFINE_RESPONSE_TABLE(Fer1,TWindow)
EV_COMMAND(ID_BUTTON,HandleButtonMsg),
EV_COMMAND(IDB_BITMAP1,Buton1),
END_RESPONSE_TABLE;
Fer1::Fer1():TWindow(0,0,0) {
TB1 = new TButton(this,ID_BUTTON,"Button",250,50,280,30,false); };
void Fer1::HandleButtonMsg()
{ TClientDC dc1(*this);
dc1.Rectangle(50,50,150,150); };
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
~Aplicatie1(){};
void InitMainWindow();
TControlBar* ControlBar;
TButtonGadget* TG1; };
void Aplicatie1::InitMainWindow()
{ TWindow* testWindow = new Fer1;
TDecoratedFrame* frame = new TDecoratedFrame(0,"test",testWindow,true);
ControlBar = new TControlBar(frame,TControlBar::Horizontal,
new TGadgetWindowFont,0);
frame->Insert(*ControlBar,TDecoratedFrame::Top);
ControlBar->Insert(*new TButtonGadget(IDB_BITMAP1,IDB_BITMAP1,
TButtonGadget::Command,true,TButtonGadget::Up,false));
SetmainWindow(frame); };
int OwlMain(int,char[]*) { return Aplicatie1().Run; }
Pentru imaginea de tip BITMAP,puteti utiliza fila .rc din owl21
(fila owl21.rc) sau puteti edita o fila noua.

-84-
Pentru a edita o fila noua de resurse,alegeti din meniul File optiunea
New si Resource Project.In fereastra de dialog New Resource Project ale-
geti optiunea Bitmap.Observati ca in meniul principal au aparut o serie
de optiuni noi.Alegeti Bitmap si Show,apoi ColorPallete pentru a afisa
si paleta de culori.Apoi utilizati instrumentele de desanare pentru a
realiza imaginea dorita.Pentru exemplul owl21 desenati un camion.Apoi
salvati fila in directorul in care urmeaza sa fie utilizata(de exemplu
in owl21) cu numele de BITMAP1.bmp.
Pentru ca fila sa poata fi utilizata in proiect,trebuie sa fie inclusa
in fila de resurse.Eliminati din proiect fila .def,care nu este necesara,
apoi executati un dublu click de mouse pe fila .rc.Se va deschide o
fereastra de dialog in care apare caseta owl21.rc goala.Executati un
click de mouse drept pe caseta goala si alegeti optiunea Add to Project,
apoi utilizati caseta de navigare pentru a selecta fila BITMAP1.bmp si
confirmati cu Open apoi OK.
Daca totul este OK,acum caseta va avea un semn + si in momentul in
care executati un click pe semnul + se va afisa o ramificatie in care
este declarata resursa de genul: #define IDB_BITMAP1 <1> si un nou
folder in care este inclusa fila BMP cu numele: BITMAP:IDB_BITMAP1(1)
Daca fila are alta denumire sau extensie,inlocuiti in fila .cpp declaratia
#define IDB_BITMAP1 1 cu cea existenta in fila editata de d-voastra.
Proiectul contine cele doua file: owl21.cpp si owl21.rc.Compilati fiecare
nod,apoi construiti nodul .exe si verificati legaturile cu Link(cu click
drept de mouse pe fiecare nod).
Executati aplicatia si apasati alternativ cele doua butoane.
Observati ca fereastra contine un buton obisnuit,iar in partea supe-
rioara contine o banda gri si un buton activ,pe care apare desenul reali-
zat in fila BITMAP1.bmp.Bara de control,realizata prin obiectul ControlBar
s-a adaptat automat al dimensiunea butonului inclus.
Analizati putin fila .cpp.Fila contine o serie de file header noi (
<owl/buttonga>,<owl/controlb>,<owl/controlg>,<owl/gadget>,<owl/decframe>).
Deschideti fiecare fila si analizati clasele de obiecte declarate.Fila
"decframe" se utilizeaza pentru clasa de fereastra TDecoratedFrame,care
accepta si barele de controale de tip TControlBar si TStatusBar.Fila
"controlb" contine declaratia pentru bara de control iar fila "buttonga.h"
contine declaratia pentru butonul cu imaginea Bitmap de import.
Analizati cu atentie fiecare clasa utilizata (mai ales constructorul).
In exercitiul owl21,am efectuat in ordine urmatoarele operatii:
1. -am declarat fereastra Fer1 de tip TWindow la care am declarat si
doua proceduri de raspuns la actionarea butoanelor.
2. -am declarat si definit tabela de raspuns in care am asociat fiecare
mesaj Windows de apasare a butoanelor cu una dintre procedurile de
raspuns.
3. -am redefinit constructorul pentru Fer1 si am inclus in fereastra
si un buton obisnuit (ID_BUTTON)
4. -am declarat un obiect derivat din clasa TApplication
5. -am redeclarat constructorul si destructorul
6. -am declarat o procedura InitMainWindow pentru setarea obiectului
7. -am declarat cate un obiect de tip:TControlBar si TButtonGadget
8. -am definit procedura InitMainWindow astfel:
8.1 -am declarat un obiect de tip TDecoratedFrame
-85-
8.2 -am declarat un obiect de tip TControlBar (bara de control)
8.3 -am introdus bara de control in fereastra "frame" cu Insert
8.4 -am introdus in bara de control butonul cu pictograma
8.5 -am setat fereastra frame ca fereastra principala
9. -am executat aplicatia din functia principala OwlMain prin apelul
metodei .Run() din obiectul de tip TApplication.
In rezumat,pentru realizarea interfetei grafice se pot utiliza si o serie
de obiecte grafice denumite "gadgets",definite in file speciale.Fiecare
astfel de "gadget" poate fi inclus cu Insert,intr-un obiect de grupare,
denumit TControlBar sau TStatusBar.Obiectele de grupare,denumite si bare
de control pot fi incluse in partea superioara sau in partea inferioara
a unei ferestre de tip TDecoratedFrame.Asadar,se va declara o ferastra
de tip TDecoratedFrame,apoi se va include o bara de control,iar in bara
de control se vor include obiectele dorite.Evenimentele si procedurile
de raspuns la fiecare eveniment se programeaza la fel ca pentru orice alt
obiect(cu o tabela de raspuns).Procedeul tehnic este putin mai dificil
decat in Delphi,dar nu este chiar atat de dificil de implementat.Atunci
cand utilizati un numar mare de obiecte de tip "gadget" este bine sa
desenati si pe hartie,schema obiectelor si a legaturilor ce trebuiesc
rezolvate(filele de resurse necesare,tabela de raspunsuri si procedurile
de raspuns,mesajele Windows utilizate,etc.).
Dificultatile pot sa apara mai ales atunci cand utilizati algoritmi
dinamici de includere si excludere a obiectelor din bara de control.De
exemplu,in functie de o anumita parola de raspuns,bara de controale poate
avea o configuratie diferita.In acest caz,pentru fiecare configuratie va
trebui editat un algoritm de generare dinamica a obiectelor incluse.In
acest caz,exista riscul ca mesajele Windows exploatate de obiectele gene-
rate automat sa fie nediscriminative,caz in care procedurile de raspuns
la evenimente vor fi accesate defectuos.In aceste situatii,fiecare obiect
generat automat va trebui sa fie depanat independent,apoi se vor face
combinatii intre doua sau mai multe obiecte,pana cand se identifica me-
sajul "buclucas".
Cu cat exista mai multe obiecte care au acelasi spatiu de vizibilitate,
cu atat creste riscul ca mesajele destinate unui obiect (sau cele astepta-
te de un anumit obiect) sa interfereze si cu un alt obiect din acelasi
spatiu de vizibilitate.Solutia cea mai simpla este sa grupati obiectele
astfel incat sa nu poata exsita interferente incompatibile.Daca este
necesar sa utilizati un numar mare de obiecte de acelasi fel,este bine
sa utilizati un numar mai mare de obiecte de grupare (bare de control),
astfel incat obiectele care asteapta acelasi tip de mesaj sa fie incluse
in spatii cu vizibilitate diferita (sa fie incapsulate diferit).
Pe cat posibil,evitati supraincarcarea interfetelor cu obiecte inutile.
Fiecare obiect acupa spatiu in memoria de operare si trebuie sa fie ges-
tionat separat.Fiecare obiect poate fi o sursa pentru erori de executie
(nedetectate in etapa de compilare),care sunt uneori extrem de greu de
anticipat sau foarte greu de depanat.Daca interfata trebuie sa contina
un numar foarte mare de optiuni,este preferabil sa utilizati meniuri si
bare de meniuri,sau meniuri pop-up,care se preteaza mult mai greu la
astfel de interferente.
Ca pentru orice alta problema,solutia cea mai buna este intotdeauna
solutia cea mai simpla.
-86-
Un alt obiect asemanator cu TControlBar este TStatusBar (bara de stare)
definit in fila "statusba.h".Se utilizeaza pentru a afisa starea unui
anumit grup de taste (de obicei tastele speciale: Scroll,Insert etc.) sau
pentru a afisa data si ora curenta.Se apeleaza la fel ca orice obiect:
EXEMPLU: (vezi si OWL_ABC /owl22 )
Deschideti un proiect nou si completati fila .cpp astfel:
#include <owl/pch.h>
#include <owl/statusba.h>
#include <owl/timegadg.h>
#include <owl/decframe.h>
class Fer1 : public TWindow {
public: Fer1(); };
Fer1::Fer1(): TWindow(0,0,0) { };
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
~Aplicatie1(){};
void InitMainWindow();
TStatusBar* BaraDeStare; };
void Aplicatie1::InitMainWindow()
{ TWindow* testWindow = new Fer1;
TDecoratedFrame *frame=new TDecoratedFrame(0,"test",testWindow,true);
BaraDeStare = new TStatusBar(frame);
BaraDeStare->Insert(*new TTimeGadget(&TTimeGadget::GetSystemTime));
frame->Insert(*BaraDeStare,TDecoratedFrame::Bottom);
SetMainWindow(frame); };
int OwlMain(int,char*[]) { return Aplicatie1().Run(); }
Compilati,construiti si executati proiectul.
Si in acest exemplu,suportul este asigurat de o fereatra de tip TDeco-
ratedFrame in care se va introduce bara de stare cu Insert.In bara de
stare am introdus un obiect de tip TTimeGadget,pentru a afisa in perma-
nenta data si ora sistemului(preluata cu metoda GetySystemTime).
Pentru limitarea spatiului ocupat,cele trei operatii se realizeaza intr-o
singura expresie.Pentru ca programul sa fie cat mai clar,cele trei ope-
ratii se pot separa: 1.se deriva un obiect de tip TTimeGadget 2.se ape-
leaza metoda GetSystemTime 3.se include obiectul in bara de stare .
Observati cele doua file header: <owl/statusba.h> si <owl/timegadg.h>.
Analizati cu atentie fiecare clasa,si metodele specifice.
In programele si aplicatiile mari,se pot realiza diverse combinatii de
bare de meniu,bare de stare si bare de control.Exemplu clasic il repre-
zinta interfata grafica pentru Windows95,care combina o bara de control
cu un meniu popup (declansat de butonul Start).Obiectele dintr-o bara de
control(sau de stare) se pot grupa si cu ajutorul unor alte obiecte de
grupare(gen GroupBox).In acest caz,creste numarul de spatii de vizibili-
tate si depanarea programului poate fi destul de dificila.Pentru astfel
de programe,se recomanda sa realizati si un mic program de depanare auto-
mata,care verifica fiecare mesaj si fiecare obiect din aplicatie,dupa
care alcatuieste un raport final.O alta solutie,este sa adaugati un mic
comentariu,prin care sa semnalizati fiecare spatiu de vizibilitate pentru
mesajele Windows care sunt asociate cu o functie de raspuns.Pentru pro-
gramele mari se pot realiza diagrame,tabele,sau file auxiliare de tip
text in care se specifica recomandarile pentru depanarea programului.

-87-
Daca se combina TControlBar cu TStatusbar,prin conventie bara de stare
se amplaseaza la baza ferestrei.
EXEMPLU: ( vezi si OWL_ABC/ owl23 )
#include <owl/pch.h>
#include <owl/statusba.h>
#include <owl/timegadg.h>
#include <owl/decframe.h>
#include <owl/controlg.h>
#include <owl/modegadg.h>
#include <owl/textgadg.h>
#include <owl/gadget.h>
#include <edit.h>
class Fer1: public TWindow {
public: Fer1(); };
Fer1::Fer1():TWindow(0,0,0) { };
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication(){};
~Aplicatie1(){};
void InitMainWindow();
TStatusBar* BaraDeStare; TControlBar* Control1;
TTextgadget* text1; };
void Aplicatie1::InitmainWindow()
{ TWindow* testWindow = new Fer1;
TDecoratedFrame* frame= new TDecoratedFrame(0,"test",testWindow,true);
BaraDeStare = new TStatusBar(frame);
BaraDeStare->Insert(*new TModeGadget(VK_INSERT,"Insert"));
BaraDeStare->Insert(*new TModeGadget(VK_SCROLL,"Scroll"));
Control1 = new TControlBar(frame);
text1 = new TTextGadget(0,TGadget::Recessed,TTextGadget::Left,15);
text1->SetText("Editor de text:");
Control1->Insert(*text1);
Control1->Insert(*new TControlGadget(*new TEdit(0,222,"",0,0,150,25)));
frame->Insert(*BaraDeStare,TDecoratedFrame::Bottom);
frame->Insert(*Control1,TDecoratedFrame::Top);
SetMainWindow(frame); };
int OwlMain(int,char* []) { return Aplicatie1().Run(); }
Compilati,construiti si executati proiectul.
In acest caz,in bara de stare am inclus doua obiecte de tip TModeGadget
care verifica starea tastelor Scroll si Insert (apasati cele doua taste
pentru a vedea diferenta),iar in bara de controale am inclus un text cu
ajutorul unui obiect de tip TTextGadget si un editor de text de tip TEdit.
Analizati cu atentie filele header pentru fiecare tip de gadget.In mod
similar se pot realiza diverse combinatii de butoane,texte statice sau
casete de dialog,prin care se realizeaza interfata dintre program si
utilizator.Aspectul grafic este foarte important,dar mult mai important
este ca fiecare obiect sa fie definit corect,iar tabela de raspuns la
evenimente sa nu include nici o posibilitate de "eroare accidentala".
Nu in ultimul rand,este prioritara analiza exhaustiva a memoriei consu-
mate,raportata la rezultatul obtinut.Pentru operatii simple se vor prefera
solutiile cele mai simple,iar pentru operatii complexe se va alege solutia
care executa cat mai multe operatii/obiect/instanta/eveniment.

-88-
Un alt tip de obiect,este TGauge,definit in fila "gauge.h".Acest gen
de obiect indicator,este utilizat pentru a afisa gradul de executie a
unui proces,sau orice alt eveniment in care exista o evolutie gradata.
EXEMPLU: ( vezi si OWL_ABC / owl24) Editati fila .cpp astfel:
#include <owl/pch.h>
#include <owl/button.h>
#include <owl/gauge.h>
#include <dos.h>
const uint ID_BUTTON = 101;
class Fer1: public TWindow {
public: Fer1();
TGauge* obiect1;
void HandleButtonMsg();
DECLARE_RESPONSE_TABLE(Fer1); };
DEFINE_RESPONSE_TABLE1(Fer1,TWindow)
EV_COMMAND(ID_BUTTON,HandleButtonMsg),
END_RESPONSE_TABLE;
Fer1::Fer1():TWindow(0,0,0) {
Attr.X = 20;
Attr.Y = 20;
Attr.W = 280;
Attr.H = 200;
new TButton(this,ID_BUTTON,"Apasa",80,100,100,30,false);
obiect1 = new TGauge(this,"executie /100",209,20,20,240,40,true);
obiect1->SetRange(0,100);
SetBkgndColor(RGB(250,100,30)); };
void Fer1::HandleButtonMsg()
{ for(int i=1;i<11;i++)
{ obiect1->SetValue(i*10);
UpdateWindow();
sleep(1); };
};
class Aplicatie1::InitMainWindow()
{ TFrameWindow* testWindow = new TFrameWindow(0,"text",new Fer1,true);
SetMainWindow(testWindow);
};
int OwlMain(int,char* []) { return Aplicatie1().Run(); }
Compilati,construiti si executati obiectul.
Deschideti fila <owl/gauge.h> si observati metodele obiectului TGauge.
TGauge are trei constructori,metode pentru specificarea gradatiei:SetRange
SetStep,SetValue,StepIt,metode pentru citirea gradatiei:GetRange,GetValue,
GetStep,DeltaValue,metode pentru personalizarea indicatorului: SetLed si
SetColor sau SetNativeUse,metode pentru redesenarea obiectului: Paint,
SetupWindow,PaintBorder si EvEraseBkgnd si metode de raspuns la mesajele
Windows (pentru tratarea evenimentelor Windows): Min,Max,Value,Step,Margin
IsHorizontal,LedSpacing,LedThick,BarColor.
Cu ajutorul acestor metode,TGauge se poate utiliza atat pentru a afisa
gradul de evolutie al unor procese,cat si pentru a determina o serie de
procese in functie de starea de moment a obiectului.Un exemplu clasic este
obiectul utilizat de sistemul Windows pentru a afisa o operatie de copiere
(download) a datelor (vezi si exemplul Gauge din Examples).

-89-
Un obiect asemenator cu TGauge este THSlider din fila "slider.h".
Butonul de culisare (slider) se utilizeaza mai ales pentru a seta o
anumita valoare dintr-o scara gradata,dar poate fi actionat si automat.
EXEMPLU: ( vezi si OWL_ABC / owl25 ) Editati fila .cpp astfel:
#include <owl/pch.h>
#include <owl/button.h>
#include <owl/slider.h>
#include <dos.h>
const uint16 ID_BUTTON = 101;
class Fer1 : public TWindow {
public: Fer1();
THSlider* obiect1;
void HandleButtonMsg();
DECLARE_RESPONSE_TABLE(Fer1); };
DEFINE_RESPONSE_TABLE1(Fer1,TWindow)
EV_COMMAND(ID_BUTTON,HandleButtonMsg),
END_RESPONSE_TABLE;
Fer1::Fer1():TWindow(0,0,0) {
Attr.X = 20;
Attr.Y = 20;
Attr.W = 280;
Attr.H = 200;
new TButton(this,ID_BUTTON,"Apasa",80,100,100,30,false);
obiect1 = new THSlider(this,209,20,20,240,40);
obiect1->SetRange(0,100);
SetBkgndColor(RGB(25,150,200)); };
void Fer1::HandleButtonMsg()
{ for (int i=1;i<11;i++)
{ obiect1->SetPosition(i*10);
if (i > 9) { MessageBox("Volum Maxim !","",MB_OK); };
UpdateWindow();
sleep(1); }; };
class Aplicatie1: public TApplication {
public: Aplicatie1() : TApplication() {};
~Aplicatie1(){};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{ TFrameWindow* TestWindow = new TFrameWindow(0,"text",new Fer1,true);
SetMainWindow(testWindow); };
int OwlMain(int,char* []) { return Aplicatie1().Run(); }
Compilati,construiti si executati proiectul.Obesrvati ca am utilizat
si structura TWindowAttr (din <owl/window.h>) pentru a redimensiona Fer1.
Deschideti si fila <owl/slider.h> si observati metodele obiectului(care
este derivat din TScrollBar).Remarcati metodele destinate pentru exploata-
rea butonului: PointToPos,PosToPoint,NotifyParent,SetupThumbRgn,HitTest,
SlideThumb,PaintRuler,PaintSlot,PaintThumb precum si metodele de raspuns
la mesajele Windows: EvGetDlgCode,EvPaint,EvLButtonDown,EvMouseMove,EvL-
ButtonUp,EvLButtonDblClk,EvKeyDown,EvSetFocus,EvKillFocus etc.
TSlider este mult mai complex decat TGauge si permite o gama mult mai
larga de implementari.Un exemplu clasic este butonul utilizat de sistemul
Windows pentru reglarea volumului sunetelor,pentru egalizator,etc.

-90-
Un obiect destinat pentru importul de imagini de tip BitMap si pentru
realizarea de efecte de animatie este TImageList,definit in "imagelst.h".
EXEMPLU: (vezi si OWL_ABC / owl26 ) Editati fila .cpp astfel:
#include <owl/pch.h>
#include <owl/imagelst.h>
#include <dos.h>
#define IDB_BITMAP1 1
#define IDB_BITMAP2 2
class Fer1 : Fer1();
~Fer1();
void Paint(TDC& dc,bool erase,Trect& rect);
TSize s1,s2;
TInageList* Imagine1;
TImageList* Imagine2; };
Fer1::Fer1():TWindow(0,0,0) {
Attr.X = 20;
Attr.Y = 20;
Attr.W = 480;
Attr.H = 480;
SetBkgndColor(RGB(250,250,220));
s1 = TSize(100,100);
s2 = TSize(10,10);
Imagine1 = new TImageList(s1,true,10,10);
Imagine2 = new TImageList(s2,true,10,10);
TBitmap img(*GetModule(),1);
TBitmap img2(*GetModule(),2);
Imagine1 -> Add(img);
Imagine2 -> Add(img2);
Imagine1 -> SetBkColor(RGB(250,250,250)); };
Fer1::~Fer1()
{ delete Imagine1;
delete Imagine2; };
void Fer1::Paint(TDC& dc,bool,TRect&)
{ Imagine1 -> Draw(0,dc,20,250,ILD_NORMAL);
Imagine2 -> Draw(0,dc,105,300,ILD_NORMAL);sleep(1);
Imagine2 -> Draw(0,dc,150,280,ILD_NORMAL);sleep(1);
Imagine2 -> Draw(0,dc,200,282,ILD_NORMAL);sleep(1);
Imagine2 -> Draw(0,dc,250,300,ILD_NORMAL);sleep(1);
Imagine2 -> Draw(0,dc,300,320,ILD_NORMAL);
MessageBox("SFARSIT","",MB_OK); };
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
~Aplicatie1(){};
void InitMainWindow(); };
void Aplicatie1 :: InitMainWindow()
{ MainWindow = new TFrameWindow(0,"text",new Fer1,true); };
int OwlMain(int,char* []){ return Aplicatie1().Run(); }

Eliminati din proiect fila .DEF (cu DeleteNode) si apoi editati fila
.rc astfel incat sa contina imaginile BitMap necesare.Puteti copia fila
owl26.rc,sau puteti utiliza o fila noua (cu desenele d-voastra).

-91-
Pentru a edita fila de resurse puteti utiliza orice fila de tip BitMap
(cu extensia .bmp) sau puteti utiliza editorul C++.In acest caz,alegeti
din meniul File optiunea New,apoi Resource Project si apoi Bitmap.Pentru
a specifica dimensiunile desenului si numarul de culori,alegeti la inceput
butonul Options si complectati in caseta de dialog paleta de culori (2,16,
sau 256) si dimensiunile (Width in Pixels: si Height in Pixels).Apoi
confirmati cu OK si din nou cu OK.La nevoie utilizati din meniul principal
optiunea Bitmap pentru a afisa paleta de culori si paleta de obiecte
pentru desenare.Apoi desenati imaginile dorite (la fel ca si in Paint din
Windows).In final,salvati fila cu extensia .bmp (in acelasi director in
care va fi utilizata).Realizati doua astfel de file .bmp.Apoi,pentru a
include cele doua file in fila de resurse .rc,executati in proiectul
dorit un dublu click stg pe fila .rc,apoi executati un click de mouse
drept pe dirctorul afisat (cel cu numele .rc) si alegeti Add to Project.
Dupa ce introduceti cele doua file,executati un dublu click pe fila .rc
si apoi pe semnul plus.Daca totul este corect,folderul .rc va contine:
--owl26.rc
|
- #define: IDB_BITMAP2 <2>
|
- #define: IDB_BITMAP1 <1>
|
- fila1.bmp (folder)
|
- fila2.bmp (folder)
Daca deschideti cele doua foldere si selectati fila BITMAP,in fereastra
din dreapta se va afisa desenul respectiv.Pentru exemplul owl26 am desenat
un tun si un proiectil,dar puteti utiliza orice desene doriti.
In final,compilati si construiti fiecare nod,apoi verificati toate le-
gaturile dintre module (cu LINK) si executati aplicatia.
Observati ca am definit si un destructor pentru fereastra Fer1.Pentru
a elimina cele doua desene,se poate apela direct destructorul.In cazul
in care desenele se vor utiliza in mod repetat,se va apela fie constructo-
rul fie destructorul.Observati ca si obiectul Aplicatie1 are un destructor
distinct.Practic,fiecare modul sau obiect din aplicatie are constructorul
si destructorul sau.Pentru a elimina din proiect doar un anumit modul,sau
doar un anumit obiect,se va apela destructorul respectiv.Pentru a elibera
complet memoria,se va apela destructorul obiectului de tip TApplication.
Deschideti fila <owl/imagelst.h> si studiati obiectul TImageList.
Obiectul TImageList are opt constructori distincti,astfel incat permite o
gama foarte larga de implementari.Deasemenea,metoda Add() are supraincar-
cate patru variante diferite,astfel incat sa poata adauga imagini BitMap
sau Icon cat mai usor.Pentru desenarea obiectelor exista doua versiuni ale
metodei Draw,iar pentru deplasarea imaginilor contine metodele mecanismu-
lui de tip Drag and Drop (BeginDrag,DragEnter,DragMove,DragLeave,EndDrag).
In exemplul owl26 am utilizat cel mai simplu mecanism posibil.Observati
ca este necesara o procedura speciala pentru definirea contextului de
dispozitiv grafic.Prin conventie,aceasta procedura se denumeste Paint,
pentru a fi cat mai usor de identificat.Se pot adauga algoritmi pentru
deplasarea unuia dintre obiecte(sau a ambelor obiecte).Incercati sa adau-
gati un buton de culisare pentru reglarea tirului.

-92-
Pentru a crea senzatia de deplasare,se utilizeaza o imagine denumita
masca (mask) cu aceleasi dimensiuni,dar colorata in culoarea de fond.Masca
se va suprascrie peste imaginea anterioara,pentru a sterge "urmele".
EXEMPLU: (vezi si OWL_ABC / owl27 ) Editati fila .cpp astfel:
#include <owl/pch.h>
#include <owl/imagelst.h>
#include <dos.h>
#define IDB_BITMAP1 1
#define IDB_BITMAP2 2
class Fer1 : public TWindow {
public: Fer1();
~Fer1();
void Paint(TDC& dc,bool erase,TRect& rect);
TSize s1,s2;
TImageList* Imagine1; TImageList* Imagine2; };
Fer1::Fer1():TWindow(0,0,0) {
Attr.X = 20;Attr.Y = 20; Attr.W = 480; Attr.H = 480;
s1 = TSize(200,200); s2 = TSize(200,200);
Imagine1 = new TImageList(s1,true,10,10);
Imagine2 = new TImageList(s2,true,10,10);
TBitmap img(*GetModule(),1);
TBitmap img2(*GetModule(),2);
Imagine1 -> Add(img);
Imagine2 -> Add(img2);
Imagine2 ->SetBkColor(RGB(250,250,250)); };
Fer::~Fer1()
{ delete Imagine1;
delete Imagine2; };
void Fer1::Paint(TDC& dc,bool,TRect&)
{ Imagine1->Draw(0,dc,20,200,ILD_NORMAL);sleep(1);
Imagine2->Draw(0,dc,20,200,ILD_NORMAL);
Imagine1->Draw(0,dc,150,150,ILD_NORMAL);sleep(1);
Imagine2->Draw(0,dc,150,150,ILD_NORMAL);
Imagine1->Draw(0,dc,200,100,ILD_NORMAL);sleep(1);
Imagine2->Draw(0,dc,200,100,ILD_NORMAL);
Imagine1->Draw(0,dc,250,50,ILD_NORMAL); };
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
~Aplicatie1(){};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{ MainWindow = new TFrameWindow(0,"text",new Fer1,true); };
int OwlMain(int,char* []) { return Aplicatie1().Run(); }
Pentru fila .rc utilizati doua imagini de tip .bmp de aceeasi dimen-
siune(200/200),cea de a doua imagine fiind complet goala.
Eliminati fila .def,apoi compilati,construiti si executati obiectul.
Observati ca cea de a doua imagine practic suprascrie prima inagine,dupa
fiecare interval de o secunda.Daca nu se specifica o culoare pentru fond
(cu SetBkColor()),atunci inaginea va fi transparenta.Metoda Draw accepta
si urmatoarele stiluri: ILD_BLEND25,ILD_BLEND50,ILD_TRANSPARENT si eventual
ILD_OVERLAYMASK pentru a determina modul de suprapunere a imaginilor.

-93-
Pentru a putea deplasa imaginea cu ajutorul tastelor,se poate adauga o
metoda specializata de tipul EvKeyDown.
EXEMPLU: (vezi si OWL_ABC / owl28 ) Editati fila .cpp astfel:
#include <owl/pch.h>
#include <owl/imagelst.h>
#define IDB_BITMAP1 1
#define IDB_BITMAP2 2
int xxx=150;int yyy=150;int xxx1=150;int yyy=150;
class Fer1 : public TWindow {
public: Fer1(); ~Fer1();
void Paint(TDC& dc,bool,erase,TRect& rect);
void EvKeyDown(uint key,uint repeatCount,uint flags);
TSize s1,s2;
TImageList* Imagine1; TImageList* Imagine2;
DECLARE_RESPONSE_TABLE(Fer1); };
DEFINE_RESPONSE_TABLE1(Fer1,TWindow)
EV_WM_KEYDOWN,
END_RESPONSE_TABLE;
Fer1::Fer1(): TWindow(0,0,0) {
Attr.X = 20; Attr.Y = 20; Attr.W = 480; Attr.H = 480;
s1 = TSize(100,100); s2 = TSize(100,100);
Imagine1 = new TImageList(s1,true,10,10);
Imagine2 = new TImageList(s2,true,10,10);
TBitmap img(*GetModule(),1);TBitmap img2(*GetModule(),2);
Imagine1 -> Add(img);Imagine2 -> Add(img2);
Imagine2 -> SetBkColor(RGB(250,250,250)); };
Fer1::~Fer1() { delete Imagine1;delete Imagine2; };
void Fer1::Paint(TDC& dc,bool,Trect&)
{ Imagine1->Draw(0,dc,xxx,yyy,ILD_NORMAL); };
void Fer1::EvKeyDown(uint key,uint,uint)
{ TClientDC dc(*this); switch (key)
{ case VK_UP: xxx1=xxx;yyy1=yyy-50;
Imagine2->Draw(0,dc,xxx,yyy,ILD_NORMAL);
Imagine1->Draw(0,dc,xxx1,yyy1,ILD_NORMAL);break;
case VK_DOWN: xxx1=xxx;yyy1=yyy+50;
Imagine2->Draw(0,dc,xxx,yyy,ILD_NORMAL);
Imagine1->Draw(0,dc,xxx1,yyy1,ILD_NORMAL);break;
case VK_LEFT: xxx1=xxx-50;yyy1=yyy;
Imagine2->Draw(0,dc,xxx,yyy,ILD_NORMAL);
Imagine1->Draw(0,dc,xxx1,yyy1,ILD_NORMAL);break;
case VK_RIGHT: xxx1=xxx+50;yyy1=yyy;
Imagine2->Draw(0,dc,xxx,yyy,ILD_NORMAL);
Imagine1->Draw(0,dc,xxx1,yyy1,ILD_NORMAL);break; };
xxx=xxx1;yyy=yyy1; };
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {}; ~Aplicatie1() {};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
( MainWindow = new TFrameWindow(0,"text",new Fer1,true); };
int OwlMain(int,char* []) { return Aplicatie1().Run(); }
In timpul executiei,utilizati tastele cu sageti pentru a deplasa imaginea.
-94-
Pentru deplasare,se pot utiliza si metodele "Drag and drop" astfel:
EXEMPLU: (vezi si OWL_ABC / owl29 ) Editati fila .cpp astfel:
#include <owl/pch.h>
#include <owl/imagelst.h>
#include <dos.h>
#define IDB_BITMAP1 1
int xxx=150;int yyy=350;int xxx1=150;int yyy1=150;
class Fer1 : public TWindow {
public: Fer1(); ~Fer1();
void Paint(TDC& dc,bool erase,TRect& rect);
void EvRButtonDown(uint,TPoint&);
TSize s1;
TImageList* Imagine1;
DECLARE_RESPONSE_TABLE(Fer1); };
DEFINE_RESPONSE_TABLE1(Fer1,TWindow)
EV_WM_RBUTTONDOWN,
END_RESPONSE_TABLE;
Fer1::Fer1():TWindow(0,0,0) {
Attr.X =20; Attr.Y = 20; Attr.W = 480; Attr.H = 480;
s1 = TSize(100,100);
Imagine1 = new TImageList(s1,true,10,10);
TBitMap img(*GetModule(),1);
Imagine1 -> Add(img); };
Fer1::~Fer1() { delete Imagine1; };
void Fer1::Paint(TDC& dc,bool,Trect&)
{ Imagine1 -> Draw(o,dc,xxx,yyy,ILD_NORMAL); };
void Fer1::EvRButtonDown(uint,TPoint&)
{ TClientDC dc(*this); xxx1=xxx;yyy1=yyy-50;
ShowCursor(FALSE);SetCapture();UpdateWindow();
Imagine1->SetDragCursorImage(0,0,0);
Imagine1->BeginDrag(0,0,0);
Imagine1->DragEnter(*this,xxx,yyy);
Imagine1->DragMove(xxx1,yyy1);sleep(2);
Imagine1->DragLeave(*this);Imagine1->EndDrag();
ReleaseCapture();ShowCursor(TRUE);xxx=xxx1;yyy=yyy1; };
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
~Aplicatie1(){};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{ MainWindow = new TFrameWindow(0,"text",new Fer1,true); };
int OwlMain(int,char* []){ return Aplicatie1().Run(); }
Compilati,construiti si executati exercitiul.La fiecare click de mouse
cu butonul drept,imaginea va fi deplasata timp de 2 secunde.La fiecare
click saltul va fi din ce in ce mai mare,deoarece se acumuleaza difernta
din variabila yyy1.
In acest exemplu,am utilizat evenimentul RButtonDown,dar se poate uti-
liza orice alt eveniment Windows.In multe situatii,metodele DRAG AND DROP
pot fi impartite in mai multe proceduri,astfel incat la apasarea butonului
sa se efectueze selectia imaginii,iar la eliberarea butonului sa se faca
deplasarea si operatiile de stergere pentru imaginea anterioara.
-95-
Pentru a aplica metodele DRAG AND DROP,este necesar ca imaginea sa fie
stocata temporar intr-un tampon de memorie.In acest exemplu,si in multe
alte situatii,s-a utilizat tamopnul de memorie destinat cursorului.Pentru
acest scop,cursorul a fost invalidat temporar (observati ca in timpul ope-
ratiei de DRAG cursorul dispare) iar imaginea a fost stocata temporar cu
SetDragCursorImage().Dupa epuizarea operatiilor,cursorul trebuie reactivat
cu ShowCursor(True).Acest gen de implementare este foarte avantajos,pentru
ca nu necesita alocarea de memorie suplimentara,dar prezinta si un mic
inconvenient.In situatiile in care aplicatia se blocheaza in timpul unei
operatii de DRAG,cursorul este invalidat si aplicatia nu poate fi depanata
sau actualizata.Singura solutie este oprirea si repornirea calculatorului.
Operatiile de grafica animata nu ridica probleme atunci cand se lucreza
cu o singura imagine grafica.Pe masura ce se utilizeaza tot mai multe
obiecte si imagini grafice,memoria de operare devine din ce in ce mai
incarcata iar la un anumit moment dat,incep sa apara suprascrieri.Din
acest motiv,trebuie tinuta o evidenta foarte stricta a operatiilor efec-
tuate si o gestiune/operatie a memoriei consumate.Obiectele care nu mai
sunt necesare de loc,vor fi eliberate complet apeland destructorul,iar
cele care urmeaza sa fie utilizate din nou,vor fi doar inactivate temporar
sau vor fi pur si simplu suprascrise (sterse prin suprapunerea unui alt
obiect grafic).Este bine sa lasati o marja de siguranta,astfel incat dupa
ce calculati toate operatiile ce urmeaza sa fie executate,sa mai ramana
inca suficient loc in memoria de operare si pentru restul variabilelor,
pentru gestiunea proceselor,pentru expandarea unor obiecte sau baze de
date etc.Nu va lansati la operatii foarte complexe.Este bine sa verificati
pas cu pas,fiecare operatie introdusa in program.Dupa fiecare etapa rezol-
vata,efectuati o copie de siguranta a proiectului,astfel incat sa puteti
reveni cat mai usor la o atapa anterioara de dezvoltare.Uneori,de la un
anumit punct,se vor putea ramifica mai multe variante,dintre care urmeaza
sa alegeti varianta cea mai buna,in functie de evolutia ulterioara a
proceselor (este greu de anticipat care va fi solutia cea mai buna).
Pentru programele si aplicatiile mari,este bine lucrati modular.Fiecare
astfel de modul,va fi mult mai usor de depanat,dar mai ales,va putea fi
utilizat in mai multe programe.In timp,prin cumularea acestor module,pro-
gramarea unei aplicatii noi va fi asemenatoare cu un joc de "puzzle" in
care nu trebuie decat sa alegeti piesele de care aveti nevoie pentru a
realiza tabloul complet.
Dupa adaugarea fiecarui modul,compilati,executati si eventual depanati
programul.Daca adaugati mai multe module dintr-un singur pas,va fi mult
mai greu de verificat legaturile si eventualele situatii de supraincarcare
a unor identifcatori sau definitii.Verificati cu atentie fiecare modul.
Chiar daca modulul functioneaza perfect in alta aplicatie,este posibil ca
in programul d-voastra sa nu functioneze,sau sa functioneze incorect.De
obicei este o problema legata de "mediul de operare".Cu alte cuvinte,in
spatiul de vizibilitate comuna exista doi sau mai multi identificatori
"supraincarcati",sau pur si simplu exista mai multe obiecte care exploa-
teaza competitiv aceleasi mesaje Windows,aceleasi tampoane de memorie sau
aceiasi registri de procesor.Prin adaugarea succesiva a modulelor,ope-
ratiile de verificare si control vor fi mult mai usor de realizat.
Dupa ce programul merge "perfect",rugati un cunoscut sau un coleg sa-l
verifice.In multe cazuri va descoperii "lacune" la care nu v-ati gandit.

-96-
Pentru a repeta unele operatii la intervale mai mici de o secunda,se
pot utiliza cronometrele.In OWL,cronometrul este inclus in fereastra de
tip TWindow cu ajutorul functiei SetTimer (urmata de KillTimer).
EXEMPLU: (vezi si OWL_ABC /owl30 ) Editati fila .cpp astfel:
#include <owl/pch.h>
#include <owl/imagelst.h>
#define IDB_BITMAP1 1
#define IDB_BITMAP2 2
int xxx=150;int yyy=350;int xxx1=150;int yyy1=150;
class Fer1: public TWindow {
public: Fer1();~Fer1();
void Paint(TDC& dc,bool erase,TRect& rect);
void EvTimer(uint id);
TSize s1,s2;
TImageList* Imagine1;TImageList* Imagine2;
DECLARE_RESPONSE_TABLE(Fer1);
DEFINE_RESPONSE_TABLE1(Fer1,TWindow)
EV_WM_TIMER,
END_RESPONSE_TABLE;
Fer1::Fer1():TWindow(0,0,0) {
Attr.X = 20;Attr.Y = 20;Attr.W = 480;Attr.H = 480;
s1 = TSize(100,100); s2 = TSize(100,100);
Imagine1 = new TImageList(s1,true,10,10);
Imagine2 = new TImageList(s2,true,10,10);
TBitmap img(*GetModule(),1); TBitmap img2(*GetModule(),2);
Imagine1 -> Add(img); Imagine2 -> Add(img2);
Imagine2 -> SetBkColor(RGB(250,250,250)); };
Fer1::~Fer1() { delete Imagine1; delete Imagine2;KillTimer(1); };
void Fer1::Paint(TDC& dc,bool,TRect&)
{ Imagine1 -> Draw(0,dc,xxx,yyy,ILD_NORMAL);
SetTimer( 1,50); };
void Fer1::EvTimer(uint)
{ TClientDC dc(*this);
xxx1=xxx;yyy1=yyy-1;
Imagine2 ->Draw(0,dc,xxx,yyy,ILD_NORMAL);
Imagine1 ->Draw(0,dc,xxx1,yyy1,ILD_NORMAL);
xxx=xxx1;yyy=yyy1; };
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
~Aplicatie1(){};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{ MainWindow = new TFrameWindow(0,"text",new Fer1,true); };
int OwlMain(int,char* []){ return Aplicatie1().Run(); }
Compilati,construiti si executati.Functia KillTimer() a fost amplasata in
destructorul ferestrei Fer1,astfel incat operatiile incluse in procedura
EvTimer se vor repeta atat timp cat fereastra este deschisa (desenul se
va deplasa in sus,la fiecare 50 de milisecunde.Fiecare "timer" are un
numar de ordine,astfel incat sa se poata utiliza simultan mai multe crono-
metre.Atentie la destructori.Orice cronometru neinchis va executa operatii
in fundal,pana la inchiderea calculatorului(consuma memoria de RAM).

-97-
In Win32 nu se pot utiliza simultan mai mult de 32 de cronometre,dar
in sistemele mai mari(Millenium,XP etc.) numarul de cronometre poate fi
mai mare(64-256 in functie de tipul partitiei).Cronometrele se pot utiliza
si pentru procese cu executie multifilara (fiecare cronometru controleaza
un anumit proces sau o anumita prioritate de executie).
Un alt obiect destinat pentru interfata grafica este butonul de tip
TUpDown,definit in fila "updown.h" si utilizat pentru incrementarea sau
decrementarea unor valori numerice.
EXEMPLU: (vezi si OWL_ABC /owl31 )
#include <owl/pch.h>
#include <owl/updown.h>
#include <owl/dc.h>
const uint16 IDC_BUTTON1 = 111;int valoare;
class Fer1 : public TWindow {
public: Fer1();
void SetupWindow();
void EvLButtonDown(uint,TPoint&);
protected: TUpDown* Buton1;
DECLARE_RESPONSE_TABLE(Fer1); };
DEFINE_RESPONSE_TABLE1(Fer1,TWindow)
EV_WM_LBUTTONDOWN,
END_RESPONSE_TABLE;
Fer1::Fer1():TWindow(0,0,0) {
Buton1 = new TUpDown(this,111,300,100,30,30,0);
Buton1 -> SetRange(0,250); Buton1 -> SetPos(100); };
void Fer1::SetupWindow() { Buton1 -> Create(); };
void Fer1::EvLButtonDown(uint,TPoint&)
{ valoare = Buton1 -> GetPos();
TClientDC dc(*this);
dc.Rectangle(200,200,valoare,valoare); };
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie1 :: InitMainWindow()
{ MainWindow = new TFrameWindow(0,"test",new Fer1); };
int OwlMain(int,char* []) { return Aplicatie1().Run(); }
Compilati,construiti si executati aplicatia.
Executati un click de mouse,in fereastra pentru a desena un patrat de
referinta (cu valorile initiale: 200,200,100,100).Apoi utilizati unul
dintre butoanele de incrementare sau decrementare,de mai multe ori.Fiecare
click pe unul dintre butoane va modifica variabila VALOARE cu o unitate.
Apoi executati din nou un click de mouse in fereastra pentru a observa
diferenta.
Deschideti fila "owl/updown.h" si analizati obiectul TUpDown.Observati
ca este derivat din TControl si TBitFlags(mosteneste si metodele acestora)
la care se adauga metodele proprii.Obiectul contine si o serie de constan-
te pentru a specifica starea obiectului (csGrayed,csHidden,csMouseOut,
csIncrement,csDecrement,csTimer1On,csTimer2On) precum si o serie de pro-
ceduri specializate pentru tratarea evenimentelor Windows (EvEnable,EvShow
Window,EvCancel,EvTimer,EvLButtonDown,EvRButtonDown,EvLButtonUp,si
EvMouseMove).

-98-
In interfata grafica se pot utiliza si imagini de tip BitMap indepen-
dente de contextul de dispozitiv grafic (DC),cu ajutorul unei clase specia-
lizate,denumita TPictureWindow,definita in fila "pictwind.h".Acest obiect
permite preluarea diercta a imaginii bitmap.Mai mult,imaginea poate fi
expandata sau restransa,pixel cu pixel,astfel incat sa se adapteze la
dimensiunea ferestrei.
EXEMPLU: (vezi si OWL_ABC / owl32 ) Editati fila .cpp astfel:
#include <owl/pch.h>
#include <owl/gdiobjec.h>
#include <owl/pictwind.h>
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{ MainWindow = new TFrameWindow(0,"text",new TPictureWindow
(0,new TDib("Desen.bmp"),TPictureWindow::Center)); };
int OwlMain(int,char*[]) { return Aplicatie1().Run(); }
Adaugati in acelasi director orice imagine de tip BitMap cu numele
Desen.bmp (sau utilizati Resource Project pentru a edita o fila noua).
Compilati,construiti si executati aplicatia.
Din economie de spatiu,am cumulat trei operatii in definitia ferestrei
principale MainWindow.In ordine cronologica,cele trei operatii se pot exe-
cuta si astfel: 1.Se defineste o fereastra de tip TPictureWindow in care
se utilizeaza un obiect TDib pentru a prelua imaginea BitMap.
2.Se defineste o fereastra TFrameWindow din tipul de fe-
reastra TPictureWindow definit anterior.
3.Se defineste fereastra MainWindow din tipul TFrameWin-
dow definit in etapa anterioara.
Deschideti fila "pictwind.h" si observati ca definitia clasei TPicture-
Window (derivata din TWindow) include si o serie de parametri utilizati
pentru a seta modul de afisare (UpperLeft,Center,Stretch).Pentru ca imagi-
nea preluata din fila BitMap sa se adapteze automat la dimensiunea feres-
trei se va utiliza parametrul "Stretch".
EXEMPLU: (vezi si OWL_ABC / owl33 ) Editati fila .cpp astfel:
#include <owl/pch.h>
#include <owl/gdiobjec.h>
#include <owl/pictwind.h>
class Aplicatie1: public TApplication {
public:
Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{ MainWindow = new TFrameWindow(0,"text",new TPictureWindow
{0,new TDib("Desen.bmp"),TPictureWindow::Stretch)); };
int OwlMain(int,char* []) { return Aplicatie1().Run(); }
Puteti utiliza orice fila .bmp (utilizati numele corect in loc de Desen)
Compilati,construiti si executati aplicatia.Observati ca daca utilizati
butoanele de marire sau micsorare a ferestrei,imaginea se va adapta auto-
mat la dimensiunea ferestrei.Pentru a adauga butoane,sau alte controale,se
va proceda la fel ca in exercitiile precedente,dar fereastra Fer1 va fi
de tip TPictureWindow in loc de TWindow.

-99-
Biblioteca OWL contine si o fila destinata pentru organizarea datelor
sub forma de arbore genealogic.File se numeste "treewind.h" si contine
definitia urmatoarelor clase: TTreeWindow,TTreeNode,TTreeItem,TTwHitTest-
Info,TTwNotify,TTwKeyDownNotify,TTwComparator.Pentru a realiza un arbore
se utilizeaza o fereastra de tip TTreeWindow in care nodurile retelei
sunt formate din obiecte de tip TTreeNode iar elementele din TTreeItem.
EXEMPLU: (vezi si OWL_ABC / owl34 ) Editati fila .cpp astfel:
#include <owl/pch.h>
#include <owl/treewind.h>
class Fer1 : public TWindow {
public: Fer1();
void SetupWindow();
TTreeWindow* Arbore; };
Fer1::Fer1():TWindow(0,0,0) {
Arbore = new TTreeWindow(this,133,10,10,400,200,
TTreeWindow::Style(TTreeWindow::twsHasLines)); };
void Fer1::SetupWindow()
{ TWindow::SetupWindow();
TTreeNode root = Arbore ->GetRoot();
TTreeNode parent1 = root.AddChild(TTreeItem("Director1"));
TTreeNode child1 = parent1.AddChild(TTreeItem("Subdirector 1"));
parent1.AddChild(TTreeItem("Subdirector 2"));
parent1.AddChild(TTreeItem("Subdirector 3"));
TTreeNode child2 = child1.AddChild(TTreeItem("Sub-Subdirector 1"));
Arbore -> Update(); };
class Aplicatie1 : public TApplication {
public : Aplicatie1() : TApplication {};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{ MainWindow = new TFrameWindow(0,"Test",new Fer1); };
int OwlMain(int char* []) { return Aplicatie1().Run(); }
Compilati,construiti si executatti aplicatia.
Observati ca la lansarea aplicatiei se va afisa doar nodul principal (cel
denumit Director).Pentru a afisa si sundirectoarele sale,executati un
dublu click de mouse pe "Director".Pentru a inchide directorul,executati
un nou dublu click,iar pentru a afisa si sub-subdirectorul 1,executati un
dublu click pe subdirectorul 1.
In mod similar,se poate construi orice tip de retea binara organizata
sub forma de arbore genealogic.
Clasa TTreeItem are patru constructori si metode pentru setarea sau
preluarea datelor (SetText si GetText) sau pentru setarea imaginii de tip
bitmap atasata datelor (SetImageIndex).
Clasa TTreeNode contine trei constructori,un grup de metode destinate
pentru navigarea automata prin arbore (GetParent,GetChild,GetNextSibling,
GetPrevSibling,GetNextVisible,GetPrevVisible,GetNextItem) sau pentru a
adauga/sterge elemente (AddChild,AddSibling,InsertChild,InsertItem,Delete)
precum si alte metode utile (Editlabel,EnsureVisible,ExpandItem etc.).
Clasa TTreeWindow contine doi constructori si metode pentru setarea si
sau identificarea datelor.
Prin combinarea celor trei obiecte si a metodelor aferente,se poate
obtine un numar practic nelimitat de variante pentru afisarea datelor.

-100-
Un astfel de arbore genealogic se poate utiliza pentru a organiza un
grup de file de tip text,sau pentru a prezenta selectiv un grup de date.
De exemplu,in functie de o parola de intrare,arborele va prezenta un
grup mai mic sau mai mare de optiuni executabile.
Un alt element esential pentru dezvoltarea de interfete grafice il re-
prezinta temporizarea actiunilor.In exemplele precedente,actiunile au
fost declansate de apasarea butonului mouse,de actionarea unui buton sau
a unui meniu popup sau au fost temporizate prin functia Sleep() si cu
ajutorul cronometrelor.O alta modalitate simpla si eficienta este sa
utilizati functia GetTickCount(),care determina in milisecunde timpul
scurs de la initializarea programului.
EXEMPLU: (vezi si OWL_ABC / owl 35 ) Editati fila .cpp astfel:
#include <owl/pch.h>
class Fer1 : public TWindow {
public : Fer1();
void SetUpWindow(); };
Fer1::Fer1(): TWindow(0,0,0) {};
void Fer1::SetupWindow()
{ for (int i=0;i<10;i++) {
MessageBeep(-1);
uint32 start = GetTickCount();
while ( GetTickCount() - start <250 );
MessageBeep(-1); };
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{ MainWindow = new TFrameWindow(0,"Test",new Fer1); };
int OwlMain(int,char*[]) { return Aplicatie1().Run(); }
Compilati,construiti si executati aplicatia.
In exemplul de mai sus,functia SetupWindow executa un numar de 10 beep-uri
cu o intarziere de 250 milisecunde.Pentru a obtine un timp de intarziere
mai lung sau mai scurt,modificati valoarea 250 din bucla WHILE.
Trebuie remarcat faptul,ca desi functia GetTickCount are un grad de
discriminare de 1 milisecunda,intervalul discriminativ real depinde de
sistemul de operare.Astfel,sistemul Windows 95,verifica timerul sistemului
la un interval de 55 de milisecunde,sistemul Windows NT 3.1 verifica
timer-ul la 16 milisecunde iar Windows NT 3.5 si urmatoarele la 10 mili-
secunde.Ca rezultat,in Windows 95 nu puteti obtine intervale mai mici de
55 milisecunde,iar in Windows NT 3.5 nu puteti obtine intervale mai mici
de 10 milisecunde.Din acest motiv,jocurile pentru calculator ruleaza dife-
rit in functie de sistemul de operate.Cu cat sistemul de operare este mai
recent si procesorul este mai rapid,cu atata intervalul de verificare este
mai scurt si functia GetTickCount() este mai discriminativa.
Functia GetTickCount() utilizeaza o valoare de tip DWORD.Ca rezultat,
poate obtine valori semnificative (discriminative) timp de 49,7 zile de
la momentul primei apelari.Dupa acest interval,depaseste domeniul de
reprezentare si returneaza valori care oscileaza in jurul valorii zero.
Daca utilizati aceasta functie pentru animatie si/sau jocuri,trebuie sa
tineti cont si de sistemul de operare pe care va rula aplicatia realizata.
ATENTIE ! -orice bucla WHILE neinchisa va determina operatii infinite.

-101
Un alt tip de obiect este TSplashWindow,care poate fi derivat din clasa
cu acelasi nume definita in fila "splashwi.h".Se utilizeaza pentru a putea
afisa o imagine de tip BitMap pentru un interval determinat de timp (de
obicei scurt),asemanator cu "flash-urile" publicitare.Clasa TSplashWindow
este derivata la randul sau din TLayoutWindow si contine un obiect de tip
TDib,utilizat pentru afisarea imaginii si metode proprii.
EXEMPLU: (vezi si OWL_ABC / owl36 ) Definiti fila .cpp astfel:
#include <owl/pch.h>
#include <owl/splashwi.h>
class Fer1 : public TWindow {
public: Fer1(); };
Fer1::Fer1(): TWindow(0,0,0) { TSplashWindow* s1; int timp = 3000;
s1=new TSplashWindow(*new TDib("Desen.bmp"),200,200,0x0008,timp,"",0);
s1 -> Create() };
class Aplicatie1 : public TApplication {
public:
Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{ MainWindow = new TFrameWindow(0,"Test",new Fer1); };
int OwlMain(int,char* []) { return Aplicatie1().Run(); }
Adaugati in acelasi director fila .bmp (Desen.bmp),apoi compilati si
construiti nodul si apoi intregul proiect.Executati aplicatia.
Fila .bmp va fi afisata timp de 2500 milisecunde.Pentru a modifica
durata,puteti schimba valoarea parametrului "timp".
Fereastra de tip TSplashWindow accepta mai multe stiluri de afisare:
None = 0x0000,ShrinkToFit = 0x0001 (fereastra se redimensioneaza la dimen-
siunea imaginii .bmp),MakeGauge = 0x0002 (afiseaza si un obiect TGauge
pentru a indica progresia),MakeStatic = 0x004 (afiseaza si un text de
control) si CaptureMouse = 0x008 (captureaza si click-uri de mouse).
Constructorul ferestrei include un obiect de tip TDib in care se va
prelua imaginea .bmp,largimea si inaltimea ferestrei,stilul (vezi mai sus)
durata de afisaj (in milisecunde) si eventual fereastra in care este con-
tinuta (fereastra sau obiectul parinte).
Fereastra poate utiliza si evenimentele EvLButtonDown sau EvTimer,
pentru a organiza una sau mai multe operatii sau proceduri.
Se poate utiliza pentru a semnaliza anumite informatii.De exemplu se
poate afisa la debutul executiei autorul,compania si numele programului,
sau o sigla,un antet,o adresa de e-mail etc.O alta aplicatie posibila,
este pentru a semnaliza sfarsitul unei anumite operatii (cum ar fi sal-
varea unor date pe disc),aparitia unei anumite exceptii,sau pur si simplu
pentru a avertiza sau a reaminiti ceva,din timp in timp,fara ca sa fie
necesar ca utilizatorul sa execute operatii de inchidere a ferestrei.
Daca fereastra este de tip CaptureMouse,poate fi inchisa inainte de
epuizarea termenului cu un simplu click de mouse.Pentru restul situatiilor
se poate apela metoda CloseWindow() sau se poate apela destructorul,pentru
a elimina obiectul din memorie.
Pentru operatii care dureaza mai mult timp,se poate utiliza o astfel
de fereastra,cu stilul MakeGauge,pentru a prezenta pas cu pas modul de
progresie a operatiilor (intr-un obiect TGauge).Pentru a prezenta imagini
diferite,utilizati cate o fereastra pentru fiecare imagine.

-102-
O alta fila utila din biblioteca OWL este "validate.h" in care sunt
definite urmatoarele clase: TValidator,TXValidator,TPXPictureValidator,
TFilterValidator,TRangeValidator,TLookupValidator,TSortedStringArray,
si TStringLookupValidator.
Dim toate aceste clase,se pot deriva obiecte utilizate pentru a filtra
datele introduse de la tastatura intr-un obiect de tip TEdit.Pentru a de-
termina definitia datelor acceptate,se pot utiliza cifre,litere si carac-
terele speciale generale de tip #,? etc.
EXEMPLU: ( vezi si OWL_ABC / owl37 )
#include <owl/pch.h>
#include <owl/edit.h>
#include <owl/button.h>
#include <owl/validate.h>
const uint ID_BUTTON = 101;
class Fer1 : public TWindow { public: Fer1(); void SetupWindow();
void HandleButtonMsg(); TEdit* Caseta1; TLookupValidator* val1;
TButton* b1; DECLARE_RESPONSE_TABLE(fer1); };
DEFINE_RESPONSE_TABLE1(Fer1,TWindow)
EV_COMMAND(ID_BUTTON,HandleButtonMsg),
END_RESPONSE_TABLE;
Fer1::Fer1(): TWindow(0,0,0) {
Caseta1= new TEdit(this,222,"",50,50,150,100);
b1 = new TButton(this,ID_BUTTON,"AJUTOR",350,50,80,30,false);
val1 = new TLookupValidator(); };
void Fer1::SetupWindow()
{ Caseta1 -> Create();
Caseta1 -> SetValidator(new TFilterValidator("A-M"));
b1 -> Create(); };
void Fer1::HandleButtonMsg()
{ MessageBox("Utilizati doar literele A...M","",MB_OK); };
class Aplicatie1 : public TApplication (
public : Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow()
{ MainWindow = new TFrameWindow(0,"test",new Fer1); };
int OwlMain(int,char* []) { return Aplicatie1().Run(); }
Compilati,construiti si executati proiectul.Executati un click de
mouse in caseta TEdit si observati ca nu puteti introduce decat caractere
majuscule cuprinse intre A si M (intervalul din TFilterValidator).
Pentru alt gen de filtru,sau alt gen de obiect,puteti utiliza pentru
functia SeTValidator una dintre variantele:
SetValidator(new TPXPPictureValidator("###-##-####"));
SetValidator(new TRangeValidator(1,99));
SetValidator(new TPXPictureValidator("Ion,Ana,Maria,Vasile"));
SetValidator(new TPXPictureValidator("11,12,13,14,15"); etc...
Ca rezultat,caseta TEdit nu va accepta decat formatul impus: care poate
fi un numar de telefon,un cod numeric sau alfa-numeric,o parola etc.
Acest gen de implementare se utilizeaza pentru a bloca orice posibilitate
de a introduce date eronate sau cu un format diferit decat cel acceptat.
Este extrem de util atunci cand se lucreaza cu baze de date formatate.
Pentru a rezolva eventualele erori,se poate utiliza metoda Error.

-103-
Pentru imprimarea datelor,biblioteca OWL contine fila "printer.h" in
care sunt definite urmatoarele clase: TPrinterAbortDlg,TPrintout,TPrinter,
si TXPrinter.Dintre acestea,TPrintout preia si prelucreaza documentul pe
care il expediaza la imprimanta,iar TPrinter face legatura fizica cu
dispozitivul de imprimare.Pentru fiecare document se va utiliza cate un
obiect de tip TPrintout(fiecare document va avea setari proprii).
EXEMPLU: (vezi si OWL_ABC /owl38 ). Editati fila .cpp astfel:
#include <owl/pch>
#include <owl/framewin.h>
#include <owl/dc.h>
#include <owl/printer>
class Imprima : public TPrintout {
public : Imprima(const char* title,TWindow* window);
void PrintPage(int page,TRect& rect,unsigned flags);
bool HasPage(int pagina) { return pagina == 1; };
bool SetBanding(bool b) { Banding = b;};
TWindow* Window; };
Imprima :: Imprima(const char* title,TWindow* window): TPrintout(title)
{ Window = window; };
void Imprima :: PrintPage(int,TRect& rect,unsigned);
{ int prevMode;
TSize oldVExt,oldWExt;
prevMode = DC -> SetMapMode(MM_ISOTROPIC);
TRect windowSize = Window -> GetClientRect();
DC -> SetViewportExt(PageSize,&oldVExt);
DC -> SetWindowExt(windowSize.Size(),&oldWExt);
DC -> IntersectClipRect(windowSize);
DC -> DPtoLP(rect,2);
Window -> Paint(*DC,false,rect);
DC -> SetWindowExt(oldWExt);
DC -> SetViewportExt(oldVExt)
DC -> SetMapMode(prevMode); };
class Fer1 : public TWindow {
public: Fer1();
~Fer1(){ Scrie(); delete Imprimanta; };
void Scrie();
void Paint(TDC&,bool,TRect&);
TPrinter* Imprimanta; };
Fer1::Fer1(): TWindow(0,0,0) { Imprimanta = new TPrinter; };
void Fer1::Scrie() { Imprima imp1("text",this);
Imprimanta -> Print(this,imp1,false); };
void Fer1::Paint(TDC& dc,bool,TRect&)
{ dc.TextOut(50,100,"Primul rand de text....",25);
dc.TextOut(50,150,"Al doilea rand de text...",25);
dc.Ellipse(50,250,100,300);dc.Ellipse(250,250,300,300); };
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie::InitMainWindow()
{ MainWindow = new TFrameWindow(0,"Test",new Fer1()); };
int OwlMain(int,char* []) { return Aplicatie1().Run(); }
-104-
Proiectul nu poate fi compilat ca atare,deoarece C++ nu are legaturi
directe cu porturile de iesire si nu poate apela direct imprimanta.Pentru
a putea realiza legatura fizica cu dispozitivele de iesire,trebuie editata
o procedura in assembler,sau trebuiesc apelate resursele sistemului de
operare Windows.
O parte din resursele utilizate pentru obiectele TPrintout si TPrinter
sunt incluse in fila owl/printer.rc.Pentru a include si aceste resurse
editati fila de resurse (fila owl38.rc) astfel:
#ifndef WORKSHOP_INVOKED
#include <windows.h>
#endif
#include <owl/printer.rc>
#include <owl/except.rc>
#include <owl/owlapp.rc>
#include <owl/editfile.rh>
Optional,puteti adauga un meniu,icon etc...
Fila owl38.def nu este necesara.Puteti elimina aceasta fila din proiect,
sau puteti utiliza fila default,din directorul LIB(redenumita owl38.def).
In plus fata de celelalte proiecte,pentru a putea avea acces la calea
fizica(porturile de imprimanta),este necesar ca procesul de compilare si
constructie sa fie dirijat de o fila de tip "makefile".Utilizati orice
editor de text,pentru a scrie urmatoarea fila:
MODELS = ldft

EXERES = prntprev

!include $(BCEXAMPLEDIR)\owlmake.gen
Copiati fila in directorul owl38 cu numele de makefile.Puteti utiliza fila
din exemplu,sau puteti edita o fila noua.
In mod normal,procesul de compilare a proiectului utilizeaza o rutina
automata pentru generarea filei de tip MAKE (puteti verifica cu optiunea
Generate makefile din meniul Project).Pentru a utiliza o fila makefile
editata de d-voastra,trebuie inactivata aceasta rutina automata.Pentru
acest scop,executati un click de mouse cu butonul drept pe fila owl38.exe
din proiect si alegeti optiunea "View options hierarchy".In fereastra
Options Hierarchy alegeti nodul owl38.exe si apoi apasati butonul Edit.
In fereastra Project Options,din Topics alegeti Make si apoi dintre
optiunile casetei Autodependencies (butoane radio) selectati optiunea
"None".Confirmati cu OK,si apoi iesiti din setari cu butonul Close.
In acest moment,directorul proiectului trebuie sa contina filele:
owl38.cpp,owl38.rc si fila makefile.Compilati,construiti si apoi utilizati
optiunea Make node,pentru a finaliza proiectul.Apoi executati aplicatia.
In momentul in care imprimanta este pregatita pentru scriere,inchideti
fereastra si imprimarea va incepe imediat.
Din economie de spatiu,am introdus comanda de imprimare in destructorul
ferestrei,astfel incat imprimarea sa inceapa doar dupa inchiderea feres-
trei.O solutie mult mai eleganta este sa utilizati un meniu,sau un buton
pentru declanasarea functiei Scrie().Pentru a utiliza ferestrele de
dialog,se poate utiliza metoda TPrinter.Setup(this),respectiv metoda
TPrinter.Print() cu optiunea TRUE.Atentie: TPrinter.Print() apeleaza auto-
mat metoda Print.Daca se include aceasta metoda in Print() se va obtine o
-105-
bucla infinita in care Paint() apeleaza Print() iar Print() apeleaza
Paint().
Nu toate tipurile de imprimanta accepta setarile pentru Banding.Daca
utilizati o imprimata mai rudimentara,renuntati la aceasta setare.
Exemplul prezentat este rudimentar.Pentru a specifica parametrii de
imprimare,puteti utiliza toate metodele celor doua obiecte.
Metoda PrintPage() a fost definita pentru modul MM_ISOTROPIC,adica va
imprima datele asa cum sunt.Pentru a modifica aspecul se pot utiliza
toate metodele contextului de dispozitiv definite in fila dc.h.
Pentru a personaliza si mai mult proiectul,puteti utiliza si alte
optiuni din Project Options.Pentru a vedea toate optiunile posibile,
utilizati "View options hierarchy",apasati butonul Edit si apoi,pentru
fiecare optiune din Topics executati un click de mouse pe semnul plus (+).
Selectati fiecare optiune si observati se variante sunt.
Nu va jucati cu setarile.Daca efectuati modificari,notati fiecare mo-
dificare efectuata,astfel incat sa puteti reveni la setarile initiale.In
caz contrar,riscati sa dereglati ireversibil procesul de compilare.
EXEMPLU:
Daca efectuati un click pe Compiler se vor afisa urmatoarele optiuni:
Defines,Code generation,Floating Point,Compiler output,Source,Debugging
si Precompiled Headers.Alegeti Floating Point si selectati "Correct
Pentium FDIV flaw",sau respectiv "No floating point" (daca doriti sa
blocati utilizarea numerelor in virgula mobila).Optiunea implicita este
"Fast floating point".
Pentru a limita spatiul ocupat de proiectul d-voastra,puteti selecta
optiunea "Precompiled Headers" si puteti alege "Do not generate or use".
In acest caz,compilatorul nu va mai genera fila de tip .csm (destul de
voluminoasa) pe care o utilizeaza in situatiile de depanare automata.
Ca rezultat,proiectul va fi mult mai mic (portabil pe diskete).
Nu faceti nici o modificare,daca nu intelegeti exact care va fi rezul-
tatul obtinut.In orice caz,este bine sa aveti o schema cu setarile origi-
nale,pentru a putea reveni la valorile implicite.
In mod similar,puteti alege optiunea Messages,pentru a specifica ce
situatii doriti sa va fie semnalate de catre compilator.
EXEMPLU: Alegeti "Potential Errors" si bifati caseta "Ambiguous operators
need parantheses".In acest caz,in situatiile in care utilizati expresii
complexe,cu mai multi operatori,la care ordinea de precedenta este incerta
compilatorul va utiliza si acest mesaj de avertizare.
Pentru o orientare sumara,cititi si textul afisat imediat sub caseta
"Topics",pentru o explicatie sumara a variantelor respective.
Exista un numar destul de mare de variante posibile,astfel incat
pornind de la aceeasi fila sursa de tip .cpp se pot obtine o varietate de
file executabile,cu proprietati diferite.Asa se explica de ce doua pro-
iecte dezvoltate din aceeasi sursa,ruleaza sau executa programul final in
mod diferit (Exemplu: utilizeaza sau nu utilizeaza numerele cu virgula
mobila).Din acest motiv,atunci cand distribuiti programele realizate de
d-voastra,este bine sa includeti si fila de tip .ide in care sunt deja
setate toate optiunile necesare pentru executia optima.
Setarile astfel specificate,nu vor fi valabile decat pentru proiectul
respectiv.Pentru orice proiect nou,se vor utiliza setarile implicite.

-106-
Dupa specificarea setarilor,proiectul trebuie reconstruit cu Make all,
sau cu Make node.Pentru orice eventualitate,este bine sa detineti si o
copie de siguranta a filelor sursa,astfel incat proiectul sa poata fi
reconstruit de la zero.
Daca rezultatul obtinut nu este cel scontat,reconstruiti proiectul cu
aceleasi file sursa si utilizati setarile implicite,sau reluati procesul
de setare.
Pentru setarea optiunilor,se poate utiliza si meniul Options,respectiv
optiunea Project (vezi mai sus) sau optiunea Enviroment.
Pentru setarea optiunilor referitoare la mediul de operare,alegeti din
meniul Options,optiunea Enviroment.Se va afisa fereastra denumita :
"Enviroment Options" in care puteti modifica mediul de operare:
EXEMPLU: Alegeti SpeedBar (cu un click de mouse) si optiunea Customize:
In caseta Available Buttons alegeti butonul Browse Print si apoi apasati
butonul cu sageata orientata spre caseta Active Buttons.Butonul va trece
in caseta Active Buttons.Confirmati cu OK.Observati ca in meniul princi-
pal,in bara de obiecte SpeedBar a aparut un buton nou (inactivat).
Pentru a sterge butonul,se procedeaza invers.Alegeti butonul Browse Print
din caseta Active Buttons si utilizati butonul cu sageata orientata spre
caseta Available Buttons,apoi confirmati cu OK.
Pentru a alege fonturile si culoarea preferata,utilizati optiunea Fonts.
Pentru a modifica aspectul arborelui de directoare din fereastra Project,
alegeti Project View.De exemplu,daca selectati si casetele "Show run-times
node" si "Show project node",se vor afisa permanent si subdirectoarele
din proiect.
Pentru a modifica aspectul filei script (cea in care introduceti codu-
rile) alegeti Syntax Highlighting si optiunea Customize.Apoi alegeti
culoarea preferata pentru fiecare tip de element:
Exemplu: albastru pentru comentarii,rosu pentru cuvintele rezervate,
negru bold pentru identificator,albastru pentru siruri,verde pentru
preprocesor etc. Daca doriti,puteti utiliza si culori speciale pentru
background,dar,daca fila este prea incarcata de culori va fi mai greu de
citit.In fereastra Sample se va afisa un text cu culorile specificate de
d-voastra.Cand va declarati satisfacuti,confirmati cu OK.
In functie de nivelul de pregatire si de necesitatile de moment,puteti
modifica si celelalte optiuni ale mediului de operare.Nici o modificare
nu este ireversibila,sau daunatoare pentru calculatorul d-voastra.Este
bine sa va notati setarile preferate,astfel incat sa puteti sa reveniti
la ele cat mai usor.Nu apelati la cunoscuti,sau la necunoscuti pentru
"ajutor de specialitate".Nu lasati pe nimeni sa modifice neautorizat
optiunile din programul d-voastra.In situatii disperate,dezinstalati si
apoi reinstalati programul.
In mod normal,este bine sa pastrati o fila de siguranta pentru fiecare
fila generata.In momentul in care efectuati modificari,compilatorul sal-
veaza fila anterioara cu extensia .bak.La nevoie fila poate fi utilizata
pentru refacerea proiectului anterior (se redenumeste cu extensia .cpp).
In cazurile in care generati un numar foarte mare de file .cpp si un
numar mare de actualizari,la care nu doriti sa pastrati cate o copie de
tip .bak,pentru a face economie de memorie,puteti alege din Editor optiu-
nea File si apoi deselectati caseta "Create Backup".Este bine sa va ale-
geti un set de preferite,pe care sa le utilizati pentru toate proiectele.

-107-
PRELUCRAREA MESAJELOR WINDOWS - FUNCTIILE DISPECER (dispatch)

Aplicatiile de program realizate pentru sistemul de operare Windows,


difera radical de cele realizate sub sistemul de operare DOS.In Windows,
aplicatiile realizate nu sunt o insiruire de apeluri catre o anumita
functie sau procedura ci sunt controlate de catre sistemul de operare,
ca rezultat al evenimentelor din aplicatie.Orice aplicatie de tip Windows
este structurata modular.Cel mai simplu modul este fereastra Windows.O
aplicatie Windows lucreaza cu una sau mai multe ferestre.Ferestrele de
tip Windows nu apeleaza direct anumite functii,pentru a obtine un anumit
rezultat,ci asteapta sa fie apelate de catre sistemul de operare.Pentru
fiecare fereastra din aplicatie,sistemul distribuie periodic ( la cateva
nanosecunde-in functie de viteza procesorului) toate mesajele destinate
ferestrei respective.Pentru comunicarea cu sistemul de operare,fiecare
fereastra are o functie specializata,denumita procedura ferestrei(window
procedure),care va receptiona toate datele de intrare si respectiv va
returna catre sistem toate datele de iesire.
Sistemul Windows trimite datele de intrare pentru ferestre sub forma
de mesaje Windows.Mesajele pot fi generate atat de catre sistem,cat si de
catre aplicatia aflata in executie.Sistemul genereaza mesaje pentru fie-
care eveniment din program (Exemple: -deplasarea mouse,click de mouse,
apasarea unei taste...etc.),sau ca raspuns la orice modificare a datelor
din aplicatie (Exemple: -se schimba fonturile,se redimensioneaza fereastra
sau se deplaseaza fereastra...etc.).Aplicatia genereaza mesaje prin care
poate comunica cu alte ferestre,sau cu alte module din program (Exemple:
-se apasa un buton grafic din aplicatie ).
Fiecare mesaj Windows este format din patru elemente: un cod de manipu-
lare interna (window handle),un identificator (message identifier) si doi
parametri care preiau datele (LPARAM si WPARAM).
Codul handle identifica fereastra de destinatie a fiecarui mesaj.Iden-
tificatorul este o constanta prin care se specifica scopul mesajului emis.
(Exemplu: WM_PAINT inseamna ca fereastra va fi redesenata).Cei doi para-
metri (valori pe 32 de biti),contin datele transmise sau adresa de memorie
la care sunt localizate datele ce urmeaza sa fie utilizate pentru prelu-
crarea mesajului.Sensul si valoarea pentru fiecare parametru,difera de
la un mesaj la altul.Astfel,un paramteru poate contine un integer,o va-
loare binara,un pointer spre o adresa de memorie,sau orice alt tip de data
ce poate fi preluat de o valoare pe 32 de biti.Daca mesajul nu contine
nici o data,cei doi parametrii vor fi setati NULL.
Pentru a determina modul in care va fi interpretat fiecare mesaj,pro-
cedura de fereastra trebuie sa utilizeze identificatorul mesajului si o
functie de distributie (dispatch) care sa separe datele si pointerii din
fiecare parametru si sa le trimita catre functiile,procedurile sau meto-
dele interne ale ferestrei respective.
Daca exista un numar mic de mesaje,iar procesorul este liber,mesajele
vor fi transmise la ferestre instantaneu.Daca exista un numar mare de
mesaje emise simultan,mesajele vor fi arhivate temporar intr-o stiva,unde
asteapta sa le vina randul sa fie prelucrate si distribuite.Pentru ampla-
sarea mesajelor in stiva,sistemul utilizeaza o structira de tip MSG,iar
pentru a transmite si receptiona mesajele utilizeaza functii specializate
cum sunt:PostMessage,PeekMessage,GetMessage,WaitMessage etc.

-108-
In aplicatiile de tip API Windows,programul trebuie sa contina o
procedura specializata pentru prelucrarea mesajelor.Aceasta procedura
contine o bucla de repetitie (message loop) care utilizeaza ceasul intern
al procesorului pentru a receptiona si distribui periodic toate mesajele
din aplicatie.O astfel de bucla trebui sa contina urmatoarele functii:
1.GetMessage -copiaza din stiva mesajele arhivate si le preia tot intr-o
structura de tip MSG,de unde urmeaza sa fie prelucrate.Facultativ
se poat asocia si functii de eliberare a stivei,dupa prelucrare.
2.TranslateMessage - se utilizeaza atunci cand mesajele virtuale trebuie
sa fie transformate in mesaje reale.De exemplu,atunci cand obiectul
asteapta date introduse de la tastatura,sistemul genereaza mesaje
de tasta virtuala de tip WM_KEYDOWN (asteapta o tasta).De fiecare
data cand se apasa o tasta,functia TranslateMessage transforma
mesajul virtual intr-un mesaj real care contine caracterul introdus,
intr-un mesaj de tip WM_CHAR si amplaseaza mesajul in stiva.
3.DispatchMessage este functia care selecteaza fereastra de destinatie a
fiecarui mesaj (in functie de codul handle).
O singura bucla poate sa deserveasca toate ferestrele din aplicatie.
In aplicatiile de tip OWL,bucla de mesaje este inlocuita de tabela de
raspuns la evenimente.Pentru a simplifica munca procesorului,aplicatia
nu va mai prelucra toate mesajele si toate evenimentele,ci doar acelea
care sunt incluse in tabela de raspuns la evenimente.Ca rezultat,executia
programului este mult mai rapida si mai simpla.Nu se mai formeaza "cozi
de asteptare" in stiva de mesaje si se exclude riscul de a interpreta
mesaje nesemnificative.Pentru a realiza distributia datelor din parametri
catre metodele si procedurile proprii se utilizeaza o serie de functii
dispecer incluse in fila "dispatch.h".Aceste functii separa datele din
fiecare mesaj,in functie de tipul lor si transfera controlul catre membrii
obiectului respectiv cu ajutorul unor pointeri (pmf).Aceste functii nu
pot fi apelate direct,nici nu pot fi modificate.La nevoie,puteti crea si
adauga in fila de resurse functii noi,care sa satisfaca necesitatile de
moment.Este important sa studiati aceste functii,deoarece nu se pot uti-
liza ca raspuns la evenimentele Windows decat functii,care contin daor
parametrii pentru care exista o functie de distributie gata definita.
EXEMPLU: daca Windows trimite un mesaj de tip WM_CTRLCOLOR catre o
aplicatie,parametrul WParam este de fapt o data de tip HDC iar parametrul
LParam include o data de tip HWND si una de tip UINT (cele doua date ocupa
segmentul si respectiv off-setul adresei).Dupa interventia functiei de
dispatch,datele vor fi preluate din parametri si distribuite functiei
locale de tip OWL astfel : HBrushEvCtrlColor(HDC,HWND,UINT).
Toate functiile de tip dispatch au urmatorii patru parametrii:
1. GENERIC & -este un pointer spre obiect
2. GENERIC::*pmf -este un pointer spre functia membru
3. WPARAM -este unul dintre parametrii mesajului
4. LPARAM -este celalalt parametru al mesajului
In definitiile din fila sursa se utilizeaza urmatoarele abrevieri:
v = void return ; i = int ; U = uint ; H = handle ; I32 = int32 ;
POINT = TPoint& (pointeaza obiectul construit) ; POINTER = void*
Pentru a vedea daca metoda definita de d-voastra poate fi inclusa in
tabela de raspuns la evenimete,studiati continutul filei "dispatch.h"
si analizati daca exista o functie dispecer corespunzatoare.

-109-
Pentru fiecare definitie,primul grup de date indica tipul de data care
va fi returnat,iar cel de al doilea grup de date indica paramatrii accept-
ati.
EXEMPLU: prima functie definita in fila "dispatch.h" este:
int 32 _OWLFUNC i_LPARAM_Dispatch ( GENERIC& generic,
int (GENERIC::*pmf)(int32),
uint wParam,
int32 lParam);
Aceasta functia analizeaza parametrul LParam si returneaza o valoare de
tip int32.Se poate aplica pentru orice mesaj care contine in parametrul
WParam o valoare de tip uint si in parametrul LParam o valoare de tip
int32,atunci cand functia membru pentru care se apeleaza asteapta sa
primeasca un peremetru de tip int32 (valoarea returnata ).
Urmatoarea functie este identica,dar analizeaza parametrul WParam.
Fiecare functie este insotita si de o explicatie sumara:
EXEMPLU:
// parses wParam as bool and lParam as uint and always returns 0
int32 _OWLFUNC v_B_U Dispatch ( GENERIC& generic,
void (GENERIC::*pmf) (bool,uint),
uint wParam,
int32 lParam);
aceasta functie analizeaza parametrul wParam ca pe o valoare booleana
(zero sau unu),parametrul lParam ca pe o valaore de tip integer si retur-
neaza zero (functia este de tip void)
Analizati toate aceste definitii si alegeti cea care va putea prelucra
mesajele Windows astfel incat sa furnizeze valori pentru functia de ras-
puns la evenimentul ales.Este usor de observat ca functia de raspuns poate
avea maximum patru parametrii distincti(cate doi pentru fiecare paramteru
din mesajul Windows).Daca de exemplu LParam contine doua valori de tip
int,acestea vor fi denumite ca lParam.lo si lParam.hi,in functie pozitia
byte-ului in care sunt stocate (byte-ul HI sau byte-ul LO din word).
Daca wParam contine o singura valoare,iar lParam contine doua valori,
functia de raspuns la eveniment rezultata dupa dispatch va putea contine
trei parametri distincti.
Cu cat utilizati mai putine mesaje si mai putine functii de raspuns,cu
atat aplicatia se va executa mai simplu si mai rapid.Cu cat creste numarul
de obiecte,si respectiv numarul de mesaje procesate,cu atat procesorul nu
va mai putea face fata si va trebui ca mesajele sa fie arhivate in stiva
de asteptare,de unde vor fi prelucrate succesiv.
In cazul procesoarelor cu linii paralele de procesare,sau a procesoare-
lor paralele,se pot prelucra simultan mai multe mesaje,sau se poate dedica
un procesor specializat pentru fiecare obiect din aplicatie.Acest gen de
operatii se desfasoara in servere,sau in centralele mari de distributie,
pentru a permite accesul simultan la o anumita arhiva de date (Exemplu:
router-ele pentru reteaua Internet).
Pentru fiecare aplicatie,planificati cu atentie fiecare operatie si
fiecare mesaj exploatat,astfel incat sa evitati operatiile inutile.Pe cat
posibil,evitati "obiectele decorative","artificiile" si "mesajele de aver-
tizare de tip reclama,etc.Cu cat un program este mai economic si mai efi-
cient,cu atat se va bucura de mai multi utilizatori.Evitati sa editati
functii de raspuns care nu vor fi utilizate in program ("de rezerva").

-110-
VDBT (Visual Database Tools)

Versiunile mai recente ale Borland C++ includ si biblioteca Vdbt,care


include file destinate pentru utilizarea datelor organizate tabelar sub
forma de baza de date.Pentru acest scop,au fost realizate o serie de
componente software,organizate sub forma de obiecte vizuale.Constructia
acestor obiecte se bazeaza pe arhitectura de tip COM (Component Object
Model) care sta la baza arhitecturii de tip OLE (Object Link Embadded).
Ca rezultat,aceste componente pot fi utilizate pe platforme diferite.
Pentru vizualizarea acestor obiecte,cea mai clara implementare este cea
realizata de platforma Delphi,unde componentele sunt afisate vizual si
pot fi incluse in aplicatie cu ajutorul indicatorului mouse.In Borland C++
aceste obiecte sunt definite in filele cuprinse in biblioteca Vdbt si
trebuiesc introduse explicit in program.Mai mult,obiectele de acest tip
nu au constructorul si destructorul clar definit,astfel incat nu pot fi
incluse in program decat cu ajutorul unor structuri denumite SourcePool.
Source Pools sunt structuri de date in care unul sau mai multe noduri
din program sunt excluse de la etapa de "constructie".Astfel nu se va
apela constructorul pentru obiectele declarate in aceste structuri,iar
componentele de tip Visual Database Tools nu vor mai returna un mesaj de
eroare.Structurile de tip SourcePoola au si alte aplicatii practice.Se
pot utiliza pentru a partaja o fila de resurse intre mai multe aplicatii,
sau pentru a realiza mai multe versiuni ale aceleiasi aplicatii ( de
exemplu o versiune in format de 16 biti si o versune in format de 32 de
biti).SourcePools se pot utiliza si pentru a partaja aceleasi file de
antet (headers) sau pentru a utiliza in comun aceleasi optiuni de setare a
proiectului (daca proiectul contine mai multe executabile distincte).
Pentru a introduce in proiect o astfel de structura,alegeti din meniul
Project optiunea New Target iar in caseta Type alegeti SourcePool.In
caseta Name introduceti numele dorit,apoi confirmati cu OK.In proiectul
deschis se va realiza un nou nod de genul "Numele [SourcePool]".
Exemplu: ( vezi si OWL_ABC / vdbt1 )
Deschideti un director nou cu numele vdbt1.Salvati in acest director o
fila .cpp oarecare (de la un program anterior).Apoi din meniul New alegeti
Project si deschideti un nou proiect pe o platforma de 32 de biti (in
caseta Platform alegeti Win32).Eliminati din proiect filele .rc si .def,
apoi compilati si construiti proiectul.Executati proiectul.
Pana in acest moment,este un proiect asemanator cu toate celelalte.In
continuare,alegeti din meniul Project optiunea New target si introduceti
Name = Sursa1 si Type = SourcePool.Confirmati cu OK.In continuare,pentru
a adauga file la nodul creat apasati tasta Insert,sau executati un click
de mouse drept si alegeti Add node.Adaugati fila de tip .cpp din proiect.
Repetati operatiile de mai sus si realizati inca un nod de tip Source-
Pool denumit sss1,la care adaugati un anumit numar de file.
Pentru a realiza si o varianta pe 16 biti a proiectului,puteti sa
alegeti din nou din File,New si Project,apoi selectati pentru platforma
optiunea Windows 3.x(16) si acelasi nume cu proiectul realizat pe 32 de
biti,apoi confirmati cu OK (sau utilizati un nume diferit). Adaugati si
la acest nod fila de tip .cpp.In acest moment,proiectul d-voastra va
contine doua noduri executabile de tip .exe si doua noduri de tip
SourcePool,care contin cate o versiune a filei .cpp.

-111-
Pentru a observa avantajele nodului SourcePool deschideti una dintre
filele .cpp si efectuati o serie de modificari (actualizari).Inchideti
fila si puteti constata ca toate filele .cpp au fost actualizate.Din
acest motiv,nodurile de tip SourcePool se prefera atunci cand mai multe
aplicatii partajeaza aceeasi fila de resurse sau de cod.Daca proiectul
contine executabile diferite,actualizarea va interesa doar versiunile de
proiect care au acelasi nume.Acest gen de implementare se utilizeaza
mai ales in etapa de programare a unei aplicatii,cand se utilizeza
simultan una sau mai multe variante ale proiectului,sau etape de proiect
aflate in diferite stadii de dezvoltare.
Dupa construirea unui nod de tip SourcePool,acesta poate fi inclus in
proiect la orice nivel,cu o operatie simpla de tip Drag and Drop.Tineti
apasata tasta Alt,alegeti nodul de tip SourcePool dorit si apoi trageti
cu butonul mouse apasat pana la nivelul de program in care doriti sa
includeti nodul respectiv (vezi OWL+ABC /vdbt2 ).De exemplu,in proiectul
realizat in directorul vdbt1,puteti elimina fila de tip .cpp din nodul
executabil si apoi puteti sa includeti la acest nivel intregul nod de
tip SourcePool salvat in Sursa1.Construiti apoi proiectul cu Make si
executati aplicatia.
Acest gen de implementare,este necesar pentru toate componentele de
tip Tool,si in toate situatiile in care fila sursa contine declaratia
obiectelor dar nu include si constructorul sau destructorul implicit.
Pentru exemplificarea nodului de tip SourcePool studiati si exemplul
Srcpool din Examples/IDE.
Pentru a edita o aplicatie simplista,care utilizeaza o baza de date,
editati urmatoarea fila .cpp.
#include <vdbt\bdto.h>

int PASCAL WinMain( HINSTANCE,HINSTANCE,LPSTR,int)


{
PTTable table = new TTable;
table -> DatabaseName = string( "DivePlan" );
table -> TableName = string( "divestok.db" );
table -> Open();
int count = table->FieldCount;
int i; for (i = 0; i < count; i++)
{ MessageBox( 0,table->Fields[i]-AsString->c_str(),
table->Fields[i]-FieldName->c_str(),MB_OK); };
if (table) delete table;
return 0;
}
Salvati fila intr-un director nou (vdbt3).Construiti proiectul cu New si
Project (bifati si Controls: VBX,respectiv Libraries: OLE).Eliminati
din proiect fila .def si fila .cpp,apoi adaugati un nod de tip SourcePool.
Adaugati la nodul de tip Sourcepool fila .cpp de mai sus,apoi adaugati
din biblioteca Lib si filele ajutatoare bdtof.lib si bdtcfi.lib.Apoi
alegeti fila executabila .exe si cu un click drept de mouse selectati
View options hierarchy.In caseta,alegeti nodul .ide,apasati butonul Edit
iar pentru optiunea Make selectati Autodependencies = None.Confirmati cu
Ok apoi cu Close si construiti proiectul cu Make.Executati aplicatia si
observati primele inregistrari din tabelul "divestoc.db"(din SAMPLEDB).

-112-
Exemplul a fost dezvoltat prin simplificarea exemplului Calcfld din
Examples/Vdbt/Calcfld.Baza de date este inclusa in directorul Sampledb.
Acest exemplu contine cea mai simpla operatie posibila.In exemplu,se
declara si se utilizeaza un obiect de tip TTable,pentru a accesa baza
de date,apoi se utilizeaza o bucla de repetitie pentru a afisa datele
din fiecare inregistrare cu ajutorul unui mesaj de tip MessageBox.
Pentru a studia metodele si proprietatile fiecarui component visual,
utilizati in Help optiunea Data-access componets pentru obiectele uti-
lizate la accesare datelor (TBatchMove,TDatabase,TDataSource,TQuery,
TStoredProc si tTable) si respectiv Data Aware components pentru cele
utilizate la prezentarea si/sau actualizarea datelor(TDBCheckBox,TDBEdit,
TDBComboBox,TDBGrid,TDBImage,TDBListBox,TDBLookupCombo,TDBLookupList,
TDBMemo,TDBNavigator,TDBRadioGroup,TDBText).
Pentru utilizatorii de Delphi,selectarea si utilizarea acestor compo-
nente este mult mai usor de realizat,deoarece se poate face oricand un
control vizual direct (proiectati aplicatia in Delphi si apoi execu-
tati operatiile necesare pentru implementarea in C++).Bazele de date
pot fi utilizate in comun de catre aplicatii scrise in Delphi,C++ sau
respectiv in programele sursa (Fox,VisualFox,Excel etc.).
Biblioteca Vdbt nu este destinata pentru a realiza programe complexe
de tip baza de date ci doar pentru a putea realiza legatura dintre apli-
catiile de tip C++ si o baza de date oarecare.In special,se pot realiza
operatii de preluare,prelucrare si actualizare automata a datelor.Pentru
operatii complexe cu si asupra bazelor de date,se vor prefera intotdeauna
programele specializate (Borland C++ nu are tampoane de memorie proiec-
tate special pentru operatiile cu baze mari de date si exista riscul de
a suprascrie bazele de date cu informatii incorecte(tampoane suprascrise).
Componentele vdbt sunt de tip Vbx si pot fi vizualizate cu ajutorul
editorului de resurse.Pentru a utiliza aceste componente vizuale este
necesara o clasa de tip TDialog si o resursa (de tip .rc) care sa includa
si o resursa de tip DIALOG.Componentele de tip "Data access" pot fi
vizualizate doar in etapa de editare a resursei in timp ce componentele
de tip "Data aware" vor fi prezente si in timpul executiei programului.
Pentru a putea prelua si afisa datele stocate intr-o baza de date este
necesar sa se inlantuiasca cel putin trei componente vizuale: unul de tip
"data access" (TTable sau TQuery),unul de tip "Data aware" (TDBGrid,sau
TDBEDit...etc) si un component de legatura,denumit TDataSource.
Pentru a efectua legaturile necesare dintre obiecte,se poate edita
un algoritm in care se precizeaza proprietatile fiecarui obiect,sau se
poate utiliza obiectul grafic denumit "Property Inspector" care a fost
special realizat pentru acest scop (este similar cu Obiect Inspector din
Delphi).
Un exemplu destul de simplu se gaseste arhivat in Examples\Vdbt\Animals.
Construiti si executati proiectul,apoi deschideti fila IDE cu optiunea
Project/Open.Proiectul contine doua file executabile: animalssrc.exe si
un nod de tip SourcePool denumit "animalssrc".
Selectati fila beastwin.rc si executati un dublu click,apoi executati
un click de mouse pe semnul "+" care precede directorul beastwin.rc.
Din directorul beastwin.rc se va afisa fila resursa denumita :
DIALOG:IDD_ANIMALS(1)
Executati un click de mouse cu butonul drept,pe aceasta fila si alegeti
-113-
optiunea View.Se va deschide o fereastra denumita IDD_ANIMALS in care
sunt prezentate toate componentele vizuale ce formeaza resursa (la fel
ca si fereastra Form din Delphi).
Observati cele doua componente de tip "Data acces" suprapuse peste
obiectul TNavigator.Executati un click de mouse drept pe fiecare dintre
ele si alegeti optiunea Properties.Se va afisa fereastra denumita
Property Inspector in care sunt prezentate toate proprietatile obiectului
si modul in care sunt setate.
De exemplu,pentru obiectul TTable1 puteti observa urmatoarele proprie-
tati: DatabaseName = diveplan,TableName = ANIMALS.DBF si Active = True.
Acesta este obiectul prin care se acceseaza o baza de date oarecare.
Apoi executati un click drept de mouse pe obiectul TDataSource1 si
observati ca proprietatea DataSet este setata TTable1,adica obiectul
TDataSource are ca sursa de date obiectul TTable.
Toate celelalte obiecte vizuale sunt conectate la sursa de date prin
intermediul obiectului TDataSource.De exemplu,executati un click drept
pe obiectul grafic de tip TDBImage (desenul cu un peste exotic) si
alegeti Properties.Observati proprietatea DataSource = TDataSource1.
In mod similar,daca executati un click pe obiectul TDBText6 (cel care
contine textul "Angel Fish"),in Property Inspector puteti identifica
proprietatea DataSource=TDataSource1 si proprietatea DataField = NAME.
Pentru a deschide pe rand mai multe obiecte,puteti utiliza caseta de
dialog de tip TEdit,asociata cu obiectul Property Inspector(executati un
click pe butonul de scroll si alegeti obiectul dorit).Toate obiectele
legate de baza de date vor avea proprietatea DataSource = TDataSource1.
Daca doriti sa preluati si sa afisati date din mai multe baze de date
simultan,este necesar sa utilizati cate un obiect de tip TTable si cate
un obiect de tip TDataSource,pentru fiecare baza de date distincta.Apoi
obiectele care prezinta datele vor fi legate de sursa datelor prin pro-
prietatea DataSource si prin obiectrul TDataSource corespunzator.
Pentru a edita o astfel de fila de resurse,cel mai simplu este sa
modificati o fila de resurse gata existenta (vezi si exemplul vdbt4).
De exemplu,pentru a modifica resursa beastwin.rc,inchideti fereastra
IDD_ANIMALS,apoi executati din nou un click de mouse drept pe fila
DIALOG: IDD_ANIMALS(1) si alegeti optiunea Edit.Se va deschide o fereastra
similara,denumita tot IDD_ANIMALS ,dar de aceasta data,toate proprietatile
obiectelor vor fi editabile.Executati un click drept de mouse in fereastra
IDD_ANIMALS (in zona alba) si selectati optiunile: Control Palette,Tool
Palette si Property Inspector.
Optiunea Control Palette va adauga la meniul principal inca o bara de
meniu in care sunt incluse toate obiectele grafice utilizabile intr-o
resursa de tip DIALOG.Obiectele grafice sunt grupate in functie de tipul
lor in urmatoarele categorii: Standard,Common,BWCC,Data Access,Data Aware
si Custom.Grupele Standard,Common si BWCC contin obiectele Windows de tip
OWL prezentate in capitolul anterior.Se utilizeaza la fel,dar se poate
utiliza Property Inspector pentru setarea proprietatilor.
Grupele DataAccess si Data Aware contin componentele vizuale de tip
vdbt utilizate pentru operatii cu si asupra bazelor de date.
Pentru a a dauga in fila resursa un astfel de obiect,selectati obiectul
dorit,apoi apasati butonul cu sageata alba (denumit Select) si executati
un click de mouse in fereastra,in locul dorit.
-114-
De exemplu,pentru a adauga un obiect de tip TDBGrid in care sa fie
afisate toate datele din baza de date,executati urmatoarele operatii:
1.Selectati si extindeti fereastra Form5 (cea cu puncte mici)
2.Alegeti grupul de obiecte DataAware.
3.Selectati butonul SSView (ultimul).
4.Apasati si butonul Select (cel cu sageata alba).
5.Executati un click de mouse in locul dorit.
6.Selectati noul obiect.
7.Extindeti obiectul la dimensiunea dorita (cu mouse apsat).
8.Executati un click drept de mouse pe obiect si alegeti Properties.
9.In Property Inspector alegeti DataSource si apasati sageata
butonului de derulare (scroll) si alegeti TDataSource1.
10.Inchideti Property Inspector.
11.Inchideti fereastra IDD_ANIMALS.
12.Deschideti fila beastwin.rh si verificati daca obiectul
adaugat a fost adaugat si in lista de definitii (Exemplu:
#define IDC_VBCONTROLS 14 114 )
Pentru siguranta,comparati cu identificatorul utilizat in definitia
obiectului(in fereastra din dreapta verificati linia:
CONTROL "VTSSONLY.VBX;SSView;Sheet1,IDC_VBCONTROL14,"VBControl".....
In mod normal,daca proiectul este construit,fila se va actualiza
automat.In caz contrar,identificatorul trebuie specificat manual.
Fie editati fila beastwin.rh,fie editati resursa (cu optiunea Edit
Text),fie adaugati in fila .cpp #define IDC_VBCONTROL14 114.
13.Inchideti fereastra filei de resurse(confirmati schimbarile).
14.Reconstruiti si executati obiectul.
Pentru siguranta,este bine sa pastrati o copie a filei beastwin.rc
originale(copia de siguranta),inainte de a efectua modificarile de mai
sus.
In mod similar,se poate aduga orice alt component.Verificati insa cu
atentie codul de identificare al obiectului,astfel incat sa fie definit
in fila .cpp inainte de a fi apelat prin apelul filei de resurse.
Daca doriti,puteti sa stergeti toate componentele si sa reeditati
complet fila de resurse.
Pentru a selecta o alta baza de date,se procedeaza astfel:
In Property Inspector se selecteaza obiectul TTable1 si se seteaza pro-
prietatea Active = False,apoi se alege baza de date dorita in proprietatea
DatabaseName,iar apoi se alege tabelul dorit,setand proprietatea TableName.
In final se reseteaza proprietatea Active = True.Din acest moment,sursa de
date va fi tabelul setat in TableName si toate obiectele inlantuite cu
ajutorul obiectului TDataSource vor fi orientate spre aceasta sursa de
date.Ca rezultat,toate obiectele conectate cu aceasta sursa de date vor
trebui resetate pentru proprietatea DataField (exceptie fac situatiile in
care noua baza de date contine aceleasi campuri de date-Exemplu: in cazul
in care o baza de date este actualizata cu o baza de date structurata
identic,dar cu date noi).
Daca bazele de date contin aceleasi campuri,cu aceleasi denumiri,se pot
utiliza in paralel.Astfel,se va conecta cate un obiect de tip TTable si
cate un obiect de tip TDataSource pentru fiecare baza de date.Pentru a
schimba interactiv baza de date activa,in obiectele de prezentare a
datelor se va schimba doar proprietatea DataSource.

-115-
Dupa ce resursa de tip DIALOG are aspectul corespunzator,poate fi
apelata in cadrul constructorului unui obiect de tip TDialog,pentru a
fi inclusa in program.Obiectele de tip TDialog pot fi derivate din clasa
TDialog,definita in fila "owl/dialog.h".Clasa TDialog este derivata din
clasa TWindow,astfel incat poate fi utilizata la fel ca orice alta
fereastra,dar adauga si o serie de metode proprii(Create,DoCreate,Execute,
DoExecute,Destroy,Reposition.etc.).
EXEMPLU: ( vezi si OWL_ABC / vdbt5 )
Deschideti un proiect si editati urmatoarea fila .cpp.
#include <owl/pch.h>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/dialog.h>
#define IDD_ABORTDIALOG 32599
class Aplicatie1 : public TApplication {
public: Aplicatie1() : TApplication() {};
void InitMainWindow(); };
void Aplicatie1::InitMainWindow() {
SetMainWindow(new TFrameWindow(0,"Test",
new TDialog(0,IDD_ABORTDIALOG),true)); };
int OwlMain(int,char* []) { return Aplicatie1().Run(); };
Eliminati din proiect fila .DEF care nu este necesara si inlocuiti fila
de resurse cu fila "owl.printer.rc" (din INCLUDE/OWL ).
Compilati,construiti si executati proiectul (atentie ca fila .cpp si
fila .rc sa fie denumite la fel ca fila .ide).
La executie se va lansa caseta de dialog utila pentru anularea unui
proces de imprimare a datelor.In mod similar,puteti realiza si apela
orice resursa de tip dialog,sau puteti apela cele predefinite.Atentie
ca identificatorul resursei sa fie definit inainte de a fi apelat si sa
corspunda cu cel utilizat in fila de resurse ( IDD_ABORTDIALOG in exemplul
de mai sus).Eventual,identificatorul poate fi inlocuit direct prin codul
sau numeric (32599 in exemplul de mai sus).Pentru resursele create de
d-voastra este bine sa utilizati codurile numerice mici (0-256) pentru
a nu supraincarca resursele standard ale sistemului de operare.
Tipic,clasa TDialog si resursa aferenta se utilizeaza pentru a prelua
informatii de la utilizator,prin intermediul unor obiecte specializate de
tip caseta de dialog(TEdit,TListBox,TComboBox etc.).Obiectele de tip
TDialog sunt ferestre care pot fi afisate in orice loc al ecranului (spre
deosebire de ferestrele de tip "Child" obisnuite,care nu pot fi afisate
decat in interiorul ferestrei "Parent").
Obiectele de tip TDialog pot fi de doua tipuri: modale si modeless.
Cele modale intrerup activitatea din fereastra parinte atat timp cat se
desfasoara operatii in fereastra de tip TDialog.Ca rezultat,nu se pot
face nici un alt fel de operatii in restul obiectelor din program pana
cand nu se inchide fereastra de tip TDialog.Cele de tip modeless,permit
efectuarea de operatii si in obiectele situate in afara ferestrei de tip
TDialog.Pentru a crea un obiect de tip modal se apeleaza metoda: Execute,
iar pentru a crea un obiect de tip modeless,se apeleaza metoda: Create.
Cele doua tipuri au constructori diferiti.
In situatiile in care resursa de tip DIALOG contine componente de tip
vdbt(care nu au constructor),se va include intr-un nod de tip SourcePool.

-116-
Asadar,pentru a utiliza componentele de tip vdbt este necesara o resursa
de tip DIALOG si un obiect de tip TDialog.Pentru ca obiectele din fe-
reastra deschisa sa fie interactive (sa primeasca si sa emita mesaje de
tip Windows) este necesara si o tabela de raspunsuri si un obiect de tip
TVbxEventHandler,care sa preia mesajele obiectului de tip TBIVbxLibrary
utilizat pentru manevrarea obiectelor de tip Vbx.
In exemplul Animals(din Examples/Vdbt),aceste structuri sunt imple-
mentate in patru file diferite (2 file .cpp si doua file header).Pentru
a simplifica putin acest exemplu puteti edita urmatoarea fila .cpp:
( vezi si OWL_ABC / vdbt6)
#include <owl/pch>
#include <owl/applicat.h>
#include <owl/framewin.h>
#include <owl/dialog.h>
#include <owl/vbxctl.h>
#include <owl/opensave.h>
#include <vdbt/dbgrid.h>
#include <vdbt/bdto.h>
#define IDD_ANIMALS 1
#pragma hdrstop
class Anim1 : public TDialog,public TVbxEventHandler{
public:
Anim1(TWindow* parent,TresId = IDD_ANIMALS,TModule* module=0);
virtual ~Anim1();
DECLARE_RESPONSE_TABLE(Anim1); };
DEFINE_RESPONSE_TABLE2(Anim1,TDialog,TVbxEventHandler)
END_RESPONSE_TABLE;
Anim1::Anim1(TWindow* parent,TresId resId,Tmodule* module)
:TDialog(parent,resId,module) { };
Anim1::~Anim1(){ Destroy(); };
class Aplicatie1::InitMainWindow(){
SetMainWindow(new TFrameWindow(0,"Test",new Anim1(0,1,0),true));};
int OwlMain(int,char*[]) {
TBIVbxLibrary Biblioteca;
return Aplicatie1().Run; }
Formati un nod de tip SourcePool(cu Project/New Target),adaugati fila
.cpp de mai sus si resursa de la exemplul Animals,apoi includeti nodul
SourcePool in executabil (apasati tasta Alt si trageti cu mouse apasat).
In final compilati si executati aplicatia.
Exemplul de mai sus,contine toate elementele necesare.Observati ca
pentru clasa Anim1 s-a utilizat o mostenire multipla,iar tabela de
raspuns controleaza mesajele atat pentru fereastra TDialog cat si pentru
obiectul TVbxEventHandler.Ca rezultat,toate actiunile din fereastra
TDialog vor transmite mesaje care vor avea vizibilitate pentru intregul
program.
Acest gen de implementare,este mai laborios decat in Delphi,dar
ofera avantajul de a avea acces nemijlocit la codul aplicatiei,cat si la
tabela de raspunsuri.Astfel,programatorul poate avea controlul absolut al
evenimentelor din aplicatie (in Delphi,tabela de raspunsuri este generata
automat,iar functiile de tratare a evenimentelor sunt predefinite).
In mod similar se poate apela orice resursa de tip DIALOG.

-117-
Pentru a deschide un alt tabel,sau pentru a modifica modul de prezen-
tare a datelor,fila de resurse se poate modifica dupa bunul plac.
EXEMPLU: ( vezi si OWL_ABC /vdbt7 )
Copiati intr-un director nou,fila .cpp si fila de resurse din exemplul
anterior,apoi adaugati un nod de tip SourcePool si adaugati cele doua
file,iar in final adaugati nodul SourcePool la fila executabila.
Apoi deschideti fila .rc (cu Open sau cu dublu click),executati un
click drept de mouse pe fila DIALOG:IDD_ANIMALS(1) si alegeti optiunea
Edit...
Pentru a elimina componentele vizuale,executati un click de mouse
drept pe componentul selectat si apoi alegeti Delete.Eliminati din
resursa toate componentele de tip TDBText si cel de tip TDBImage,apoi
adaugati un component de tip TDBGrid(SSView).
Pentru a alege tabelul dorit,selectati componentul TTable1 si executati
un click drept de mouse,apoi alegeti Properties.In Property Inspector,
setati: DatabaseName=DIVEPLAN si TableName = sites.db,apoi Active = True.
Pentru a inlantui si restul componentelor,alegeti TDataSource1 si setati
DataSet = TTable1,apoi alegeti Sheet1 si TDBNavigator1 si setati Data-
Source = DataSource1.Inchideti si salvati resursa.
Compilati,construiti cu Build,apoi cu Make si executati aplicatia.
Daca analizati fila de definitii beastwin.rh,puteti observa ca nu au
fost sterse codurile de identificare IDC_VBCONTROL de la componentele
eliminate din resursa,dar au fost adaugate coduri noi,pentru componentele
nou adaugate.Din acest motiv,nu este bine ca o fila de resurse sa fie
modificata prea des,deoarece exista riscul de a bulversa codurile de
identificare a resurselor.Mai mult,nu pot exista doua variante de
resursa cu acelasi nume si de acelasi tip.Daca doriti sa utilizati intr-un
proiect mai multe variante de prezentare a datelor,va trebui creata cate
o fila de resurse separata,pentru fiecare varianta de prezentare.
In situatii limita,codurile de identificare pot fi inlocuite cu valori
numerice (alegeti Edit as Text si modificati linia de definitie).In acest
caz nu va mai fi necesara o fila auxiliara in care sunt definite codurile
handle,dar exista riscul sa utilizati aceeasi valoare numerica pentru
doua sau mai multe componente.Cel mai simplu,este sa lasati programul
sa-si gestioneze singur codurile handle.
Pentru a crea o fila de resurse noua,se utilizeaza editorul de resurse
din meniul File.Alegeti File,apoi New,apoi Resource Project si apoi
Resource Script.Pentru ca sa adaugati o resursa de tip dialog,executati
un click drept de mouse in fereastra "noname00.rc" si alegeti optiunea
Show,apoi List by Type.Din lista tipurilor de resurse alegeti DIALOGEX
si executati un nou click de mouse drept,apoi alegeti New.
In fereastra "New Resource" alegeti DIALOGEX si confirmati cu OK.
Eventual puteti utiliza si butonul Options pentru a realiza o versiune
personalizata a ferestrei de dialog.
Se va deschide fereastra IDD_DIALOG1 in care puteti adauga componentele
dorite dupa bunul plac.Pentru a avea acces la paleta de controale si la
paleta de instrumente,executati un click drept de mouse in zona alba si
alegeti una dintre optiunile: Control Palette,ToolPalette,Property Inspec-
tor,sau Grid (de obicei alegeti Control Palette).
Puteti utiliza toate cele trei butoane implicite: OK,Cancel si Help,
sau le puteti elimina cu click drept + Delete.

-118-
EXEMPLU: (vezi si OWL_ABC / vdbt8 )
Pentru a crea o resursa noua,procedati ca mai sus,apoi:
Adaugati un component de tip TQuery (din DataAccess),executati un click
drept,alegeti Properties si setati in Property Inspector urmatoarele
campuri: DatabaseName = DIVEPLAN ,apoi alegeti SQL,executati un click
pe butonul cu trei puncte(...) si in fereastra de editare SQL adaugati
urmatorul Text: SELECT * FROM biosite.db,iar in final alegeti Active
si setati Active=True.
Apoi adaugati un component de tip TDataSource si in Property Inspector
setati DataSet =TQuery1.
Observati ca am inlocuit TTable prin TQuery.Acest tip de component
permite formularea de solicitari in limbaj SQL,prin care se pot selecta
doar un anumit tip de date din baza de date,se pot ordona datele dupa
un anumit criteriu,sau se pot executa o serie de operatii automate asupra
datelor (suma,selectie,filtrare,selectie incrucisata din doua sau mai
multe tabele,etc.).Pentru detalii despre limbajul SQL si despre compo-
nentele vizuale,puteti consulta si manualul: "Limbajul Delphi ABC-doar",
sau orice alta sursa de informare.
Pentru afisarea datelor,adaugati un component de tip SSView(Sheet1)
din grupul DataAware si setati in Property Inspector proprietatea
DataSource = TDataSource1.
Pentru a putea naviga cat mai usor in tabel,adaugati si un component de
tip TDBNavigator si setati proprietatea DataSource = TDataSource1.Acest
obiect permite operatii complexe,cum ar fi: adaugarea sau inserearea unei
inregistrari noi,stergerea definitiva a unei inregistrari,navigarea in
tabel etc.Pentru a adauga o inregistrare noua,apasati butonul cu semnul
plus,iar pentru a sterge inregistrarea curenta,apasati butonul cu semnul
minus,etc.Fiecare buton are in Property Inspector un camp special destinat
prin care poate fi setat "activ" sau poate fi inactivat.De exemplu,pentru
a bloca orice operatie de stergere a datelor,selectati BtnDelete si
alegeti optiunea Off sau Disabled.
Dimensionati si redimensionati fiecare component dupa bunul plac.
Apoi puteti utiliza restul proprietatilor pentru a adauga o amprenta
personala.De exemplu adaugati un text oarecare cu un component de tip
TDBtext,alegeti fonturile si culorile preferate etc.
In final,inchideti fereastra de dialog si salvati resursa.
Apoi copiati fila .cpp si resursa intr-un director nou,adaugati nodul
de tip SourcePool,construiti si executati proiectul la fel ca si cel
precedent.In fila .cpp puteti inlocui IDD_ANIMALS prin IDD_DIALOG1,sau
puteti utiliza direct un cod numeric oarecare de tip INT.
Pentru fiecare resursa nou construita,este bine sa respectati toate
elementele standard si sa utilizati elemente care vor putea fi utilizate
si in alte proiecte,sau de catre alti utilizatori.Din acest motiv,este
bine ca fiecare component sa fie spatiat cu atentie,sa fie amplasat cat
mai simetric,sa prezinte datele cat mai clar,sa evidentieze datele cele
mai importante si daca se poate sa fie realizate in culori placute,usor
de distins.Denumirile si codurile handle sa fie cat mai clare si sa ex-
prime cat mai sugestiv continutul resursei pe care o ordoneaza,pentru a
fi cat mai usor de identificat intr-un program.Cu cat sunt mai complexe,
cu atat se pot strecura mai usor eventualele greseli de conceptie sau de
implementare.

-119-
Pentru actualizarea datelor dintr-un tabel: se poate utiliza TNavigator
pentru a adauga si insera inregistrari,iar datele se pot introduce direct
in tabel.O alta modalitate,mult mai eleganta,este sa utilizati componente
specializate (Exemplu: TDBEdit) care preiau si actualizeaza doar un anumit
camp de date din tabel si exculd orice eroare accidentala de selectare a
casetei dorite.
EXEMPLU: (vezi si OWL_ABC /vdbt9 )
Realizati o resursa noua astfel:
1.Adaugati un component TTable si setati: DatabaseName = DIVEPLAN,
TableName = biolife.db si Active = True
2.Adaugati un component TDataSource si setati: DataSet =TTable1
3.Adaugati un component SSView si setati: DataSource = TDataSource1
4.Adaugati un component TDBImage si setati: DataSource = TDataSource1 si
DataField = Graphic
5.Adaugati un component TDBMemo si setati: DataSource = TDataSource1 si
DataField = Notes
6.Adaugati un component TDBEdit si setati: DataSource = TDataSource1 si
DataField = Common Name
7.Adaugati alt component TDBEdit si setati: DataSource = TDataSource1 si
DataField = Length(cm)
8.Adaugati un component TDBNavigator si setati: DataSource = TDataSource1

Dimensionati fiecare component dupa bunul plac,apoi alegeti pozitia


in fereastra,culorile etc.Eventual adaugati si texte explicative cu
ajutorul unor componente de tip TDBText (textul se scrie in Caption).
Inchideti si salvati resursa.Copiati resursa si fila .cpp intr-un director
nou,apoi adaugati nodul de tip SourcePool si construiti proiectul la fel
ca si cele precedente(copiati fila .cpp din proiectul anterior).
Construiti proiectul cu Make si executati aplicatia.
Daca doriti sa modificati continutul tabelului,selectati cu mouse
caseta dorita si introduceti datele dorite.Alternativ,puteti sa utilizati
cele doua casete de tip TDBEdit,pentru a modifica doar denumirea comuna,
sau lungimea in centimetri a fiecarei specii.
Modul de implemenetare difera dupa tipul de utilizator.In general,pentru
programele realizate pentru uz propriu,se prefera tipul de implementari
care ofera acces complet,direct si nemijlocit la toate datele din aplica-
tie.Pentru cele realizate pentru uzul unor utilizatori neavizati,se vor
prefera implementarile in care utilizatorul nu poate avea aces decat la
un numar limitat de campuri,si/sau in anumite conditii,pentru a evita la
maximul dezorganizarea datelor (accidental sau intentionat).
De exemplu,pentru a bloca complet editarea si/sau actualizarea date-
lor se poate seta in TTable1 proprietatea ReadOnly = True.In acest caz
utilizatorul va putea utiliza resursa doar pentru a citi datele iar
programatorul va utiliza o alta resursa pentru a actualiza datele.
Fiecare component are o serie intreaga de proprietati specifice prin
care seteaza caracteristicile de editare si/sau imprimare,modul de acces,
etc.Studiati cu atentie fiecare proprietate si documentatia sa,pentru a
putea distinge eventualele aplicatii practice.Exemplu:componentele de tip
DataAware au si o proprietate denumita Visible prin care pot fi afisate
sau eliminate din fereatra de dialog,interactiv (de exemplu cu un buton
de control).Nu ezitati sa incercati cat mai multe variante posibile.

-120-
Pentru a utiliza un tabel izolat,care nu este inclus intr-o baza de
date,copiati tabelul in directorul BIN,construiti resursa de tip dialog,
apoi construiti proiectul si copiati tabelul si in directorul curent.
(vezi si OWL_ABC / vdbt10 ) (in TTable setati direct TableName).

CONCLUZII

Acest manual nu epuizeaza resursele programului C++.Daca deschideti


directorul INCLUDE,puteti observa si restul bibliotecilor incluse in
versiunea Borland C++ 5.0A (Classlib,GI,Ideaddon,Ocf,Osl,Owlcvt,Services,
Sys,Win16,Win32 si Winsys).Ramane sa explorati singuri aceste biblioteci
si sa alegeti functiile,componentele si clasele ca va sunt utile.Mai mult
decat atat,exista o gama extrem de variata de biblioteci si componente
noi,care pot fi adaugate din diverse surse (Internet,CD-uri etc.).
Cu notiunile de pana acum,ar trebui sa fiti capabili sa continuati
studiul pe cont propriu.Explorati cu atentie fiecare fila,studiati cu
atentie toate referintele bibliografice si mai ales incercati sa utilizati
in exemple si programe fiecare dintre facilitatile programului.
Nu va descurajati atunci cand gresiti ceva.Corectati cu rabdare fiecare
eroare returnata de compilator,pana cand programul merge perfect.Nu igno-
rati mesajele de avertisment,chiar daca programul merge aparent "perfect".
In etapa de studiu este extrem de improbabil sa puteti edita un program
intreg,fara nici o greseala.Fiecare mesaj de eroare,va arata de fapt ce
notiuni va sunt inca insuficient de clare,sau chiar complet necunoscute.
Pentru fiecare eroare,studiati bibliografia,faceti o mica fisa,sau o
scurta sinteza in scris.Exista si inevitabilele erori generate de o simpla
neatentie,greseli de ortografie,etc.
Limbajul C++ si descendentul sau Visual C++,utilizeaza extensiv con-
ceptul modular si structurile de tip obiect.Este bine sa utilizati aceste
concepte,pentru orice program de interes general,pentru filele de resurse,
si pentru orice component sau modul care considerati ca va putea fi inclus
si in alte aplicatii si programe.Pentru exemplele si exercitiile "de unica
folosinta",nu are rost sa va complicati cu concepte prea sofisticate,ci e
preferabil sa alegeti cea mai simpla cale care rezolva problema propusa.
Atunci cand programul va fi structurat modular,e bine sa aveti o schema
de ansambu a proiectului,diagrame grafice sau tabele si o schita a lega-
turilor dintre module (link-uri),astfel incat fiecare modul adaugat sa se
incadreze intr-un concept global.Nu abuzati de modularitate,doar pentru
ca "este la moda".Fiecare modul nou,inseamna pentru sistemul de operare
o referinta in plus,care va incarca memoria de operare cu un cod suplimen-
tar si va reduce din viteza globala de executie.
Daca realizati programe ce urmeaza sa fie utilizate de alte persoane,
nu contati pe conostiintele software sau pe experienta utilizatorului.
Programele trebuie sa contina tot ce este necesar pentru executie,sa se
instaleze cat mai simplu (preferabil prin simpla copiere) si sa poata fi
executate imediat,fara nici o alta operatie preliminara.
Acest abecedar nu este o garantie a succesului.Pentru ca sa puteti
scrie programe bune si foarte bune,trebuie sa depuneti un efort individual
sustinut(toti am studiat acelasi abecedar,dar nu scriem toti ca Eminescu).

- S F A R S I T -

S-ar putea să vă placă și

  • Cap 4 1
    Cap 4 1
    Document11 pagini
    Cap 4 1
    GabrielDanaila
    100% (1)
  • Cap 2
    Cap 2
    Document34 pagini
    Cap 2
    alynacke25
    Încă nu există evaluări
  • Cap 3
    Cap 3
    Document32 pagini
    Cap 3
    George Stroia
    Încă nu există evaluări
  • Cap 4 2
    Cap 4 2
    Document11 pagini
    Cap 4 2
    Teodorescu Alexandru
    Încă nu există evaluări
  • Manual C++ ABC
    Manual C++ ABC
    Document135 pagini
    Manual C++ ABC
    cos
    Încă nu există evaluări
  • Cap 1 Curs Fiabilitate
    Cap 1 Curs Fiabilitate
    Document8 pagini
    Cap 1 Curs Fiabilitate
    Ionescu Viorel
    Încă nu există evaluări
  • Lectie Introducere in C++ PDF
    Lectie Introducere in C++ PDF
    Document70 pagini
    Lectie Introducere in C++ PDF
    cos
    Încă nu există evaluări
  • PC2008
    PC2008
    Document202 pagini
    PC2008
    smedrea
    Încă nu există evaluări
  • TSM TSM: Today Software Magazine
    TSM TSM: Today Software Magazine
    Document42 pagini
    TSM TSM: Today Software Magazine
    cos
    Încă nu există evaluări
  • PC2008
    PC2008
    Document202 pagini
    PC2008
    smedrea
    Încă nu există evaluări