Sunteți pe pagina 1din 32

Soluii

n aceast seciune sunt prezentate soluii pentru unele din exerciiile propuse n text. n general, s-au selectat acele exerciii care ofer o modalitate de abordare a unei anumite categorii de probleme i cele care ofer rspunsuri n situaii mai deosebite de execuie. Celelalte, care extind doar materialul deja soluionat, rmn ca exerciiu de programare pentru cititori.

1 Concepte de baz ale limbajului C++


E1.2 Operaia de copiere dat n enun este foarte ineficient deoarece funcia strlen() returneaz lunginea unui ir de caractere terminat cu nul prin parcurgerea acestuia de la primul pn la ultimul caracter, deci n total irul este parcurs de dou ori: o dat pentru aflarea lungimii irului q i a doua oar pentru copiere. Se poate implementa operaia de copiere a dou iruri i mai eficient (cu o singur parcurgere a irurilor ) i mai concis folosind operaii asupra pointerilor astfel:
while (*q! = 0) { *p = *q; p++; q++ } *p = 0; // irul trebuie terminat cu zero

Deoarece incrementarea postfix permite utilizarea incrementarea acesteia, se poate scrie mai concis astfel:
while (*q! = 0) *p++ = *q++; *p = 0;

valorii

apoi

Deoarece valoarea *p++ = *q++ este *q, se poate scrie nc mai concis:
while(*p++ = *q++);

E1.4 Funcia fr() returneaz o referin la o variabil local. Unele compilatoare dau un mesaj de atenionare (warning) n acest sens. Dup apelul funciei fr(), int& v = fr(4, 6); referina v va indica variabila local x n funcia, care se afl n stiv. Apelul altei funcii (cum este funcia operator <<) folosete stiva, modificnd coninutul acesteia, deci inclusiv locaia de memorie refereniat cu numele v. De aceea, cele dou operaii de scriere la consol a referinei v pot s dea rezultate diferite.

214

Elemente de Programare Orientat pe Obiecte

E1.8

n aceast funcie trebuie s fie testat pointerul returnat de operatorul new:


void f(long n){ int* p = new int[n]; if (!p) cout << "Eroare alocare\n"; else cout << "Alocare corecta\n"; }

Un exemplu de utilizare a acestei funcii:


void main (){ f(100); f(100000000); } // afiseaza: Alocare corecta // afiseaza: Eroare alocare

2 Clase i obiecte
E2.4 Mesajele sunt: Nr. Nr. Nr. Nr. Nr. Nr. Nr. Nr. Nr. Nr. Nr. Nr. Nr. Nr. Nr. Nr. obiecte obiecte obiecte obiecte obiecte obiecte obiecte obiecte obiecte obiecte obiecte obiecte obiecte obiecte obiecte obiecte 1 2 3 4 5 4 3 4 5 6 5 4 3 2 1 0

Primele trei obiecte (ob1, ob2, ob3) sunt create la nceputul funciei main(). La intrarea n blocul nou creat se mai creeaz 2 obiecte (ob4 i ob5) (deci numrul de obiecte ajunge la 5), care sunt imediat distruse la ieirea din bloc. Dup aceasta se creeaz un vector de 3 obiecte, care sunt distruse prin apelul operatorului delete. La ieirea din funcia main() se distrug i cele 3 obiecte rmase, ob1, ob2, ob3. E2.5 Programul modificat este:
#include <iostream.h>

Soluii class Item{ static int numbers; // (a) public: Item(){ // constructorul creste cu 1 nr obiectelor numbers++; cout << "Nr. obiecte " << numbers << endl; } ~Item(){ //destructorul descreste cu 1 nr obiectelor numbers--; cout << "Nr. obiecte " << numbers << endl; } }; int Item::numbers = 0; // (b) void main(){ Item ob1, ob2, ob3; { // se deschide un nou bloc Item ob4, ob5; } Item *pob6 = new Item[3]; delete [] pob6; }

215

Se remarc urmtoarele modificri notate (a) i (b): (a) numbers este declarat membru static al clasei Item. (b) numbers este definit i iniializat n alt parte n program, n afara clasei Item. (a) Mesajul afiat la consol este: 11 22 33 44 (b) Modificarea pointerilor p i p1, urmat de tergerea lor cu delete produce eroare de execuie, deoarece se ncearc eliberarea altei zone de memorie dect cea alocat. E2.7 Funciile de inserare i eliminare a unui element din vectorul clasei IntArray sunt:
int IntArray::InsertAt(int i, int x){ if (i < 0 || i > count){ cout << Eroare indice de inserare\n; return -1; // eroare de inserare } else { if (i == count) Add(x); else { if (count < size){ for (int j= count; j > i;j--) p[j] = p[j-1]; p[i] = x; count++; } else {

E2.6

216

Elemente de Programare Orientat pe Obiecte int *p1 = p; p = new int[count + grows]; for (int j=0; j < i;j++) p[j] = p1[j]; p[i] = x; for (j=i; j < count;j++) p[j+1] = p1[j]; delete p1; count++;

} } int IntArray::RemoveAt(int i) { if (i < 0 || i >= count){ cout << Eroare de stergere element\n; return 0; } else { for(int j=i; j < count-1;j++) p[j] = p[j+1]; count--; return 1; } }

} } return count - 1;

Modificarea vectorului din problema precedent se obine astfel:


