n cadrul acestui laborator este prezentat un program care realizeaz un meniu arborescent la consol, oarecum similar cu meniul principal al aplicaiilor windows. Partea central a programului este o bibliotec de clase pe baza creia poate fi creat un meniu propriu. Biblioteca demonstreaz utilizarea noiunilor de POO nvate pn n prezent. La sfrit, sunt propuse cteva exerciii, care presupun realizarea unor meniuri proprii. n continuare vom crea un proiect pe baza codului din arhiva atasata. S executm programul obinut i s studiem funcionarea lui. 2. Biblioteca de clase meniu.h
Header-ul meniu.h declar o ierarhie de 3 clase. Clasa de baz este ElementMeniu, care reprezint orice element de meniu att meniul principal, un submeniu sau o operaie final. Toate elementele meniu concrete sunt clase derivate direct sau indirect din ElementMeniu. Din ElementMeniu sunt derivate direct clasele Meniu i Operatie. Meniu reprezint meniul principal sau un submeniu. Iar Operatie o operaie final. 3. Clasa ElementMeniu
class ElementMeniu { private: char *nume; ElementMeniu *parinte;
Clasa conine membrii comuni pentru toate elementele. Cmpurile clasei: nume numele elementului, afiat atunci cnd se ajunge la acel element. parinte elementul printe. null dac este meniul principal. Constructorul permite crearea unui ElementMeniu pe baza numelui. Cteva metode: afisareIncompletaTitlu() afieaza titlul tuturor prinilor, de la rdcina pn la elementul curent, separate prin sgeat ->: void ElementMeniu::afisareIncompletaTitlu() { if (parinte != NULL) { parinte->afisareIncompletaTitlu(); cout << " -> " << nume; } else { cout << nume; } }
afisareTitlu() afieaz titlul aa cum apare pe prima linie de ecran, cnd ne aflm n elementul curent. Implementarea este bazat pe afisareIncompletaTitlu() : void ElementMeniu::afisareTitlu() { afisareIncompletaTitlu(); cout << ":" << endl << endl; }
Aceste 2 funcii au modul acces protected pentru c sunt apelate din funcia executa() a claselor derivate. Apelarea lor din exteriorul ierarhiei nu este necesar. executa() este o metod virtual pur. n clasele derivate conine logica ce este executat atunci cnd utilizatorul selecteaz meniul curent. 4. Clasa Operatie
class Operatie : public ElementMeniu { protected: Operatie(char *nume);
/*operatia specifica acestui element virtual void execOperatie() = 0; */
public: /*intreaga logica a elementului - afisare titlu + operatie */ void executa(); };
Constructorul clasei preia argumentul nume i l transmite constructorului clasei de baz: Operatie::Operatie(char *nume) : ElementMeniu(nume) {}
Funcia executa() realizeaz partea comun tuturor operaiilor terge ecranul, afieaz poziia curent n meniu, care apare pe prima line a ecranului, i apeleaz execOperatie() pentru a-i da posibilitatea clasei derivate s-i realizeze logica: void Operatie::executa() { clrscr(); this->afisareTitlu(); this->execOperatie(); }
execOperatie() funcia virtual pur va conine n clasele derivate logica specific operaiei. Rostul de a avea 2 funcii executa() i execOperatie() n loc s avem doar executa() a fost de a reutiliza codul. Logica comun tuturor operaiilor este plasat n funcia executa(), iar partea diferit n funcia execOperatie() a claselor derivate. 5. Clasa Meniu
class Meniu : public ElementMeniu { private: static const int nrMaxElemente = 9; int nrElemente; ElementMeniu **elemente;
void adaugaElement(ElementMeniu *element); void executa(); }; Reamintim, clasa Meniu reprezint un meniu principal sau un submeniu. Clasa conine o list de referine ctre alte elemente de tip ElementMeniu. Cmpuri: nrMaxElemente cmp constant, numrul maxim de elemente a meniului. ntruct spre elemente se navigheaza cu tastele 1-9, numrul lor maxim este 9. nrElemente numrul de elemente elemente vector de pointeri ctre elementele meniului. Deoarece tipul pointerilor este ElementMeniu, elementele pot fi de orice tip derivat din ElementMeniu, att submeniuri ct i operaii. Consturctor: Meniu::Meniu(char *nume) : ElementMeniu(nume) { this->elemente = new ElementMeniu*[nrMaxElemente]; this->nrElemente = 0; }
Parametrul nume este transmis constructorului clasei de baz. n corpul constructorului se iniializeaz cmpurile clasei. Destructor: Meniu::~Meniu() { for (int i=0; i<nrElemente; i++) { delete elemente[i]; } delete[] elemente; }
Pn n prezent n clasele pe care le-am studiat, n destructor s-au dealocat doar acele zone de memorie care au fost alocate n cadrul clasei, de obicei n constructor. ns n clasa Meniu ne este mai simplu s dealocm i elementele meniu referite, chiar dac acestea au fost alocate n alt parte. Aceasta pentru c elementele meniu nu au nici o utilitate n afara meniului principal, i trebuie dealocate odat cu el. Destructorul clasei Meniu i va dealoca elementele, eventual recursiv, i memoria dinamic alocat. Unele funcii: citireComanda() execut o bucl infinit n care se citete o tast. Dac tasta este valid se iese din funcie i se returneaz codul corespunztor aciunii selectate, n caz contrar bucla se repet. /*returneaza indicele elementului activat, sau -1 pentru iesire*/ int Meniu::citireComanda() { while (1) { char ch; cout << "Introduceti comanda:"; ch = _getch(); cout << endl; if (ch > '0' && (ch - '0') <= this->nrElemente) { //element meniu int comanda = ch - '1'; /*pt '1' va fi elementul 0*/
adaugaElement(ElementMeniu *element) adaug un element. n element, printele este setat s fie obiectul curent this. void Meniu::adaugaElement(ElementMeniu *element) { nrElemente++; elemente[nrElemente - 1] = element; element->parinte = this; }
Pentru a putea executa instruciunea: element->parinte = this;
a fost nevoie de a declara clasa Meniu prieten a clasei ElementMeniu. Vedei ultima linie a declaraiei clasei ElementMeniu n meniu.h. A fost necesar pentru a putea accesa cmpul element->parinte din clasa Meniu. executa() conine o bucl infinit n care se afieaz meniul, se citete o comand i se execut elementul selectat. Din funcie se iese atunci cnd comanda citit este -1 ieirea din meniu. void Meniu::executa() { for (;;) { int comanda;
clrscr(); afisareTitlu(); afisare(); comanda = citireComanda(); if (comanda >=0 && comanda < nrElemente) { elemente[comanda]->executa(); } else { //probabil iesirea - -1 return; } } } 6. Clasele operaii
O operaie din meniu este o clas derivat din Operatie. Pentru a le separa de clasele din biblioteca, operaiile au fost definite n fiiere separate operatiiSimple.h i operatiiSimple.cpp. De exemplu, clasa OperatieAdunare:
operatiiSimple.h
class OperatieAdunare : public Operatie { public: OperatieAdunare(char *nume); void execOperatie(); };
void OperatieAdunare::execOperatie() { int a, b; cout << "Introduceti 2 numere:"; cin >> a >> b; cout << "suma = " << a + b << endl; pauza(); }
Tot ce trebuie s facem ntr-o operaie simpl este s definim constructorul, i s implementm funcia virtual execOperatie(). La sfritul lui execOperatie() s-a apelat pauza() pentru a da posibilitatea utilizatorului s vad rezultatul, nainte s se ntoarc n meniu. 7. Funcia main()
n main() este construit arborele meniului, apoi este executat meniul principal.
Pe prima linie este instaniat meniul principal: Meniu *meniu = new Meniu("Meniu Principal");
n continuare la meniu sunt adugate elementele: Meniu *submeniuCalculator = new Meniu("Calculator"); meniu->adaugaElement(submeniuCalculator); submeniuCalculator->adaugaElement(new OperatieAdunare("'+'")); submeniuCalculator->adaugaElement(new OperatieScadere("'-'"));
Ca s adugm un element trebuie s-l instaniem i s-l adugm la printele su, apelnd funcia adaugaElement() din printe: submeniuCalculator->adaugaElement(new OperatieAdunare("'+'"));
Pornirea meniului principal se face apelnd: meniu->executa();
Att timp ct programul se va afla n meniu, ne vom afla n aceast funcie. La sfrit, meniul principal este dealocat, iar destructorul su dealoc recursiv tot arborele de elemente: delete meniu; 8. Clasa Lista
Mai jos este prezentat clasa Lista, care reprezint o list de numere. Clasa urmeaz s fie utilizat ntr-un meniu. Suport operaia de adugare a unui numr, prin intermediul operatorului +=, tergere a unui numr dup index, i afiarea listei. Fiecare numr are un index, ncepand de la 0, la fel ca un vector. Codul listei este deja adugat la proiectul meniuri.
lista.h #ifndef _lista_ #define _lista_
/*reprezinta o lista de numere. Fiecare numar are un index, incepand de la 0, la fel ca intr-un vector.*/ class Lista { private: int *elem; int n, dim; public: Lista(int dim); ~Lista(); void operator+=(int num); void sterge(int index);
//afiseaza numerele si indecsii lor void afisare(); };
#endif
lista.cpp #include<iostream> #include"lista.h" using namespace std;
Ne propunem sa crem un meniu care s gestioneze o list de numere reprezentat de Lista. Avem nevoie de minim 2 operaii una care s adauge un numar la list, i alta care s afieze lista. De data aceasta clasele operaii sunt mai complexe. Ele trebuie s aib acces la obiectul list. Iar lista trebuie s fie aceeai pentru ambele operaii. Singurul mod elegant n care putem realiza acest lucru este s avem n clasele operaii un cmp de tip pointer la Lista. i ambele operaii s refere prin intermediul pointerului aceeai list. Lista nu poate fi instaniat n nici una din clasele operaii. Vom instania lista cu un nivel mai sus n funcia main(). i o vom transmite operaiilor prin intermediul unui parametru la constructor. Mai jos este prezentat codul celor 2 operaii:
lista.h
class OpAdaugaInLista : public Operatie { private: Lista *lista; public: OpAdaugaInLista(char *nume, Lista *lista); void execOperatie(); };
class OpAfisareLista : public Operatie { private: Lista *lista; public: OpAfisareLista(char *nume, Lista *lista); void execOperatie(); };
Adugai cele 2 clase n program. Completai funcia main() cu codul necesar pentru a crea un submeniu al listei cu cele 2 operaii. Observai c ambele clase operaii conin un cmp pointer la list. Obiectul Lista trebuie creat i dealocat n main(). Rulai programul rezultat. 10. Exerciii
1. Implementai o operaie pentru tergerea unui element din list dup index.
2. Adugai la list operatorul de indexare []. Operatorul va servi pentru accesarea unui element dup index. Implementai n meniu operaia de afiare a unui element dup index.
3. Implementai un meniu pentru 2 liste. Redefinii n clasa list operatorul +=, astfel nct s accepte un operand dreapta de tip Lista&. Instruciunea list1+=list2 va aduga toate elementele din list2 n list1. Implementai o operaie meniu care s testeze acest operator.
afisare() afiare mulime. 4. Implementai un meniu care s testeze clasa Multime de la exerciiul 1 din lab. 3.