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:

valorii

apoi

while (*q! = 0) *p++ = *q++;


*p = 0;

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.

Elemente de Programare Orientat pe Obiecte

214

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

215
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;
}

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.
E2.6

(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 {

Elemente de Programare Orientat pe Obiecte

216

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++;

}
}
return count - 1;

}
}
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;
}
}

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

Elemente de Programare Orientat pe Obiecte

218

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

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

}
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

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

}
}
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;
}

Elemente de Programare Orientat pe Obiecte

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

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

223
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);
}

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.

Elemente de Programare Orientat pe Obiecte

224

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

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

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;
}

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

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

E4.2

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


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

Elemente de Programare Orientat pe Obiecte

226
day = 1;
return *this;

}
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;
}

E4.3

return *this;

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

227
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;
}

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

229

coord = new double[dim];


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

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};

Elemente de Programare Orientat pe Obiecte

230

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

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

};
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

Elemente de Programare Orientat pe Obiecte

232

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

233
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;
}
}

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()<<" ";

Elemente de Programare Orientat pe Obiecte

234

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 =

Elemente de Programare Orientat pe Obiecte

236
Constructor copiere
Destructor
Destructor
Destructor

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

237

stream << iter.next() << "


stream << endl;
return stream;

";

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;
}
}

Elemente de Programare Orientat pe Obiecte

238

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

239

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

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++)

Elemente de Programare Orientat pe Obiecte

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

}
return count-1;

}
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;
}
}

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

241
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;

(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;

Elemente de Programare Orientat pe Obiecte

242

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";
}
}