void main() { IntArray array1; array1.Add(11); array1.Add(22); array1.Add(33); array1.Add(44); array1.Add(55); array1.InsertAt(2,0); array1.RemoveAt(4); int val; int count = array1.GetCount(); for (int i=0;i<count;i++){ array1.GetAt(i,&val); cout << val << " "; } cout << endl; }

Mesajul la consol este: 22

0 33 55.

E2.8 La apelul funciei fint()programul d eroare de execuie, deoarece se transmite un argument de apel prin valoare care se copiaz n variabila local

Soluii

217

corespunztoare. Eroarea apare datorit copierii membru cu membru a unui obiect de clas IntArray care conine date alocate dinamic. Soluia corect este de a se defini un constructor de copiere al clasei IntArray astfel:
IntArray::IntArray(IntArray &r){ count = r.count; grows = r.grows; size = r.size; p = new int[size]; for (int i = 0; i < count; i++) p[i] = r.p[i]; }

E2.11 Este necesar ca unele funcii membre ale clasei s acceseze obiecte declarate const. Deci sunt necesare urmtoarele modificri: int GetCount() const {return count;} int GetAt(int i) const;

3 Implementarea modelelor de date


E3.1 Definiiile funciilor membre ale clasei IntNode sunt:
IntNode::~IntNode(){ cout << "Destructor nod v = " << v << endl; if (link) delete link; } IntNode* IntNode::AddHead(int x){ IntNode* p = new IntNode(x); p->link = this; return p; } IntNode* IntNode::RemoveHead(){ IntNode* p = link; link = 0; return p; } void IntNode::Display(){ IntNode *current = this; while (current){ cout << current->v <<" "; current = current->link; } cout << endl; }

Dezavantajele acestei implementri sunt: nu se poate defini o list vid; orice operaie de inserare sau tergere modific valoarea pointerului la list; pentru

218

Elemente de Programare Orientat pe Obiecte

extragerea unui element se folosesc dou funcii GetHead() i RemoveHead(), iar dup aceea nodul eliminat din list trebuie ters folosind operatorul delete. E3.2 Destructorii claselor IntSListNode i IntSList se mai pot defini i astfel:
~IntSListNode(){ cout << "Destructor nod v = "<< v <<endl; if(link){ delete link; link = 0; } } IntSList::~IntSList(){ IntSListNode* p = first; if (first){ delete first; first = 0; } }

Cu aceste definiii nodurile listei se terg n ordine invers, ncepnd cu ultimul nod i sfrind cu primul nod. Mesajele care se afieaz la execuia funciei fslist1() sunt urmtoarele:
1 2 3 4 5 Destructor Destructor Destructor Destructor Destructor nod nod nod nod nod v v v v v = = = = = 5 4 3 2 1

E3.3 Funciile constructori i destructori ale claselor IntDListNode i IntDList sunt:


IntDListNode::~IntDListNode(){ cout << "Destructor nod v = "<< v<<endl; } IntDList::IntDList(int* p, int n){ first = 0; last = 0; count = 0; for (int i=0;i<n;i++) AddTail(p[i]); } IntDList::IntDList(const IntArray& array){ first = 0; last = 0; count = 0; int n = array.GetCount();

Soluii for (int i=0;i<n;i++) AddTail(array.GetAt(i));

219

} IntDList::IntDList(IntDList& r){ first = 0; last = 0; count = 0; IntDListNode* ref = r.first; for (int i=0;i<r.count;i++){ AddTail(ref->v); ref = ref->next; } } IntDList::~IntDList(){ IntDListNode* p = first; while (p){ IntDListNode* current = p; p = p->next; delete current; } }

Versiunile constructorilor clasei IntDList folosind funcia AddHead() sunt:


IntDList::IntDList(int* p, int n){ first = 0; last = 0; count = 0; for (int i=0;i<n;i++) AddHead(p[n-1-i]); } IntDList::IntDList(const IntArray& array){ first = 0; last = 0; count = 0; int n = array.GetCount(); for (int i=0;i<n;i++) AddHead(array.GetAt(n-1-i)); } IntDList::IntDList(IntDList& r){ first = 0; last = 0; count = 0; IntDListNode* ref = r.last; for (int i=0;i<r.count;i++){ AddHead(ref->v); ref = ref->prev; } }

220

Elemente de Programare Orientat pe Obiecte

Funcia Display() a clasei IntDList afieaz elementele listei de la capul listei pn la sfritul acesteia. Funcia ReverseDisplay() afieaz elementele listei n ordine invers: de la sfritul listei pn la primul element.
void IntDList::Display(){ IntDListNode* p = first; while (p){ cout << p->v <<" "; p = p->next; } cout << endl; } void IntDList::ReverseDisplay(){ IntDListNode* p = last; while (p){ cout << p->v<<" "; p = p->prev; } cout << endl; }

