lab
TRANSCRIPT
Programare 2
Indrumar Laborator
Nota laborator = Prezenta + Activitate laborator + Partial + Examen
Prezenta
Fiecare prezenta duce la obtinerea unui punct (1p). Se pot obtine maximum 12 puncte. In ultima
saptamana se va desfasura examenul de laborator, la care se adauga partialul.
Partial
Prezenta este obligatorie. Acesta are loc in saptamana a 7-a sau a 8-a. Daca partialul nu este
promovat aceasta se poate repeta in ultima saptamana.
Partialul consta din rezolvarea (pe calculator) a unei probleme asemanatoare cu cele propuse si
rezolvate in prima parte a semestrului. Notarea se face cu punctaj intre 0 si 45. Trebuie sa se
obtina minum 23 puncte pentru a considera ca partialul a fost promovat.
Examen
Este obligatoriu. Neprezentarea la examenul de laborator echivaleaza cu nepromovarea
laboratorului si implicit a materiei.
Examenul consta din rezolvarea (pe calculator) a unei probleme asemanatoare cu cele propuse si
rezolvate in timpul semestrului. Notarea se face cu punctaj intre 0 si 50. Trebuie sa se obtina
minum 25 de puncte pentru a considera ca examenul a fost promovat.
Punctaj laborator = (Numar prezente) * 1 + (Punctaj partial) + (Punctaj examen laborator)
Asadar, punctajul maxim poate fi:
Punctaj laborator max = 12 + 45 + 50 = 107
Nota laborator = Punctaj laborator / 10.
Pentru promovarea laboratorului este necesara obtinerea unei punctaj mai mare de 50 de
puncte (corespunzatoare notei 5). Atentie! 45 de puncte nu inseamna nota 5 !
Situatia la laborator este disponibila la adresa http://upm.adrianroman.ro/prog2/note.xls
Laborator 1
Recapitulare Programare 1
Exemplul 1
/********************************
Program: Hello World
Obiectiv: studirea structurii unui program C
********************************/
#include <iostream> //directiva preprocesor
using namespace std; //spune compilatorului ca anumite obiecte se
gasesc in namespace-ul standard
int main () //functia principala
{
cout << "Primul meu program in C! Bravo!\n";
getchar(); //asteapta apararea unei taste. De testat programul si fara
aceasta linie
return 0;
}
Exemplul 2
/********************************
Program: Calcularea mediei
Obiectiv: studirea instructiunii if
********************************
#include <iostream>
using namespace std;
int main ()
{
int n1, n2, n3;
double medie;
cout << "Introduceti 3 note : ";
cin >> n1 >> n2 >> n3;
medie = (n1 + n2 + n3)/3.0;
cout << "Media este : " <<medie <<endl;
if (medie >= 5.0) cout << "Studentul a promovat!";
else cout << "Studentul a picat!";
cin.ignore();
getchar();
return 0;
}
Exemplul 3
/******************************************
Program: Afisarea numerele de la 1 la 100 pe ecran
Obiectiv - instructiunea for
*****************************************/
#include <iostream>
using namespace std;
int main()
{
int number;
for (number =1; number <= 100; number++)
{
cout << number << " "; //afiseaza numarul si un spatiu
}
getchar();
return 0;
}
Exemplul 4
/********************************
Program: Apelare prin referinta
********************************/
#include <iostream>
using namespace std;
void citiredim (int &, int &);
float calc (int, int);
int main()
{
int lungime, latime;
float mp;
citiredim(lungime, latime);
mp = calc (lungime, latime);
cout << "Aria in metrii patrati " << mp;
getchar();
getchar();
return 0;
}
void citiredim (int &lun, int &lat)
{
cout << "Introduceti lungimea: ";
cin >> lun;
cout << "Introduceti latimea: ";
cin >> lat;
}
float calc (int lungime, int latime)
{
float aria;
aria = lungime * latime;
return aria;
}
Exercitiu
Sa se scrie si sa se apeleze o functie care calculeaza divizorii unui numar citit de la
tastatura.
Laborator 2
Spatii de nume
C++ foloseste spatii de nume (namespaces) pentru a organiza functii/clase cu
functionalitati inrudite, folosite in cadrul programelor.
Uneori, o variabilă dintr-un domeniu se suprapune peste o variabilă cu acelaşi nume din
alt domeniu. Suprapunerea numelor poate, uneori, să pună probleme, mai ales atunci când
se folosesc în acelaşi program mai multe biblioteci care utilizează la nivel global
identificatori cu aceleaşi nume. O astfel de situaţie conduce, de regulă, la erori
de compilare.
Limbajul C++ rezolvă această problemă prin spaţiile de nume (namespaces).
Fiecare namespace defineşte un domeniu în care sunt plasaţi identificatorii. Pentru a
folosi un membru al unui namespace, acesta trebuie să fie precedat de numele
namespace-ului:
nume_namespace::membru
Alternativ, se poate folosi declaraţia using înaintea identificatorului care este folosit.
În mod obişnuit, declaraţiile using sunt plasate la începutul fişierului în care sunt folosiţi
membrii namespace-ului. De exemplu, instrucţiunea
using namespace nume_namespace;
la începutul unui fişier sursă specifică faptul că membrii lui nume_namespace
pot fi folosiţi în fişier fără a preceda fiecare identificator cu numele namespace-ului şi
operatorul domeniu ::.
Instructiunea using namespace std; este un spatiu de nume.
Daca nu am fi folosit spatiul de nume
using namespace std; , instructiunile din corpul functiei main
ar fi trebuit scrise:
std::cout <<" Primul program CLR console application \n\nFelicitari!!";
std::cin.get();
return 0;
Instrucţiunea
using namespace std;
informează compilatorul că va fi folosit namespace-ul std. Întreg conţinutul fişierului
header iostream este definit ca parte a lui std. Această declaraţie presupune că
unii membri ai lui std vor fi folosiţi în mod frecvent în program. Programatorul are,
astfel, posibilitatea, să acceseze toţi membrii acestui namespace şi să scrie
instrucţiuni într-o variantă mai concisă:
cout << "d= " << d;
decât scriind
std::cout << "d= " << d;
Fără instrucţiunea de declarare a lui std, fiecare cout şi endl ar trebui să fie
precedat de std::.
Exemplul 2.1
//Namespace0
#include <iostream>
using namespace std;
int i = 10;
int main()
{ double i = 3.4;
cout<<"local i "<<i<<'\n';
cout<<"global i "<<::i<<'\n';
cin.get();
return 0;
}
Exemplul 2.2
//Namespace 1
#include <iostream>
using namespace std;
namespace first
{ int var =5;}
namespace second
{ double var =3.1415; }
int main()
{ cout<< first::var<<'\n';
cout << second::var << '\n';
cin.get();
return 0;
}
Exemplul 2.3
//Namespace 2
#include <iostream>
using namespace std;
namespace first
{ int x =5;
int y = 10;
}
namespace second
{ double x = 3.1415;
double y = 2.7183;
}
int main()
{ using first::x;
using first::y;
cout <<x<<'\n';
cout << y<<'\n';
cout<< second::x <<'\n';
cout<< second::y<<'\n';
cin.get();
return 0;
}
Exemplul 2.4
//Namespace 3
#include <iostream>
using namespace std;
namespace first
{ int x =5;
int y = 10;
}
namespace second
{ double x = 3.1415;
double y = 2.7183;
}
int main()
{ using namespace first;
cout <<x<<'\n';
cout << y<<'\n';
cout<< second::x <<'\n';
cout<< second::y<<'\n';
cin.get();
return 0;
}
Exercitiu
Laborator 3
Exceptii C++
Exceptiile sunt erori produse la executia programului cind controlul este preluat de codul
de tratarea acestor exceptii, daca exista. In acest fel se poate preveni opriri bruste ale
programelor la aparitia acestor erori, putindu-se lua masuri de genul eliberat resursele
afisate, atentionat utilizatorul, scris detalii despre eroare in fisiere de log, etc.
Pentru a trata erorile se folosesc blocurile try .. catch.
Exemplul 3.1
#include <iostream>
using namespace std;
int main () {
try
{
throw 20;
}
catch (int e)
{
cout << "Exceptie aparuta cu nr. " << e << endl;
}
return 0;
}
Tot codul de executat in care este posibil sa apara exceptiile e inclus in try {}. In acest
exemplu este generata o exceptie cu numarul 20, care va fi tratata mai jos. Dupa codul
din try{} urmeaza una sau mai multe instructiuni catch() pentru diverse tipuri de erori. In
acest caz este prinsa doar eroarea de tip intreg.
De exemplu puteti avea asa:
try {
// codul e aici
}
catch (int param) { cout << "Exceptie intreaga"; }
catch (char param) { cout << "Exceptie de tip caracter"; }
catch (...) { cout << "Orice alte exceptii"; }
Dupa cum se vede se poate specifica prin '...' orice tip de exceptii netratata anterior.
La aparitia unei erori sint analizate toate instructiunile catch() iar daca una se potriveste
tipului de eroare este executata, dupa care programul continua cu instructiilile de dupa
catch().
Exemplul 3.2
#include <iostream>
using namespace std;
void Ehandler(int);
int main()
{ cout<<"start\n";
Ehandler(1);
Ehandler(2);
Ehandler(0);
Ehandler(3);
cout<<"sfarsit\n";
cin.get();
return 0;
}
void Ehandler(int test)
{ try
{ if(test)
throw test;
}
catch(int i)
{ cout<<"am prins exceptia cu valoarea "<<i<<'\n';
}
}
Exemplul 3.3
#include <iostream>
using namespace std;
void Xhandler(int) throw(int,char,double);
int main()
{ cout<<"start\n";
for(int k =0;k<3;k++)
{ try
{ Xhandler(k);
}
catch( int i)
{ cout <<"am prins int\n";
}
catch( char i)
{ cout <<"am prins char\n";
}
catch( double i)
{ cout <<"am prins double\n";
}
}
cout<<"sfarsit\n";
cin.get();
return 0;
}
void Xhandler(int test) throw (int,char,double)
{
if(test == 0) throw test;
if(test == 1) throw 'a';
if(test == 2) throw 3.14;
}
Exercitii
1. Sa se scrie un program care sa citeasca 10 valori de tip intreg de la tastatura. Daca
se introduce un numar negativ sa se arunce o exceptie.
2. Sa se scrie un program care sa citeasca 10 valori de la tastatura. Daca se introduce
o valoare care nu reprezinta un numar sa se arunce o exceptie.
Laborator 4
Alocare dinamica
Se pune problema utilizării, într-un program, a unui număr foarte mare de date de tip double, de
exemplu, care trebuie prelucrate rapid şi care, în consecinţă, trebuie să se afle în memorie în
momentul rulării, chiar dacă iniţial se află într-un fişier pe disc. Este evident că în acest caz vom
folosi tablouri cu dimensiuni adecvate volumului de date.
Utilizarea tablourilor este foarte simplă dar, dacă volumul de date prelucrat este cu adevărat
mare, ne vom lovi de un neajuns major: nu pot fi declarate tablouri de dimensiuni variabile:
alocarea unui tablou are un caracter static, este făcută la compilarea programului, prin urmare
numărul de elemente trebuie desemnat printr-o constantă. Programatorul trebuie să estimeze o
dimensiune maximă, acoperitoare pentru toate situaţiile în care va fi folosit respectivul tablou, şi
să-l declare în consecinţă. Apare astfel o risipă inerentă de spaţiu de memorie.
De exemplu, dacă declarăm tabloul: double mat[100][100];
şi la rulare îl încărcăm cu o matrice de 10 x 10 elemente, folosim numai 1% din spaţiul
rezervat prin program! Risipa este evidentă, iar remediul constă în alocarea dinamică a
memoriei, adică alocarea în momentul rulării programului.
In locul lor tablourilor declarare static vom folosi variabile de tip pointer pentru a reţine adresele
locaţiilor de memorie obţinute de la sistemul de calcul prin operaţii de alocare. In cazul alocării
tablourilor se garantează că elementele succesive au locaţii succesive şi, în consecinţă,
parcurgerea tablourilor alocate dinamic poate fi facută cu operatorul de indexare, ca în cazul
alocării statice, utilizând identitatea dintre expresia p[i] şi *(p+i).
Important !
Diferenţa esenţială dintre pointeri şi tablouri: instrucţiunea double* p
declară o variabilă de tip pointer către double, în timp ce double tab[10];
declară un tablou al cărui nume, tab, este o constantă de tip pointer către double, cu valoarea:
adresa primului element al tabloului, atribuirea p=tab;
este corectă, în locaţia de memorie numită p se scrie adresa primului element din tabloul
tab, în timp ce atribuirea tab=p;
nu este permisă: nu există o locaţie de memorie numită tab care să conţină o adresă care
să fie modificată. Este clar că, la compilare, apare undeva într-un tabel numele tab urmat
de valoarea sa, adresa primului element al tabloului, şi această informaţie este utilizată la
proiectarea codului, dar programatorul nu are acces la acel tabel. Putem spune, aşadar, că
tab este un pointer static, un pointer utilizat numai de compilator, cu o valoare constantă,
în timp ce p este un pointer dinamic, a cărui valoare poate fi modificată de către programator şi
care, în consecinţă, trebuie iniţializată tot de către programator.
Un pointer poate fi iniţializat corect doar în următoarele trei moduri:
1) cu adresa unei variabile alocate deja de compilator: int i=7;
int* p;
p=&i;
cout<<*p<<endl; //7
sau, 2) printr-o atribuire cu o expresie corectă din aritmetica pointerilor, de exemplu int tab[10]={0,1,2,3,4,5,6,7,8,9};
int* p;
p=tab+5;
cout<<tab[5]<<"=="<<*p<<endl; //5==5
sau, 3) prin alocare dinamică, după cum urmează mai jos.
In limbajul C alocarea dinamică a memoriei poate fi facută numai cu funcţii din biblioteci
specializate în gestionarea memoriei, cele mai utilizate fiind funcţiile malloc(), calloc(), free(),
realloc(), toate din <malloc.h>.
In C++ se utilizeaza operatorii new şi delete care fac parte din integrantă din limbaj. Operatorul
new poate fi utilizat în două moduri: pentru a rezerva spaţiu de memorie pentru un singur obiect,
caz în care new întoarce adresa obiectului alocat, sau pentru a rezerva spaţiu pentru un tablou cu
un numar variabil de obiecte (dar bine precizat, la rulare, în momentul evaluării expresiei), caz în
care new întoarce adresa primului element din tablou.
Spaţiul alocat cu operatorul new (şi numai acesta) poate fi eliberat cu ajutorul operatorului
delete. Pentru a reuşi eliberarea spaţiului, operatorul delete trebuie să primească adresa primului
octet al locaţiei, adică exact valoarea iniţială a pointerului încărcat de new la alocare. La
dealocarea unui tablou se utilizează forma delete [ ].
Exemplul 1, alocarea şi dealocarea unor variabile simple: #include<iostream.h>
int main(void)
{
int *q,*p;
p=new int;
q=new int;
*p=10;
*q=*p+23;
cout<<"p="<<p<<endl;
cout<<"q="<<q<<endl;
cout<<"*q="<<*q<<endl;
delete q;
delete p;
cout<<"q="<<q<<endl;
cout<<"*q="<<*q<<endl;
q=new int;
*q=12;
cout<<"q="<<q<<endl;
cout<<"*q="<<*q<<endl;
return 0;
}
/* REZULTAT
p=0x00430030
q=0x00431740
*q=33
q=0x00431740
*q=-572662307 //GUNOI
q=0x00430030
*q=12
Press any key to continue
Exemplul 2, alocarea şi dealocarea unui tablou de variabile simple: #include<iostream.h>
int* aloca1D(int n)
{
int* p = new int[n];
for(int i=0;i<n;i++){
p[i]=100*(i+1);
}
cout<<endl;
return p;
}
void dealoca1D(int *p){
delete [] p;
}
int main(void)
{
int i,dim=5;
int* vector;
vector=aloca1D(dim);
for(i=0;i<dim;i++)
cout<<vector[i]<<endl;
dealoca1D(vector);
for(i=0;i<dim;i++)
cout<<vector[i]<<endl;
return 0;
}
/* REZULTAT
100
200
300
400
500
-572662307
-572662307
-572662307
-572662307
-572662307
Press any key to continue
*/
Exercitiu
Sa se scrie un program care stocheaza intr-un tablou alocat dinamic divizorii unui intreg citit de
la tastatura si ii afiseaza.
Laborator 5
Alocare unui tablou de tablouri
In programul următor declarăm, cu instrucţiunea:
int (*p)[5];
un pointer catre tipul int[5], adică pointer către tablouri de 5 întregi, pe care îl încărcăm cu
operatorul new cu adresa unui astfel de tablou, primul dintr-o serie de n tablouri succesive,
fiecare cu câte 5 elemente. Definim astfel un tablou 2D cu un număr variabil de linii şi cu un
număr constant, 5, de coloane. Declaraţia funcţiei aloca() este mai complicată: lista parametrilor
formali este formată numai de variabila int n iar rezultatul este de tip int(*)[5], adică pointer
către int[5].
Exemplul 1 #include<iostream>
#include<iomanip>
using namespace std;
int (*aloca(int n))[5]{
int (*p)[5];
p = new int[n][5];
for(int i=0;i<n;i++)
{
for(int j=0;j<5;j++){
p[i][j]=10*i +j;
}
}
return p;
}
void dealoca(int (*p)[5]){
delete [] p;
return;
}
int main(void){
int dim=3;
int (*p)[5];
p=aloca (dim);
for(int i=0;i<3;i++){
for(int j=0;j<5;j++)
cout<<setw(5)<<p[i][j];
cout<<endl;
}
dealoca(p);
getchar();
return 0;
}
/*
REZULTAT:
0 1 2 3 4
10 11 12 13 14
20 21 22 23 24
Press any key to continue
*/
Avem deci un exemplu de alocare a unui tablou 2D de tipul n x 5. Alocarea unui tablou
de tipul 5 x n este mult mai simplă, vezi secvenţa următoare: int i,j, n=7;
int* tab[5];
for(i=0;i<5;i++)
tab[i]= new int[n];
for(i=0;i<5;i++)
for(j=0;j<n;j++)
tab[i][j]=i*j;
Declarăm un tablou, tab, format din 5 pointeri către int şi încărcăm fiecare pointer cu
adresa unui vector de n elemente întregi, alocat dinamic.
Exemplul 2
//CitesteVector
#include <iomanip>
#include <iostream>
using namespace std;
int main()
{ int max = 2;
int *a = new int[max];
int n=0;
cout<<"\n\nDimensiune initiala a tabloului = "<<max<<'\n';
cout<<"\n\nIntroduceti numere intregi,terminati cu ctrl Z\n";
while(cin>>a[n])
{ n++;
if(n >= max)
{ max += 2;
int *temp = new int[max];
for(int i =0;i<n;i++)
temp[i] = a[i];
delete [] a;
a = temp;
}
}
cin.clear();
cout<<"\n\nDimensiune finala a tabloului"<<max<<'\n';
cout<<"\n\nNumerele introduse sunt:\n\n";
for(int i= 0;i<n;i++)
cout<<setw(5)<<a[i];
cout<<"\n\nApasati tasta Enter";
cin.get();
getchar();
return 0;
}
Exercitiu
Sa se scrie un program care stocheaza intr-un tablou alocat dinamic divizorii unui intreg citit de
la tastatura si ii afiseaza.
Laborator 6
Alocare unui tablou 2D cu ambele dimensiuni variabile
#include <iostream>
using namespace std;
double** aloca2D(int nrLin, int nrCol){
// double** double* double
//
// mat -> mat[0] -> mat[0][0]
// mat[0][1]
// mat[0][2]
//
// mat[1] -> mat[1][0]
// mat[1][1]
// mat[1][2]
int i;
double** mat;
mat= new double*[nrLin];
for(i=0;i<nrLin;i++){
mat[i]=new double[nrCol];
}
return mat;
}
void dealoca2D(double** mat,int nrLin, int nrCol){
for(int i=0;i<nrLin;i++)
delete mat[i];
delete mat;
}
void afiseaza(double** mat,int nrLin, int nrCol){
int i,j;
for(i=0;i<nrLin;i++){
for(j=0;j<nrCol;j++) cout<<mat[i][j]<<" ";
cout<<endl;
}
}
void citeste(double** mat,int nrLin, int nrCol){
int i,j;
for(i=0;i<nrLin;i++){
for(j=0;j<nrCol;j++) {
cout<<"m["<<i<<"]["<<j<<"]=";
cin>>mat[i][j];
}
cout<<endl;
}
}
int main(void)
{
int m,n;
double **qmat;
cout<<"dati nr. de linii, m=";cin>>m;
cout<<"dati nr. de coloane, n=";cin>>n;
qmat=aloca2D(m,n);
citeste(qmat,m,n);
afiseaza(qmat,m,n);
dealoca2D(qmat,m,n);
return 0;
}
Observăm că, în funcţia aloca2D(), alocarea se efectuează în două etape: mai întâi alocam un
tablou de pointeri (fiecare va ţinti către o linie a matricii) şi apoi încărcăm fiecare astfel de
pointer cu adresa primului element al unei linii nou alocate. Dacă dorim să lucrăm cu o matrice
de tip n x m de elemente de tip double, avem nevoie de un pointer mat de tip double** care să
reţină adresa primului pointer dintre cei n pointeri de tip
double* (anume pointerii mat[0], mat[1], ... , mat[n-1]),
care la rândul lor trebuie să reţină adresele primelor elemente de pe fiecare linie. Dealocarea se
face în ordine inversă: întâi dealocam liniile, rând pe rând, şi apoi dealocăm coloana de pointeri.
Exercitii
1. Sa se citeasca de la tastatura valorile unei matrici de n * m, unde n si m se citesc de la
tastatura si sa se adune cu matricea unitate. Sa se afiseze rezultatul.
2. Sa se citeasca de la tastatura valorile a 2 matrice si sa se calculeze suma lor.
3. Sa se citeasca de la tastatura valorile a 2 matrice si sa se calculeze produsul lor.
4. Sa se implementeze un program de calcul a sumei elementelor de pe fiecare rând
împartita la valoarea elementului de pe diagonala a unei matrice.
5. Sa se implementeze un program de calcul a rangului unei matrice.
6. Pentru o matrice de m linii si n coloane (dreptunghiulara) sa se afiseze coloanele ce
reprezinta siruri ordonate crescator, si liniile care reprezinta siruri ordonate descrescator.
7. Pentru o matrice An,n, se cauta în toate submatricele formate din liniile si coloanele i…n,
cu i=1,.., n, elementele maxime în valoare absoluta. Acestea vor fi aduse pe pozitiile ai,i.
Determinati apoi daca matricea este dominant-diagonala, adica daca elementul de pe
diagonala principala, în valoare absoluta - |ai,i| - este mai mare decât suma modulelor
vecinilor sai de pe linia i.
8. Sa se calculeze media aritmetica si produsul elementelor cuprinse în zonele superioara si
inferioara, situate între diagonala principala si secundara ale matricei.
Laborator 7
Tipuri Utilizator
Exemplul 1 – Union
//Union
#include <iostream>
int main()
{ union conversie
{ int numar;
char litere[2];
};
conversie data;
data.numar = 65 + 66*256;
cout<<"\nData ca numar zecimal "<<data.numar<<endl;
cout<<"\nData ca numar hexa "<<hex<<data.numar<<endl;
cout<<"\nData ca sir de caractere "<<data.litere[1]
<<" "<<data.litere[0]<<endl;
cout<<"\nApasati tasta ENTER";
cin.get();
return 0;
}
Exemplul 2 - Stiva
//Stiva
#include <iostream>
#include <iomanip>
#include <conio.h>
struct nod
{ int data;
nod *prec,*urm;
};
nod *varf;
void afisez();
void adaug(int);
void sterg();
int main()
{ char optiune;
int n;
varf = NULL;
do
{ clrscr();
cout<<"1 = Pun in stiva\n2 = Scot din stiva\nS = Sfarsit\n";
cout<<"\n\nOptiune : ";
cin>>optiune;
cin.ignore();
cout<<'\n';
switch(optiune)
{case '1':
cout<<"\n\nPun in stiva";
cout<<"\nValoare nod: ";
cin>>n;
cin.ignore();
adaug(n);
afisez();
break;
case '2':
cout<<"\n\nScot din stiva";
sterg();
afisez();
break;
case 'S':
break;
case 's':
optiune &= 0x5f;
break;
default:
cout<<"\n\nOptiune incorecta";
}
cout<<"\n\nApasati tasta ENTER";
cin.get();
}
while ( optiune != 'S');
return 0;
}
void afisez()
{ nod *p;
cout<<"\nStiva este: ";
if (!varf)
cout<<"vida";
else
for(p=varf;p;p=p->prec)
cout<<setw(4)<<p->data;
}
void adaug(int n)
{ nod *p = new nod;
p->data = n;
p->urm = 0;
p->prec = varf;
if(varf)
varf->urm = p;
varf=p;
}
void sterg ()
{ nod *p;
if (!varf)
return;
else
{ p = varf;
varf = varf->prec;
delete p;
}
}
Exercitiu
Sa se scrie un program care sa testeze daca o expresie ce contine paranteze este valida. Se
va folosi stiva.
Exemplu de expresie invalida: (a+b*c
Exemplu de expresie valida: (a+b)*c sau (a+b*c)
Laborator 8
Tipuri Utilizator - Coada
Exemplul 1 – Coada statica
//Coada
#include <iomanip>
#include <conio.h>
const int N=5;
int coada[N],primul=0,nrelem=0;
void adaug(int);
void extrag();
void afisez();
int main()
{ char optiune;
int n;
do
{ clrscr();
cout<<"1 = Adaugare elememt\n2 = Extragere element"
"\n3 = Afisare\nS = Sfarsit\n";
cout<<"\n\nOptiune : ";
cin >> optiune;
cin.ignore();
switch(optiune)
{ case '1':
cout<<"\n\nAdaugare element";
if (nrelem == N)
{cout<<"\n\nCoada este plina";
afisez();
}
else
{ cout<<"\nValoare element: ";
cin>>n;
cin.ignore();
adaug(n);
afisez();
}
break;
case '2':
cout<<"\n\nExtragere element";
extrag();
break;
case '3':
cout<<"\n\nAfisare coada";
afisez();
break;
case 'S':
break;
case 's':
optiune &= 0x5f;
break;
default:
cout<<"\n\nOptiune incorecta";
break;
}
cout<<"\n\nApasati tasta Enter";
cin.get();
}
while ( optiune != 'S');
return 0;
}
void adaug(int n)
{ coada[(primul+nrelem)%N]=n;
nrelem++;
}
void extrag()
{ if(!nrelem)
cout<<"\n\nCoada este vida";
else
{primul = (primul+1)%N;
nrelem--;
afisez();
}
}
void afisez()
{ if(!nrelem)
cout<<"\n\nCoada este vida";
else
{ cout<<"\n\nCoada este:";
for(int i=0;i<nrelem;i++)
cout<<setw(4)<<coada[(primul+i)%N];
}
}
Exemplul 2 – Coada dinamica
//CoadaD
#include <conio.h>
#include <iomanip>
struct nod
{int data;
nod *next;
};
nod *primul,*ultimul;
void afisez();
void adaug(int);
void extrag();
int main()
{ primul = ultimul= 0;
char optiune;
int n;
do
{ clrscr();
cout<<"1 = Adaugare elememt\n2 = Extragere element"
"\n3 = Afisare\nS = Sfarsit\n";
cout<<"\nOptiune : ";
cin>>optiune;
cin.ignore();
cout<<'\n';
switch(optiune)
{ case '1':
cout<<"\n\nAdaugare element";
cout<<"\nValoare element: ";
cin>>n;
cin.ignore();
adaug(n);
afisez();
break;
case '2':
cout<<"\n\nExtragere element";
extrag();
break;
case '3':
cout<<"\n\nAfisare coada";
afisez();
break;
case 'S':
break;
case 's':
optiune &= 0x5f;
break;
default:
cout<<"\n\nOptiune incorecta";
}
cout<<"\n\nApasati tasta ENTER";
cin.get();
}
while ( optiune != 'S');
}
void adaug(int n)
{ nod *p = new nod;
p->data = n;
p->next = 0;
if (! primul)
primul = ultimul = p;
else
{ ultimul->next = p;
ultimul = p;
}
}
void extrag()
{ nod *p;
if(!primul)
cout<<"\n\nCoada este vida";
else
{ p = primul;
primul = primul->next;
delete p;
afisez();
}
}
void afisez()
{ nod *p;
if (!primul)
cout<<"\n\nCoada este vida";
else
{ cout<<"\n\nCoada este:";
for(p=primul;p;p=p->next)
cout<<setw(4)<<p->data;
}
}
Exercitiu
Sa se scrie un program care sa citeasca de la tastatura un set de comenzi care apoi sa fie
introduse intr-o coada si apoi sa fie afisate una cate una.
Exemplu:
Intrare: comanda1 comanda2 comanda3 comanda4 comanda5
Iesire:
comanda1
comanda2
comanda3
comanda4
comanda5
Laborator 9
Tipuri Utilizator - Lista
Exemplul 1
//ListaS
#include <iomanip>
#include <conio>
#include <new>
struct nod
{ int data;
nod *next;
};
nod *primul;
void afisez();
void creare();
void adaugst(int);
void adaugdupa(int);
void sterg(int);
void main()
{ char optiune;
int n;
primul =new nod;
primul->next = 0;
set_new_handler(0);
do
{ clrscr();
cout<<"1 = Creare lista\n2 = Adaugare nod stanga\n3 = Adaugare dupa
nodul n"
"\n4 = Stergere nodul n\n5 = Afisare lista\nS = sfarsit\n";
cout<<"\nOptiune : ";
cin>>optiune;
cin.ignore();
cout<<'\n';
switch(optiune)
{ case '1':
cout<<"Creare lista";
creare();
break;
case '2':
afisez();
cout<<"\n\nAdaugare nod stanga";
cout<<"\nValoare nod: ";
cin>>n;
cin.ignore();
adaugst(n);
break;
case '3':
afisez();
cout<<"\n\nAdaugare dupa nodul";
cout<<"\nValoare nod: ";
cin>>n;
cin.ignore();
adaugdupa(n);
break;
case '4':
afisez();
cout<<"\n\nStergere nod";
cout<<"\nValoare nod: ";
cin>>n;
cin.ignore();
sterg(n);
break;
case '5':
cout<<"Afisare lista";
afisez();
break;
case 'S':
break;
case 's':
optiune &=0x5f;
break;
default:
cout<<"\n\nOptiune incorecta";
}
cout<<"\n\nApasati o tasta\n";
cin.get();
}
while ( optiune != 'S');
}
void creare()
{ int n;
nod *p;
if (primul->next)
{ cout<<"Lista exista";
afisez();
}
else
{ cout<<"\nValoare nod: ";
cin>>n;
cin.ignore();
p = new nod;
p->data = n;
p->next = 0;
primul->next = p;
afisez();
}
}
void afisez()
{ nod *p;
cout<<"\nLista este:";
if (!(primul->next))
cout<<" vida";
else
for(p=primul->next;p;p=p->next)
cout<<setw(5)<<p->data;
}
void adaugst(int n)
{ nod *p;
if (!(primul->next))
{ cout<<"\nLista este vida";
cout<<"Lista trebuie creata";
return;
}
else
{ for(p=primul;p->next && p->next->data != n;p=p->next);
if ( p->next)
cout<<"\nNodul "<<n<<" exista in lista\n";
else
{ p = new nod;
p->data = n;
p->next = primul->next;
primul->next = p;
afisez();
}
}
}
void adaugdupa(int n)
{ nod *p,*q;
int m;
if (!(primul->next))
{ cout<<"\nLista este vida";
cout<<"Lista trebuie creata";
return;
}
else
{ for(p=primul;p->next && p->next->data != n;p=p->next);
if (!( p->next))
{ cout<<"\nNodul "<<n<<" nu exista in lista\n";
return;
}
else
{ cout<<"\nValoare nod care se adauga: ";
cin>>m;
cin.ignore();
for(q=primul;q->next && q->next->data != m;q=q->next);
if ( q->next)
cout<<"\nNodul "<<m<<" exista in lista\n";
else
{ p=p->next;
q = new nod;
q->data = m;
q->next = p->next;
p->next = q;
afisez();
}
}
}
}
void sterg (int n)
{ nod *p,*q;
if (!(primul->next))
{ cout<<"\nLista este vida";
cout<<"Lista trebuie creata";
}
else
{ for(p=primul;p->next && p->next->data != n;p=p->next);
if (!( p->next))
{ cout<<"\nNodul "<<n<<" nu exista in lista\n";
return;
}
else
{ q = p->next;
p->next = p->next->next;
delete q;
afisez();
}
}
}
Exemplul 2
//ListaD
#include <iomanip>
#include <conio.h>
#include <new.h>
struct nod
{int data;
nod *prec,*urm;
};
nod *cap=0,*coada=0,*poz=0;
bool esteGoala()
{ return cap == 0;
}
bool maiSuntElemente()
{ return poz != 0;
}
int urmatorulElement()
{ nod *temp = poz;
poz = poz->urm;
return temp->data;
}
nod* gaseste(int n)
{ if(esteGoala())
return 0;
nod *temp = cap;
while(temp != 0)
{ if(temp->data == n)
return temp;
temp = temp->urm;
}
return 0;
}
void adauga(int n)
{ nod *temp = new nod;
temp->data = n;
temp->prec = 0;
temp->urm =0;
if( cap == 0)
{ cap = temp;
coada = temp;
}
else
{ coada->urm = temp;
temp->prec = coada;
coada = temp;
}
}
void insereaza(int n, int poz)
{ nod *pozNod = gaseste(poz);
if(pozNod == 0)
{ cout<<"\n\nNodul " << poz <<" nu este in lista\n";
return;
}
nod* temp = new nod;
temp->data = n;
temp->urm = pozNod;
temp->prec = pozNod->prec;
if(pozNod == cap)
cap = temp;
else
pozNod->prec->urm = temp;
pozNod->prec = temp;
}
void sterge(int n)
{ nod *stergNod = gaseste(n);
if(stergNod == 0)
{ cout<<"\n\nNodul " <<n<<" nu este in lista\n";
return;
}
if(stergNod == cap)
cap = stergNod->urm;
else
stergNod->prec->urm = stergNod->urm;
if(stergNod == coada)
coada = stergNod->prec;
else
stergNod->urm->prec = stergNod->prec;
delete stergNod;
}
void afiseaza()
{ poz = cap;
while(maiSuntElemente())
cout<<setw(4)<<urmatorulElement();
}
int main()
{char optiune;
int n,m;
do
{clrscr();
cout<<"1 = Adauga nod\n2 = Insereaza nod in fata altui nod"
"\n3 = Stergere nodul n\n4 = Afisare lista\nS = Sfarsit\n";
cout<<"\n\\nOptiune : ";
cin>>optiune;
cin.ignore();
cout<<'\n';
switch(optiune)
{ case '1':
cout<<"\n\nAdauga nod";
cout<<"\nValoare nod: ";
cin>>n;
cin.ignore();
adauga(n);
break;
case '2':
cout<<"\n\nInsereaza nod in fata altui nod";
cout<<"\nValoare nod care s insereaza: ";
cin>>n;
cin.ignore();
cout<<"\nValoare nod in fata caruia se insereaza: ";
cin>>m;
cin.ignore();
insereaza(n,m);
break;
case '3':
cout<<"\n\nStergere nod";
cout<<"\nValoare nod: ";
cin>>n;
cin.ignore();
sterge(n);
break;
case '4':
cout<<"\n\nAfisare lista\n";
afiseaza();
break;
case 'S':
break;
case 's':
optiune &= 0x5f;
break;
default:
cout<<"\n\nOptiune incorecta";
}
cout<<"\n\nApasati tasta ENTER";
cin.get();
}
while ( optiune != 'S');
return 0;
}
Exercitiu
Sa se scrie un program care sa introduca intr-o lista 20 de numere prime mai mari decat
un numar citit de la tastatura si sa se afiseze descrescator.
Exemplu:
Intrare: 6
Iesire: 83, 79, 73, 71, 67, 61, 59, 53, 47, 43, 41, 37, 31, 29, 23, 19, 17, 13, 11, 7
Laborator 10
Programare Orientata Obiect in C++
Tipul class
Sintaxa generală de declarare a unui tip de date class este:
class <nume_clasa> <: lista_clase_baza> {<lista_membri>}
<lista_variabile>;
unde:
nume_clasa este un identificator care desemnează numele tipului clasă
declarat, care trebuie să fie unic în domeniul în care este valabilă declaraţia;
lista_clase_baza este lista claselor din care este derivată clasa declarată (dacă
este cazul);
lista_membri reprezintă secvenţa cu declaraţii de datele membre şi declaraţii
sau definiţii de funcţii membre; datele membre pot fi de orice tip, mai puţin
tipul clasă declarat, dar se admit pointeri către acesta
lista_variabile este lista variabilelor de tip nume_clasa.
Important! Membrii unei clase au implicit atributul de acces private.
#include<iostream.h>
class Punct{
private: // se specifică accesul private la membrii clasei
int x, y; // date membre ale clasei
public: // se specifică acces public la membrii clasei
void init(int initx=0, int inity=0)
// funcţie de iniţializare, funcţie membră cu parametri impliciţi
{x = initx; y = inity; }
int getx(){ return x; } // funcţie care returnează valoarea membrului x
int gety(){ return y; } // funcţie care returnează valoarea membrului y
void move(int dx, int dy);
// funcţie membră cu parametri, definită în afara
// declaraţiei clasei
void afisare() // funcţie de afişare a valorilor membrilor
{ cout<<”\nx=<<x<<”\tz=”<<y”;}
};
void Punct::move(int dx, int dy)
// definirea funcţiei move(), membră a clasei Punct
{
x+=dx;
y+=dy;
}
void main()
{
Punct Punct1; // se declară un obiect de tip Punct, Punct1
int x1, y1;
cout<<"\n Introduceti coordonata x= ";
cin>>x1;
cout<<" Introduceti coordonata y= ";
cin>>y1;
Punct1.init(x1, y1); // iniţializarea obiectului Punct1 cu
valorile x1,
// respectiv y1
cout<<"\n x este = "<<Punct1.getx(); // afişarea coordonatei x
cout<<"\n y este = "<< Punct1.gety(); // afişarea coordonatei y
Punct1.move(10, 20); // modificarea coordonatelor obiectului
Punct1
cout<<"\n x este = "<<Punct1.getx(); // afişarea coordonatei x
cout<<"\n y este = "<< Punct1.gety(); // afişarea coordonatei y
Punct Punct2; // se declară un obiect de tip Punct, Punct2
Punct2.init(); // membrii x şi y preiau valorile implicite ale
// parametrilor, deci x=y=0
Punct2.afisare(); // afişarea caracteristicilor obiectului Punct2
Punct *p_Punct3; // se declară un obiect pointer la Punct
p_Punct3 = &Punct2;
p_Punct3 -> move ( 5, 12);
// apelul funcţiilor membre se face folosind
// operatorul de selecţie “->”
p_Punct3 -> afisare();
}
}
Exercitiu
Sa se modifice clasa de mai sus astfel incat Punct sa aiba 3 coordonate.
Laborator 10
Programare Orientata Obiect in C++
Constructori şi destructori
Unele obiecte necesită alocarea unor variabile dinamice la creare, eventual
atribuirea de valori adecvate datelor înainte de utilizare. Pe de altă parte, eliminarea unui
obiect de acest tip impune eliberarea zonei de memorie alocată dinamic.
Pentru crearea, iniţializarea, copierea şi distrugerea obiectelor, în C++ se folosesc
funcţii membre speciale, numite constructori şi destructori.
Constructorul se apelează automat la crearea fiecărui obiect al clasei, indiferent
dacă este static, automatic sau dinamic (creat cu operatorul new), inclusiv pentru obiecte
temporare.
Destructorul se apelează automat la eliminarea unui obiect, la încheierea timpului
de viaţă sau, în cazul obiectelor dinamice, cu operatorul delete.
Aceste funcţii efectuează operaţiile prealabile utilizării obiectelor create, respectiv
eliminării lor. Alocarea şi eliberarea memoriei necesare datelor membre rămâne în
sarcina compilatorului.
Constructorul este apelat după alocarea memoriei necesare datelor membre ale
obiectului (în faza finală creării obiectului).
Destructorul este apelat înaintea eliberării memoriei asociate datelor
membre ale obiectului (în faza iniţială a eliminării obiectului).
Constructorii şi destructorii se deosebesc de celelalte funcţii membre prin câteva
caracteristici specifice:
numele funcţiilor constructor sau destructor este identic cu numele clasei căreia îi
aparţin; numele destructorului este precedat de caracterul tilde (~);
la declararea şi definirea funcţiilor constructor sau destructor nu se specifică nici
un tip de rezultat (nici tipul void);
nu pot fi moşteniţi, dar pot fi apelaţi de clasele derivate;
nu se pot utiliza pointeri către funcţiile constructor sau destructor;
constructorii pot avea parametri, inclusiv parametri impliciţi şi se pot supradefini;
destructorul nu poate avea parametri şi este unic pentru o clasă.
Constructorul fără parametri se numeşte constructor implicit.
Dacă o clasă nu are constructor şi destructori definiţi, compilatorul generează automat
un constructor implicit, respectiv un destructor implicit, care sunt funcţii publice.
Constructorii pot fi publici sau privaţi, dar, de regulă, se declară cu acces public. Dacă
se declară constructorul private, nu se pot crea obiecte de acel tip.
Acest lucru poate avea sens dacă respectiva clasă este destinată a folosi exclusive ca
şi clasă de bază pentru o ierarhie de clase derivate.
Exemplu
#include <iostream.h>
#include <conio.h>
class punct
{ double x,y;
public:
punct()
{ x = y = 0;
cout<<"\nConstructor implicit punct ("<<x<<','<<y<<')';
}
punct(double a,double b)
{ x = a;
y = b;
cout<<"\nConstructor explicit punct ("<<x<<','<<y<<')';
}
punct(punct& p)
{ x = p.x;
y = p.y;
cout<<"\nConstructor copiere punct ("<<x<<','<<y<<')';
}
~punct()
{ cout<<"\nDestructor punct ("<<x<<','<<y<<')';
cin.get();
}
void afisare()
{ cout<<"\nPunct("<<x<<','<<y<<')';
}
void deplasare(double dx,double dy)
{ x += dx;
y += dy;
}
};
class segment
{ punct A,B;
public:
segment(punct& X, punct & Y)
{ A = X;
B = Y;
cout<<"\nConstructor segment de extremitati";
A.afisare();
B.afisare();
}
segment(segment& CD)
{ A = CD.A;
B = CD.B;
cout<<"\nConstructor copiere segment.Noul segment";
A.afisare();
B.afisare();
}
~segment()
{ cout<<"\nDestructor segment de extremitati";
A.afisare();
B.afisare();
}
void afisare()
{ cout<<"\nSegment de extremitati";
A.afisare();
B.afisare();
}
void translatie(double dx,double dy)
{ cout<<"\nTranslatie segment.Noul segment";
A.deplasare(dx,dy);
B.deplasare(dx,dy);
A.afisare();
B.afisare();
}
};
void main()
{ cout<<"\nConstruiesc punctele P,Q,A,B";
punct P(7,8),Q(-4,5),A,B;
cout<<"\nApasati o tasta";
cin.get();
clrscr();
cout<<"\nConstruiesc segmentul S = PQ";
segment S(P,Q);
S.afisare();
cout<<"\nApasati o tasta";
cin.get();
clrscr();
cout<<"\nD = P";
punct D = P;
cout<<"\nD este";
D.afisare();
cout<<"\nDeplasez D cu dx=5, dy = 7";
D.deplasare(5,7);
D.afisare();
cout<<"\nSegment S1 = S";
segment S1 = S;
cout<<"\nTranslatez segmentul S cu 10,10";
S.translatie(10,10);
cout<<"\nmain s-a terminat! Apasati o tasta";
cin.get();
clrscr();
}
Exercitiu
Să se definească tipul de date: class ceas
{ private :
long int secunde;
public :
ceas();
ceas(int o, int m, int s=0);
ceas( ceas &);
~ceas();
afisare(); };
care reprezintă timpul scurs de la ora 0:0:0 a unei zile, exprimat în secunde.
Funcţia main() va conţine declaraţii de obiecte de tip ceas cu apel al diferiţilor
constructori şi atribuiri între obiecte de tip ceas şi afişarea proprietăţilor obiectelor ceas
prin apelul funcţiei afisare().
Laborator 11
Programare Orientata Obiect in C++
Mostenirea multipla
Conceptul de moştenire permite crearea de clase noi care moştenesc proprietăţile
mai multor clase de bază. Moştenirea multiplă aduce mai multă flexibilitate în construirea
claselor, rezultatul fiind obţinerea unor structuri de clase complexe.
Sintaxa folosită pentru declararea unei clase derivate D este:
class D: specif_acces clasa_baza1, specif_acces clasa_baza2,… {…}
În lista claselor de bază, pentru fiecare clasă de bază, se include specificatorul de
acces.
Principiile prezentate la derivarea simplă şi la crearea ierarhiilor simple de clase
sunt valabile şi în cazul derivării multiple. Prin clasa strpoz se vor crea obiecte ce conţin
un şir de caractere împreună cu poziţia de afişare. Această clasă se obţine prin derivarea
din clasele pozitie şi string, accesul către acestea fiind public.
#include <iostream.h>
#include <string.h>
#include <conio.h>
class pozitie // declarare clasă pozitie
{
protected :
int x, y;
public:
pozitie(int=0, int=0);
pozitie(pozitie&);
~pozitie();
void afisare();
void deplasare(int,int);
};
pozitie::pozitie(int abs, int ord)
{
x=abs; y=ord;
cout<<"\nConstructor - pozitie";
afisare();
}
pozitie::pozitie(pozitie &p)
{
x=p.x; y=p.y;
cout<<"\nConstructor copiere-pozitie";
afisare();
}
pozitie::~pozitie()
{
cout<<"\nDestructor - pozitie";
afisare();
}
void pozitie::afisare()
{
cout<<"\npozitie: x="<<x<<" y="<<y;
}
void pozitie::deplasare(int dx, int dy)
{
x+=dx; y+=dy;
}
class string // declarare clasă string
{
protected:
int ncar; // lungimea sirului
char *str;
public:
string(int n=0)
{
ncar=n;str=new char[ncar+1];
str[0]='\0';
cout<<"Constructor 1- string" ;
afisare();
}
string(char * s)
{
ncar=strlen(s); str=new char[ncar+1];
strcpy(str,s);
cout<<"\nConstructor 2 - string";
afisare();
}
string(string & s)
{
ncar=s.ncar;
str=new char[ncar+1];
strcpy(str, s.str);
cout<<"\nConstructor de copiere-string";
afisare();
}
~string()
{ cout<<"\nDestructor- string";
afisare();
delete str;
}
void afisare()
{
cout<<"\nstring:"<<str<<"\n";
}
string operator+(string &s)
{
string temp(ncar+s.ncar);
strcat(temp.str, str);
strcat(temp.str, s.str);
return temp;
}
};
class strpoz : public pozitie, public string
// declararea clasei strpoz, derivată din
// pozitie şi string cu acces public către
{ // acestea
char culoare;
public:
// în antetul constructorului se specifică parametrii preluaţi de
// constructorii claselor de bază
strpoz(int abs, int ord, int n=0, char c='A') : pozitie(abs,
ord), string(n)
{
culoare=c;
cout<<"\nConstructor 1 - strpoz";
afisare();
}
strpoz(int abs, int ord, char *s, char c='A'):pozitie(abs, ord),
string(s)
{
culoare=c;
cout<<"\nConstructor 2 - strpoz";
afisare();
}
strpoz(strpoz &sp) : pozitie(sp), string(sp) // parametrul
preluat de // constructorii
// claselor de bază este chiar sp pentru
// care se face conversia implicită către
// clasa de bază respectivă
{ culoare=sp.culoare;
cout<<"\nConstructor copiere - strpoz";
afisare();
}
~strpoz()
{
cout<<"\nDestructor -strpoz";
afisare();
}
void coloreaza(char c)
{ culoare=c; }
void afisare()
{
cout<<"\nstrpoz:"<<str;
cout<<"\nx="<<x<<" y="<<y;
cout<<"\nculoare:"<<culoare<<"\n";
}
strpoz operator+(strpoz &sp)
{
strpoz temp(x+sp.x, y+sp.y, ncar+sp.ncar);
strcat(temp.str, str);
strcat(temp.str, sp.str);
return temp;
}
};
void main()
{
strpoz sp1(5, 5, "TEXT");
strpoz sp2(sp1);
string s1("aaa"), s2("bbb");
string s3=s1+s2;
s3.afisare();
strpoz sp3=sp1+sp2;
sp3.afisare();
}
În declaraţia clasei strpoz se specifică derivarea cu acces public din clasele pozitie
şi string. Definiţia fiecărui constructor al clasei strpoz specifică transferul datelor către
constructorii claselor de bază.
Constructorii şi destructorii claselor de bază sunt apelaţi automat la crearea,
respectiv distrugerea obiectelor derivate. Ordinea apelării constructorilor este dată de
ordinea din lista claselor de bază din definiţia clasei derivate, constructorul clasei derivate
fiind apelat ultimul. Destructorii sunt apelaţi în ordine inversă.
Clasa derivată conţine toţi membrii claselor de bază, dar îi poate accesa doar pe
cei declaraţi cu acces public sau protected. Clasele derivate pot supradefini funcţii
existente în clasele de bază. Membrii şi funcţiile omonime pot fi accesate folosind
operatorul de rezoluţie.
De exemplu, pentru clasa strpoz se poate defini funcţia membră de afişare astfel: void strpoz::afisare()
{
cout<<”\nStrpoz:”
pozitie::afisare();
string::afisare();
cout<<”\culoare:”<<c<<endl;
}
iar în funcţia main() se poate include linia de program: sp3.pozitie::afisare();
Exerciţiu:
Să se definească o clasă derivată “produs” care descrie un produs prin denumire, cod,
preţ. Se vor folosi o clasă de bază string pentru membrul denumire şi o clasă care
defineşte codul prin trei grupe de caractere. Se vor realiza cele două variante de derivare,
şi anume o ierarhie simplă de clase şi, respectiv, derivarea multiplă.
Laborator 12
Programare Orientata Obiect in C++
Funcţii şi clase prietene unei clase
Accesul la membrii private ai unei clase se poate acorda, în afara funcţiilor
membre şi unor funcţii nemembre, dacă sunt declarate cu specificatorul friend.
Funcţiile declarate prietene unei clase pot fi funcţii independente sau funcţii
membre ale altei clase. Funcţiile prietene sunt externe clasei, deci apelul lor nu se face
asociat unui obiect al clasei.
class cls
{
…
friend void func();
}
void main()
{
cls obj;
obj.func(); // eroare, funcţia func() este externă clasei
func(); // apel corect al funcţiei func()
…
}
Funcţiile prietene au acces la toţi membrii clasei, ele operând asupra obiectelor care se
transferă ca parametrii ai funcţiei. Referirea unui membru se face prin numele obiectului,
operatorul de selecţie şi numele membrului, dată sau funcţie.
Se pot întâlni următoarele situaţii:
1. o funcţie independentă este prietenă unei clase;
2. o funcţie membră a unei clase este prietenă altei clase;
3. o funcţie este prietenă mai multor clase ;
4. o clasă este prietenă altei clase (toate funcţiile membre ale unei clase sunt
prietene celeilalte clase).
// Situaţia 1. class pozitie
{
int x, y;
public:
pozitie(int abs, int ord)
{ x=abs; y=ord; }
void deplasare(int dx, int dy)
{ x+=dx; y+=dy; }
friend void compar(pozitie &, pozitie &);
// funcţia compar() se declară prietenă
// clasei pozitie
};
void compar(pozitie &p1, pozitie &p2)
// funcţia compar() este externă clasei
// poziţie, deci nu poate fi definită decât
// în afara clasei pozitie
{
if ((p1.x= =p2.x)&&(p1.y= =p2.y))
// funcţia prietenă compar() are acces la
// membrii private ai clasei pozitie
cout<<”\n Pozitii identice”;
else
cout<<”\n Pozitii diferite”;
}
void main()
{
pozitie p1(1, 1); p2(3, 3);
compar(p1, p2); // apel al funcţiei compar()
p1.deplasare(2, 2);
compar(p1, p2); // apel al funcţiei compar()
}
// Situaţia 2
#include <iostream.h>
class A; // declaraţie incompletă a clasei A
class B // declaraţia clasei B
{
int b;
public:
B(int n)
{b=n;}
void func(A &);
// declararea funcţiei membre a lui B, având
// parametru de tip A
};
class A
{
int a;
public:
A(int n)
{a=n;}
friend void B::func(A &);
// se declară funcţia membră clasei B func()
} ; // prietenă clasei A
void B::func( A &ob_a) // definirea funcţiei func() membră a clasei B
{
cout<<'\n'<<ob_a.a;
// funcţia fiind prietenă clasei A are acces la
// membrul private “a” al clasei A
cout<<'\n'<<b;
}
void main()
{
A ob1(10);
B ob2(20);
ob2.func(ob1); // apelul funcţiei membre clasei B
}
Declaraţia friend din clasa A trebuie să găsească clasa B căreia îi aparţine funcţia func()
declarată. În declaraţie trebuie să se specifice şi numele clasei căreia îi aparţine funcţia
prietenă.
// Situaţia 3
#include <iostream.h>
class A; // declaraţie incompletă a clasei
class B; // declaraţie incompletă a clasei
class A
{
int a;
public:
A(int n)
{a=n;}
friend void func(A &, B &);
// funcţia func() este declarată prietenă clasei A
} ;
class B
{
int b;
public:
B(int n)
{b=n;}
friend void func(A &, B &);
// funcţia func() este declarată prietenă clasei B
};
void func( A &ob_a, B &ob_b)
//definirea funcţiei func(), externă claselor A şi B
{
cout<<'\n'<<ob_a.a;
cout<<'\n'<<ob_b.b;
}
void main()
{
A ob1(10);
B ob2(20);
func(ob1, ob2);
// apelul funcţiei func() cu parametri de tip A şi
// respectiv B
}
Funcţia func() se defineşte în afara claselor A şi B, ea fiind o funcţie independentă.
// Situaţia 4
#include <iostream.h>
class A; // declaraţie incompletă a clasei A
class B // declaraţia clasei B
{
int b;
public:
B(int n)
{b=n;}
void func(A &);
};
class A
{
int a;
public:
A(int n)
{a=n;}
friend class B; // clasa B se declară prietenă clasei A
} ;
void B::func( A &ob_a)
{
cout<<'\n'<<ob_a.a;
cout<<'\n'<<b;
}
void main()
{
A ob1(10);
B ob2(20);
ob2.func(ob1);
}
Declaraţia friend din clasa A trebuie să găsească clasa B declarată. Declaraţia
incompletă a clasei A este necesară pentru a se putea specifica parametrii de tip A în
funcţiile membre ale clasei B.
Utilizarea funcţiilor prietene încalcă principiul încapsulării, dându-se acces şi
altor funcţii decât celor membre la membrii private ai unei clase. Pot apare probleme şi
atunci când compilarea funcţiei prietene se face independent de compilarea funcţiilor
membre, prin omiterea accidentală a acesteia. De asemenea, dacă funcţia este membră
altei clase, apar interdependenţe între clase.
În ciuda acestor inconveniente, funcţiile prietene oferă anumite facilităţi, ele
oferind o altă soluţie de acces controlat către membrii unor clase.
Exerciţii:
Se defineşte clasa: class dreptunghi
{
int x1, y1, x2, y2;
static int cnt;
// membru static ce reprezintă un contor pentru obiectele
// clasei create
void normalizare();
// valorile coordonatelor sunt încadrate in intervalul
// [1, 80], cele orizontale şi [1, 25] cele verticale
public:
dreptunghi(int=1, int=1, int=80, int=25);
~dreptunghi();
void afisare();
// afişarea se face în mod text, prin marcarea laturilor
// dreptunghiului cu un caracter oarecare afişat în mod
// repetat
static int nr_dreptunghiuri();
// afişează numărul obiectelor dreptunghi existente la un
// moment
void deplasare(int, int);
// realizează translatarea dreptunghiului;
};
Să se definească o funcţie prietenă clasei dreptunghi, aria(), care să
returneze aria corespunzătoare dreptunghiului.
În funcţia main() se declară obiecte de tip dreptunghi. Se afişează
proprietăţile obiectelor, ariile corespunzătoare, numărul de instanţieri.