Funciile de citire a elementelui din capul listei (GetHead()) i a elementului de la sfritul listei (GetTail()) i funciile de extragere element (RemoveHead() i RemoveTail()) sunt urmtoarele:
int IntDList::GetHead(){ if (count == 0){ cout << Eroare lista vida\n; return -1; } else return first->v; } int IntDList::GetTail(){ if (count == 0){ cout << Eroare lista vida\n; return -1; } else return last->v; } int IntDList::RemoveHead(){ if (count == 0){ cout << Eroare lista vida\n; return -1; } else { int v = first->v; IntDListNode* p = first; first = first->next; if (first) first->prev = 0; delete p;

Soluii count--; if (count == 0){ last = 0; } return v;

221

} } int IntDList::RemoveTail(){ if (count == 0){ cout << Eroare lista vida\n; return -1; } else { int v = last->v; IntDListNode* p = last; last = last->prev; if (last) last->next = 0; delete p; count--; if (count == 0){ first = 0; } return v; } }

E3.6

Funciile declarate ale clasei IntTree sunt:


void IntTree::preorder1(IntTreeNode *root){ if(!root) return; if(root->d) cout << root->d <<" "; preorder1(root->left); preorder1(root->right);
}

void IntTree::preorder(){ preorder1(root);


}

void IntTree::postorder1(IntTreeNode *root){ if(!root) return; postorder1(root->left); postorder1(root->right); if(root->d) cout << root->d <<" ";
}

void IntTree::postorder(){ postorder1(root); } void IntTree::delTree1(IntTreeNode *root){ if(!root) return; delTree1(root->left); delTree1(root->right); delete root; }

222 IntTree::~IntTree(){ delTree1(root); }

Elemente de Programare Orientat pe Obiecte

E3.7

Funciile callback pentru clasa info sunt:


int comp(Pdate i1, Pdate i2){ if( ((info *)i1)->get() < ((info *)i2)->get()) return -1; else if(((info *)i1)->get() == ((info *)i2)->get()) return 0; else return 1; } void execute(Pdate x) { cout << ((info *)i)->get()); }

Funciile clasei PointTree se definesc astfel:


PointNode* PointTree::insert1(PointNode *root, PointNode *r, Pdate data){ if(!r) { r = new PointNode(data); count++; if(!root) return r; if(f(data, root->d) < 0) root->left = r; else root->right = r; return r;
}

if (f(data, r->d) == 0) return 0; if(f(data, r->d) < 0) return insert1(r, r->left, data); else return insert1(r, r->right, data);
}

void PointTree::insert(Pdate data) { if(!root) root = insert1(root, root, data); else insert1(root, root, data);
}

void PointTree::inorder1(PointNode *root){ if(!root) return; inorder1(root->left); if(root->d) g(root->d); inorder1(root->right);


}

void PointTree::inorder(){ inorder1(root);


}

void PointTree::preorder1(PointNode *root){ if(!root) return; if(root->d) g(root->d); preorder1(root->left); preorder1(root->right);


}

Soluii void PointTree::preorder(){ preorder1(root); } void PointTree::postorder1(PointNode *root){ if(!root) return; postorder1(root->left); postorder1(root->right); if(root->d) g(root->d); } void PointTree::postorder(){ postorder1(root); } void PointTree::delTree1(PointNode *root){ if(!root) return; delTree1(root->left); delTree1(root->right); delete root; } PointTree::~PointTree(){ delTree1(root); }

223

Funcia de creare a unui arbore binar ordonat folosind clasa PointTree i clasa info este urmtoarea:
void fp1(){ int n = 10; PointTree tree(comp, execute); for(int i=0;i<n;i++){ info *in = new info(rand()%100); tree.insert(in); } cout <<"Numar noduri: " << tree.getcount()<< endl; tree.inorder(); cout << endl; tree.preorder(); cout << endl; tree.postorder(); cout << endl; }

La execuia acestei funcii se creeaz un arbore binar cu 10 noduri; numerele din aceste noduri sunt afiate n ordine cresctoare (prin funcia inorder()), apoi n preordine i n postordine. Aceast reprezentare a unui arbore binar sortat, chiar dac permite generalizarea tipului de informaie din nodul arborelui prin utilizarea unui pointer generic, este destul de incomod (necesit conversii explicite de la pointerul la tipul de date dorit la pointer generic i invers) i este nesigur tocmai datorit faptului c prin conversiile explicite se ocolete mecanismul de verificare a tipului datelor specific limbajelor orientate pe obiecte. Mai mult, sunt necesare funcii callback pentru definirea relaiei de preceden sau a prelucrrilor necesare pentru fiecare tip de date.

224

Elemente de Programare Orientat pe Obiecte

E3.14 Funciile membre ale clasei NTree se definesc astfel:


NTree::NTree(char *n){ first = link = NULL; parent = NULL; int size = strlen(n)+1; label = new char[size]; strcpy(label,n); } void NTree::AddChild(NTree *pNode){ if (first == NULL){ first = pNode; } else{ pNode->link = first; first = pNode; } pNode->parent = this;
}

void NTree::PostOrder(){ if(first){ NTree *current = first; NTree *next = current; while (next){ current = next; next = next->link; current->PostOrder();
} }

if (label) cout << "Nod: " << label << endl; else cout << "Nod: " << endl;
}

void NTree::RemoveChild() { if (first){ NTree* current = first; first = first->link; current->RemoveAll(); } else cout << "Nu are fii\n"; } void NTree::RemoveAll() { if(first) { NTree *current = first; NTree *next = first; while (next){ current = next; next = next->link; current->RemoveAll(); delete current->label; } }
}

Soluii NTree::~NTree(){ RemoveAll(); if (label){ delete label; label = NULL; } }

225

Mesajele afiate la consol la execuia funciei fnt() sunt urmtoarele:


Traversare preordine Nod: 12 Nod: 11 Nod: 1 Nod: 2 Nod: 3 Nod: 0 Traversare preordine Nod: 2 Nod: 3 Nod: 0

4 Suprancrcarea operatorilor
E4.1 Clasa Date se definete astfel:
class Date{ int day; int month; int year; public: Date(int d=5, int m=7,int y=1999){ year = y; month = m; day = d;
}

}; ostream& operator <<(ostream& stream, Date& d){ stream << " Ziua: " << d.day << " Luna: " << d.month << " Anul: " << d.year << endl; return stream; }

Date operator++(); friend ostream& operator <<(ostream& stream, Date& d); int& operator[](int i);

E4.2

Funcia operator++() a clasei Date se definete astfel:


Date Date::operator++(){ if (day == 28 && month ==2){ month = 3;

226 day = 1; return *this;

Elemente de Programare Orientat pe Obiecte

} if (day==31 && month == 12){ year++; month = 1; day = 1; return *this; } if (day < 30){ day++; return *this;
}

if (day==31 && (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10)){ day = 1; month++; return *this; if (day==30 && (month == 4 || month == 6 || month == 9 || month == 11 )){ day = 1; month++; return *this;

return *this;

E4.3

Funcia operator []() a clasei Date este:


int& Date::operator[](int i){ if (i<0 || i>2) cout << Eroare depasire indice\n; if (i==0) return day; else if (i==1) return month; else return year; }

Atribuirea unei valori folosind operatorul de indexare se face astfel:


date d1(2, 5, 1999) date d2(5, 7, 1999) d2[1] = d1[1]; cout << d2[1] << endl;

// afiseaz 2

E4.5 Eroarea de execuie apare la construcia obiectului, datorit faptului c pointerul pasat ca argument indic un tablou de caractere, nu un ir, deci lipsete caracterul 0 la sfritul irului necesar n constructorul de iniializare. Pentru evitarea erorii se definete constructorul:

Soluii String::String(const char* p, int n){ cout << "Constructor init\n"; size = n+1; str = new char[n+1]; for (int i=0;i<n;i++) str[i] = p[i]; str[n] = 0; }

227

care se apeleaz n funcia f3() astfel:


void f3(){ char v[4] = {65, 66, 67, 68}; String sir(v, sizeof(v)/sizeof(char)); cout << sir << endl; }

E4.7 Funcia operator+() a clasei String care concateneaz dou iruri i o funcie fs() de verificare a funcionrii acestui operator sunt:
String operator+(const String& s1, const String& s2){ String temp; temp.size = s1.size+s2.size-1; temp.str=new char[temp.size]; strcpy(temp.str, s1.str); strcat(temp.str, s2.str); return temp; } void fs(){ String string1="ABCDEF"; String string2="GHIJ"; String string3 = string1+string2; cout << string3; //afiseaza ABCDEFGHIJ }

E4.12 Funciile operator[]() i operator <<() ale clasei IntArray sunt:


int& String::operator[](int i){return p[i];} ostream& operator << (ostream& stream, IntArray& r){ int size = r.size; for (int i = 0;i<size;i++) stream << r.p[i] <<" "; stream << endl; return stream; }

La execuia funciei fia2() se copiaz ultima jumtate a irului de caractere peste prima jumtate. Mesajele afiate la consol sunt:
0 1 2 3 4 5 6 7 7 6 5 4 4 5 6 7

228

Elemente de Programare Orientat pe Obiecte

E4.13 Funciile operator << ale claselor IntSList i IntDList sunt:


ostream& operator <<(ostream& stream, IntSList& list){ IntDListNode* p = list.first; while (p){ stream << p->v <<" "; p = p->link; } stream << endl; return stream; } ostream& operator <<(ostream& stream, IntDList& list){ IntDListNode* p = list.first; while (p){ stream << p->v <<" "; p = p->next; } stream << endl; return stream; }

5 Clase derivate. Moteniri


E5.1 Clasa CPoint se poate defini astfel:
class CPoint { double *coord; int dim; public: CPoint(){ cout << "Constructor implicit\n"; dim = 0; coord = NULL; } CPoint(int d){ cout <<"Constructor initializare cu 1 arg\n"; dim = d; coord = new double[dim]; } CPoint(int d, const double *p); CPoint(const CPoint &r); virtual ~CPoint(); void Compare(const CPoint& p); friend ostream& operator <<(ostream& stream, const CPoint& point); void operator <(const CPoint& p); double& operator[](int i); }; CPoint::CPoint(int d, const double *p){ cout <<"Constructor initializare cu 2 arg\n"; dim = d;

Soluii coord = new double[dim]; for (int i=0;i<dim;i++) coord[i] = p[i];

229

CPoint::CPoint(const CPoint &r){ cout << "Constructor copiere\n"; dim = r.dim; coord = new double[dim]; for (int i=0;i<dim;i++) coord[i] = r.coord[i];
}

Point::~CPoint(){ cout << "Destructor CPoint\n"; if (coord){ delete coord; coord = NULL;
} }

void CPoint::Compare(const CPoint& p){ if (dim != p.dim) cout<<"Punctele nu sunt in acelasi spatiu\n"; else { for (int i=0; i<dim;i++) if (coord[i] != p.coord[i]){ cout<<"Punct nu sunt identice\n"; break;
}

if (i>=dim) cout <<"Punctele sunt identice\n";


} }

Se poate nlocui funcia Compare() cu o funcie operator de comparaie astfel:


void CPoint::operator<(const CPoint& p){ if (dim != p.dim) cout<<"Punctele nu sunt in acelasi spatiu\n"; else { for (int i=0;i<dim;i++) if (coord[i] != p.coord[i]){ cout<<"Puncte neidentice\n"; break; } if (i>=dim) cout <<"Puncte identice\n"; } }

Declaraia acestei funcii trebuie s fie adugat n declaraia clasei CPoint. Funcia fcp1() este urmtoarea:
void fcp1(){ double coord[] = {4,5,6,1};

230

Elemente de Programare Orientat pe Obiecte CPoint p13 = CPoint(3, coord); cout << p13; CPoint p14 = CPoint(4, coord); cout << p14; p13.Compare(p14); p13 < p14;

La execuia ei se afieaz urmtoarele mesaje:


Constructor initializare 2 arg Dimensiune: 3 Coordonate: 4 5 6 Constructor initializare 2 arg Dimensiune: 4 Coordonate: 4 5 6 1 Punctele nu sunt in acelasi spatiu Punctele nu sunt in acelasi spatiu Destructor CPoint Destructor CPoint

E5.2

Funcia operator de indexare pentru clasa CPoint este urmtoarea:


double& CPoint::operator[](int i){ if ((i >= 0) && (i < dim)) return coord[i]; else { cout <<"Eroare de indice \n"; return coord[0];
} }

Declaraia acestei funcii trebuie adugat n clasa CPoint. Se pot citi i modifica coordonatele unui punct folosind funcia operator de indexare astfel:
void fcp2(){ double coord[] = {4,5,6,1}; CPoint p14 = CPoint(4, coord); cout << p14; cout << p14[0] << p14[1] << p14[2] << p14[3] << endl; p14[0] = 0; cout << p14;
}

E5.3

Clasele derivate pentru puncte n spaiile 2d i 3d sunt:


class CPoint2d : public CPoint{ public: CPoint2d():CPoint(2){} CPoint2d(double x, double y):CPoint(2){ cout << "Constructor 2d" << endl; operator[](0) = x; operator[](1) = y;
}

Soluii CPoint2d(double *p):CPoint(2,p){} ~CPoint2d(){ cout << "Destructor 2d" << endl; } void Display (){ cout << "Display 2d "; cout << *this; }

231

}; class CPoint3d:public CPoint{ public: CPoint3d():CPoint(3){} CPoint3d(double x, double y, double z):CPoint(3){ cout << "Constructor 3d" << endl; (*this)[0] = x; (*this)[1] = y; (*this)[2] = z; } CPoint3d(double *p):CPoint(3,p){} ~CPoint3d(){ cout << "Destructor 3d" << endl; } void Display(){ cout << "Display 3d "; cout << *this; } };

n constructorii claselor derivate s-a folosit funcia operator de indexare a clasei de baz, apelat n dou modaliti. La compilarea funciei fpd() se obine de dou ori mesajul de eroare 'Display' : is not a member of 'CPoint', deoarece se apeleaz cu pointer de tipul CPoint* funcia Display() care nu este definit n acast clas. Soluia corect este declararea funciei: virtual void Display(){} n clasa CPoint. De asemenea destructorul clasei CPoint trebuie s fie declarat virtual. Mesajele afiate la execuia funciei fdp() dup aceast completare sunt:
Constructor initializare cu 1 arg Constructor 2d Display 2d Dimensiune: 2 Coordonate 4 5 Destructor 2d Destructor CPoint Constructor initializare cu 1 arg Constructor 3d Display 3d Dimensiune: 3 Coordonate 4 5 1 Destructor 3d Destructor CPoint

232

Elemente de Programare Orientat pe Obiecte

Aceste mesaje arat modul de construire a obiectelor, apelul funciei Display() redefinite n fiecare clas derivat i tergerea complet i corect a obiectelor create. E5.6 Funciile membre ale claselor de baz DListNode i DList se definesc astfel:
DList::~DList(){ DListNode* p = first; while (p){ DListNode* current = p; p = p->next; delete current; } } void DList::AddHead(DListNode* elem){ if (count == 0){ first = elem; last = elem; } else{ first->prev = elem; elem->next = first; first = elem; } count++; } void DList::AddTail(DListNode* elem){ if (count==0){ first = elem; last = elem; } else{ last->next = elem; elem->prev = last; last = elem;
}

count++;
}

DListNode* DList::GetHead() {return first;} DListNode* DList::GetTail() {return last;} DListNode* DList::RemoveHead(){ if (count == 0)return 0; else { DListNode* v = first; first = first->next; if (first) first->prev = 0; count--; if (count == 0)last = 0; return v; } }

Soluii DListNode* DList::RemoveTail(){ if (count == 0)return 0; else { DListNode* v = last; last = last->prev; if (last)last->next = 0; count--; if (count == 0)first = 0; return v; } }

233

Funciile membre ale claselor derivate IDListNode i IDList care reprezint o list dublu nlnuit de numere ntregi se definesc astfel:
void IDList::AddHead(int x){ IDListNode* p = new IDListNode(x); DList::AddHead(p); } void IDList::AddTail(int x){ IDListNode* p = new IDListNode(x); DList::AddTail(p); } IDListNode* IDList::GetHead(){ return (IDListNode*) DList::GetHead(); } IDListNode* IDList::GetTail(){ return (IDListNode*) DList::GetTail(); } int IDList::RemoveHead(){ IDListNode* p = (IDListNode*)DList::RemoveHead(); if (p){ int v = p->GetV(); delete p; return v; } else return 0; } int IDList::RemoveTail(){ IDListNode* p = (IDListNode*)DList::RemoveTail(); if (p){ int v = p->GetV(); delete p; return v; } else return 0;

} ostream& operator <<(ostream& stream, IDList& list){ IDListNode* p = list.GetHead(); while (p){ stream << p->GetV()<<" ";

234

Elemente de Programare Orientat pe Obiecte p = (IDListNode*)p->GetNext(); } stream << endl; return stream;

Pentru verificarea funcionrii listei definite prin intermediul acestor clase derivate se reia funcia fdlist2(), n care se modific tipul listelor create din IntDList n IDList. La execuia funciei astfel modificate (fd2()) se obin aceleai mesaje.
void fd2(){ cout << "Functionare ca stiva a listei\n"; IDList list2; list2.AddHead(1); // push 1 list2.AddHead(2); // push 2 list2.AddHead(3); // push 3 cout << list2; // afiseaza 3 2 1 cout << list2.RemoveHead(); // pop, afiseaza 3 cout << list2.RemoveHead(); // pop, afiseaza 2 cout << list2.RemoveHead(); // pop, afiseaza 1 cout << "Functionarea ca o coada a listei\n"; IDList list3; list3.AddTail(1); // inserare 1 list3.AddTail(2); // inserare 2 list3.AddTail(3); // inserare 3 cout << list3; // afiseaza 1 2 3 cout << list3.RemoveHead(); //extragere, afiseaza 1 cout << list3.RemoveHead(); //extragere, afiseaza 2 cout << list3.RemoveHead(); //extragere, afiseaza 3

E5.10 Funciile necesare pentru suprancrcarea operatorului de indexare al clasei IDList sunt: funcia DListNode* operator[](int x) n clasa de baz DList i funcia int& operator[](int x) n clasa derivat IDList. Aceste funcii se definesc n felul urmtor:
DListNode* DList::operator[](int x){ DListNode* p = first; for (int i=0;i<x;i++) p = p->next; return p; } int& IDList::operator[](int x){ IDListNode* p = (IDListNode*)DList::operator[](x); return p->GetV(); }

Dup definirea acestor funcii operator, elementele listei pot fi accesate prin indexare att pentru citire ct i pentru modificare, deoarece funcia operator []

Soluii

235

returneaz o referin la valoarea coninut n nodul indexat al listei. Funcia fd3() se execut corect. E5.13 Funcia operator de indexare a clasei ObArray se definete astfel:
Object* ObArray::operator[](int i) { return p[i];}

Ea poate fi utilizat pentru suprancrcarea operatorului de indexare n orice clas derivat din clasa ObArray. De exemplu, pentru clasa PointArray:
Point* PointArray::operator[](int i){ return (Point*)ObArray::operator[](i); }

E5.14 Se modific clasa Complex definit n seciunile precedente astfel nct s fie derivat din clasa de baz Object:
class Complex : public Object{ double x,y; public: Complex(double xx, double yy) {x=xx; y=yy; } Complex(double v){ x = v; y = v;} ~Complex() {}; //.. };

Un vector de numere complexe se poate defini astfel:


class ComplexArray : public ObArray { public: ComplexArray() {}; ~ComplexArray(); Complex* GetAt(int i) { return (Complex*)ObArray::GetAt(i); } Complex* operator[](int i){ return (Complex*)ObArray::operator[](i); } };

6 Sistemul de I/O din C++


E6.5 Dac au fost introduse mesaje de identificare n funciile constructor, destructor i funcii operator ale clasei String, atunci la execuia funciei fs(),se afieaz urmtoarele mesaje:
Constructor implicit ABCEDFGH Constructor initializare Operator =

236 Constructor copiere Destructor Destructor Destructor

Elemente de Programare Orientat pe Obiecte

unde ABCDEFGH este irul de caractere introdus de la consol. Funcia fs() i funcia operator >> a clasei String sunt urmtoarele:
void fs(){ String string1; cin >> string1; } istream& operator >>(istream &stream, String &r){ char buf[256]; cin.get(buf,256); r = buf; return stream; }

Constructorul implicit este apelat la crearea irului string1. La execuia instruciunii r = buf; din funcia operator >> se creaz un obiect temporar de tipul String prin apelul unui constructor de iniializare prin care bufferul de caractere este convertit n obiect de tip String; valoarea acestui obiect este asignat referinei r prin funcia operator de asignare. La returnare din funcia operator de asignare se creaz un obiect temporar prin apelul constructorului de copiere. Cele dou obiecte temporare sunt distruse dup revenirea n funcia fs(). La terminarea funciei fs() este distrus i obiectul string1 creat n aceast funcie. Dac se suprim funcia operator=() a clasei String, se execut o copiere membru cu membru a obiectului temporar de tip String n referina r, folosind constructorul de copiere implicit generat de compilator. Aceast operaie produce eroare de execuie datorit faptului c n dou obiecte String pointerii la vectorul de caractere indic aceeai zon de memorie. La distrugerea obiectelor se apelez operatorul delete de dou ori pentru accei adres de memorie, ceea ce produce eroare de execuie. E6.10 Secvena se apeluri a funciilor iteratorului de clas SetIter a unei mulimi de numere ntregi reprezentat printr-un vector ordonat (clasa IntSet) este: SetIter iter(&set); // iterator pt. multimea set while(iter.inside()) cout << iter.next() << " "; cout << endl; Aceast secven se poate nlocui cu instruciunea cout << iter; dac se definete funcia friend operator << a clasei SetIter astfel:
ostream& operator<<(ostream& stream, SetIter& iter){ iter.start(); while(iter.inside())

Soluii stream << iter.next() << " stream << endl; return stream; ";

237

7 Clase i funcii template


E7.4 Se creeaz un vector de patru obiecte din clasa String, svect[4]. La crearea acestui vector este apelat constructorul implicit al clasei String, deci obiectele trebuie s fie setate la valorile dorite ale irurilor de caractere, folosind funcia operator de asignare. Sortarea se execut prin apelul funciei template sort().
void ft3(){ String svect[4]; svect[0] = "BCD"; svect[1] = "ABC"; svect[2] = "GHFJ"; svect[3] = "MNG"; sort(svect,4); for (int i=0;i<4;i++) cout << svect[i]<< endl;

E7.5 Eroarea de compilare din instruciunea for (; iter; ++iter) apare datorit faptului c nu se poate folosi obiectul iter de clas MapIter ntr-o expresie condiional dac nu exist un operator de conversie la un tip de date care s poat fi comparat cu zero. n locul operatorului de conversie operator void*() se poate folosi un operator de conversie la valoare de tip ntreg astfel:
operator int() { return (int)node; }

n acest caz, ultimul cmp afiat n fiecare linie este valoarea pointerului node convertit n ntreg i afiat zecimal. E7.6 Destructorii claselor MapNode i Map se implementeaz astfel:
template<class K, class V> MapNode<K,V>::~MapNode(){ if (right){ K tmp = right->key; cout << "delete node "<< right->key << endl; delete right; cout << "node "<< tmp << "deleted" << endl; } }

238

Elemente de Programare Orientat pe Obiecte template<class K, class V> Map<K,V>::~Map(){ K tmp = head->key; cout << "delete node " << head->key<< endl; delete head; cout << "node " << tmp << "deleted" << endl; }

La execuia funciei fm2() se creeaz un vector asociativ reprezentat printr-o list nlnuit compus din trei elemente, cu cheile word1, word2, word3. La terminarea funciei fm2() se apeleaz destructorul obiectului table de tip Map, care afieaz un mesaj la consol i apeleaz operatorul delete pentru primul element din list (indicat de pointerul head). Operatorul delete pentru obiectul de clas MapNode apeleaz destructorul clasei. Acest destructor afieaz un mesaj la consol i apeleaz operatorul delete pentru elementul urmtor din list. n acest fel se terge recursiv ntreaga list nlnuit. Mesajele afiate la consol la execuia funciei f3() sunt urmtoarele:
word1 5 word2 8 word3 6 delete node word1 delete node word2 delete node word3 node word3 deleted node word2 deleted node word1 deleted

Elmentele din list se terg ncepnd cu ultimul nod n list, pn la primul. E7.7 Pentru a se folosi acelai iterator iter pentru o nou afiare a listei nlnuite este necesar repoziionarea iteratorului pe primul element al listei. Pentru aceasta n clasa MapIter se adaug funcia:
void first(){ node = map->head; }

n funcia fm2(), dup prima afiare a coninutului listei, se execut poziionarea iteratorului pe primul element din list prin instruciunea: iter.first(); dup care se poate folosi acelai iterator pentru o nou parcurgere a listei. Mesajele afiate la consol se repet pentru cea de-a doua parcurgere la fel ca n prima parcurgere a listei. E7.11 Funcia este urmtoarea:
template <class T> void iocopy(T z, istream& is, ostream& os){

Soluii while (is >> z) os << z << \n;

239

Variabila de tip T este transmis ca argument funciei iocopy() numai pentru a se putea identifica tipul obiectului z care se copiaz. Valoarea acesteia nu este utilizat. E7.14 Clasa template TArray se definete astfel:
#include <iostream.h> template <class E> class TArray { E* p; int count; int size; int grows; public: TArray(){ count = 0; grows = 4; size = grows; p = new E[grows]; } TArray(TArray &r); TArray(int* v, int n); ~TArray() { delete p;} int Getcount() const {return count;} int Add(E x); E& GetAt(int i) const{ if (i >= 0 && i < count) return p[i]; else { cout << "Eroare depasire indice\n"; return p[0]; } } int InsertAt(int i, E x); int RemoveAt(int i); E& operator[](int i){return p[i];} friend ostream& operator << (ostream& stream, TArray<E>& r); };

Toate funciile clasei se pot dezvolta prin analogie cu clasa IntArray. O parte dintre acestea sunt prezentate mai jos:
template <class E> int TArray<E>::Add(E x){ if (count < size) p[count++] = x; else { E *p1 = p; p = new E[count + grows]; for (int i=0; i<count;i++)

240 p[i] = p1[i]; delete p1; p[count++] = x;

Elemente de Programare Orientat pe Obiecte

} template <class E>ostream& operator << (ostream& stream, TArray<E>& r){ int count = r.count; for (int i =0;i<count;i++) stream << r.p[i] <<" "; stream << endl; return stream; } template <class E> TArray<E>::TArray<E>(E* v, int n){ count = n; p = new E[count]; for (int i = 0; i < count; i++) p[i] = v[i]; } template <class E> int TArray<E>::RemoveAt(int i){ if (i < 0 || i >=count) return 0; else { for(int j=i; j < count-1;j++) p[j]=p[j+1]; count--; return 1; } }

} return count-1;

Utilizarea clasei template TArray pentru vectori de numere ntregi, TArray<int> sau pentru vectori de numere complexe TArray<Complex> arat astfel:
void fit1(){ int v[] = {0,1,2,3,4,5,6,7}; int size = sizeof(v)/sizeof (int); TArray<int> iarray(v, size); cout << iarray; // afiseaza 0 1 2 3 4 5 6 7 TArray<Complex> carray; carray.Add(Complex(3.5,8)); carray.Add(Complex(6,9.2)); cout << carray; // afiseaza (3.5,8) (6,9.2)

E7.20 O stiva de numere complexe se poate implementa prin clasa template Tlist astfel:
void ft3(){ TList<Complex> list4;

Soluii list4.AddTail(Complex(1,2));// inserare list4.AddTail(Complex(3,4));// inserare list4.AddTail(Complex(5,6));// inserare cout<< list4.RemoveHead(); //extragere, cout<< list4.RemoveHead(); //extragere, cout<< list4.RemoveHead(); //extragere, cout << endl;
}

241 (1,2) (3,4) (5,6) afiseaza (1,2) afiseaza (3,4) afiseaza (5,6)

8 Tratarea excepiilor
E8.3 Se completeaz clasa IntArray astfel:
class IntArray { //. public: class Range{ int index; public: Range(int i){index = i;} int GetIndex(){return index;} }; int GetAt(int i) const; int& operator[](int i); }; int GetAt(int i) const{ if (i >= 0 && i < size) return p[i]; else{ throw Range(i); return -1; }
}

int& operator[](int i){ if (i >= 0 && i < size) return p[i]; else{ throw Range(i); return p[0]; } }

O funcie care capteaz excepia de depire a indicelui i trateaz aceast excepie este urmtoarea:
void fia3(){ try{ int v[] = {0,1,2,3,4,5,6,7}; int size = sizeof(v)/sizeof (int); IntArray array(v, size); cout << array;

242

Elemente de Programare Orientat pe Obiecte for (int i=0;i<size+3;i++) array[i] = i; cout << array;

} catch(IntArray::Range r){ cout << "Indicele " << r.GetIndex() << " depseste domeniul\n"; } }

La execuia acetei funcii se afiseaz urmtoarele mesaje:


0 1 2 3 4 5 6 7 Indicele 8 depaseste domeniul

Se observ faptul c dup tratarea erorii programul este continuat dup rutina de tratare a excepiei i nu cu instruciunile care urmeaz instruciunii care a provocat excepia. E8.4 Se definete clasa global pentru excepia de extragere a unei date dintr-o list goal astfel:
class Empty{ public: Empty(){}; };

Funciile membre ale clasei IntDList se modific n felul urmtor:


int IntDList::RemoveHead(){ if (count == 0){ throw Empty(); return -1; } else { //.. return v; } } int IntDList::RemoveTail(){ if (count == 0){ throw Empty(); return -1; } else { //.. return v; } }

Captarea i tratarea unei excepii de list vid se poate face n felul urmtor:

Soluii

243

void fdlist3(){ try{ IntDList list2; list2.AddHead(1); // push 1 list2.AddHead(2); // push 2 list2.AddHead(3); // push 3 cout << list2.RemoveHead(); // pop, afiseaza 3 cout << list2.RemoveHead(); // pop, afiseaza 2 cout << list2.RemoveHead(); // pop, afiseaza 1 cout << list2.RemoveHead(); // lanseaza exceptia cout << list2.RemoveHead(); } catch(Empty){ cout << "\nExceptie lista goala\n"; } cout << Dupa rutina de tratare a excepiei\n; }

La execuia acestei funcii, ultima operaie RemoveHead() lanseaz excepia Empty, deoarece lista era deja goal. Rutina de tratare a excepiei afieaz un mesaj la consol, iar execuia se continu dup aceast rutin, nu cu instruciunile care urmeaz aceleia care a lansat excepia. Ca urmare, mesajele afiate la consol sunt:
321 Exceptie lista goala Dupa rutina de tratare a exceptiei

Pentru clasa IntSlist se procedeaz n mod similar. E8.10 Se completeaz clasa Date astfel:
class Date{ //. public: int& operator[](int i); class Range{ int indice; public:Range(int i){indice = i;} int get(){return indice;} }; }; int& Date::operator[](int i){ if (i<0 || i>2) throw Range(i); if (i==0) return day; else if (i==1) return month; else return year; }

244

Elemente de Programare Orientat pe Obiecte

Captarea i tratarea execepiei Range se poate face n felul urmtor:


void fde(){ try{ Date date(3,4,99); cout << date[6]; } catch (Date::Range r){ cout << "Indicele cu valoarea " << r.get()<< " depaseste domeniul\n"; } }

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