Documente Academic
Documente Profesional
Documente Cultură
MODUL 1
CURS 1
CUPRINS
1. RECURSIVITATE
1. Noţiuni introductive
2. Funcţii recursive. Încărcarea stivei
3. Ieşirea din recursivitate
Recursivitatea în matematică
Recursivitatea în programare
Algoritm recursiv:
Pn
Pn-1
Pk → val
3
Exemple de algoritmi implementați recursiv
Factorialul:
n!= 1*2*3*...*n, definiţie nerecursivă
n! = n * (n-1)!, definiţie recursivă, știind că 0! =1
4
cmmdc(m, n) =
m, dacă n=0
n, dacă m=0
cmmdc(n, m%n), dacă m>n
cmmdc(m,n%m), dacă n>m
cmmdc(m, n) =
m, dacă m=n
cmmdc(m-n, n), dacă m>n
cmmdc(m, n-m), dacă m<n
5
Aranjamente de n luate câte k
A(n,k)=n!/ (n-k)!=n*(n-1)*…(n-k+1)=n*[(n-1)*…*((n-
1)-(k-1)+1)]= n*A(n-1, k-1)
A(n,1)=n; // condiţia de iesire
6
FUNCŢIA LUI ACKERMANN :
Autoapel:
direct (recursivitate directă)
indirect (recursivitate indirectă)
8
Factorialul n! = n * (n-1)!
int main(void)
{
cout <<" Factorial de 3 = " << factorial(3) << endl;
cout<<"adresa 1";
}
9
Câteva observații !!!
10
main()
factorial (1)
factorial (0)
11
Laapelul unei funcţii, precum şi la autoapelul ei,
pe stivă se vor depune:
12
EVOLUŢIA STIVEI factorial(0)
Stiva program
factorial(1) adresa2
Stiva program 0
factorial(2)
adresa2
adresa2
Stiva program
1
1
factorial(3)
adresa2 adresa2
adresa2
Stiva program
2 2 2
adresa1 adresa1 adresa1
adresa1
3 3 3
3
13
Şirul lui Fibonacci
f(n) = 0, dacă n=0
f(n) = 1, dacă n=1
f(n) = f(n-1) + f(n-2), dacă n>= 2
int fib(int n)
{
if (n < 2)
return n;
else
return fib(n-1)+fib(n-2);
}
int main(void){
cout << "Fibonacci de 4 = " << fib(4) << endl;
}
14
15
Metoda este însă foarte ineficientă, timpul de calcul fiind
de ordinul Φ (numit secţiunea de aur), ce reprezintă un
timp exponenţial,
Ineficienţa este dată de faptul că se evaluează unele
elemente ale şirului de mai multe ori:
16
Observații!
17
Primele n numere din şirul lui Fibonacci se pot
calcula iterativ folosind un şir ajutător :
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void) {
int l = 1, j = 0, k, N;
printf("Numarul de elemente dorit este: ");
scanf("%d", &N);
printf("f[0]= 0\nf[1]= 1\n");
for (k = 2; k < N; k++) {
l = l + j; // elem. de pe poz k din sir
j = l - j; // se pregateste elementul de pe poz k-1
printf("f[%d]= %d\n", k, l);
}//end for 19
}
Combinările
C(n,k) = C(n-1,k) + C(n-1,k-1)
21
Concluzii:
Un algoritm recursiv presupune mai multe niveluri
22
1.3. Ieşirea din recursivitate
23
Posibilităţi:
se asociază funcţiei recursive un parametru întreg n
şi apelul se face cu valorile (n-1), (n-2),… cât timp
n>0 și nu e indeplinită conditia de iesire
se asociază funcţiei recursive un parametru logic, iar
apelul se face atâta timp cât parametrul are valoarea
TRUE
25
Exemplul 1: suma cifrelor unui număr întreg
Abordare:
Recursivă: suma cifrelor numărului din stânga +
ultima cifră
Nerecursivă (iterativă): suma cifrelor (cifră cu cifră)
int suma_cifre_recursiv(int n)
{
if (n==0)
return 0;
else
return suma_cifre_recursiv(n/10)+n%10;
}
26
Exemplu:
suma_cifre_recursiv(1234);
Încărcarea stivei
Adresa 2
n=0
Adresa 2
n=1
Adresa 2
n=12
Adresa 2
n=123
Adresa 1
n=1234 27
Descărcarea stivei
Adresa 2
suma_cifre_recursiv(0) n=0
0
Adresa 2
suma_cifre_recursiv(1) n=1
0+1%10=1
Adresa 2
suma_cifre_recursiv(12) n=12
1+12%10=3
Adresa 2
suma_cifre_recursiv(123) n=123
3+123%10=6
Adresa 1
suma_cifre_recursiv(1234) n=1234
6+1234%10=10
28
main()
VARIANTA ITERATIVĂ
int suma_cifre_iterativ(int n)
{
int s=0;
while(n != 0) {
s += n%10;
n /= 10;
}
return s;
}
29
Exemplul 2: suma elementelor unui tablou
unidimensional de tip numeric
Abordare:
Recursivă: suma primelor (n-1) elemente + ultimul element
Nerecursivă: suma, element cu element
30
VARIANTA NERECURSIVĂ
31
Exemplul 3: şir palindrom
Un şir este palindrom dacă:
primul caracter este identic cu ultimul caracter
restul şirului este un şir palindrom
exemple: rotor, madam, cojoc
33
Tipuri de probleme care de obicei se rezolvă cu
ajutorul funcţiilor recursive:
34
PROGRAMAREA CALCULATOARELOR 2
Modul 1
Curs 2
CUPRINS
2. Metode de programare recursive/nerecursive
2.1. Metoda backtracking
2.2. Metoda divide et impera
36
2. Metode de programare recursive/nerecursive
2.1. Metoda backtracking
37
O primă modalitate de rezolvare:
- considerarea tuturor combinaţiilor posibile ale
candidaţilor
- evaluarea funcţiei criteriu pentru fiecare combinaţie
38
Optimizare prin reguli specifice problemei care:
fie permit selectarea combinaţiilor ce vor fi
evaluate mai întâi
fie permit eliminarea anumitor combinaţii, fără ca
acestea să mai fie evaluate
39
Principiul:
42
CARACTERISTICI:
dacă valoarea considerată nu conduce la o soluţie
plauzibilă, se alege o altă valoare din alternativele
posibile pentru acea componentă ;
44
Atât în varianta recursivă cât şi în cea nerecursivă, iniţial
se determină (citesc) date referitoare la:
numărul componentelor, n;
domeniul valorilor, h;
alte date necesare pentru elaborarea algoritmului
(functia criteriu)
46
47
Varianta nerecursivă
k = 0;
x[k] = 0;
51
Există variante de backtracking în care soluţiile nu au
aceeaşi lungime (nr. variabil de componente) sau se
cere obţinerea unor soluţii optimale, etc.
Exemple:
1. colorarea benzilor unui drapel cu n benzi având h
culori disponibile, benzile alăturate având culori
diferite (număr fix de componente)
2. descompunerea unui număr n, în sumă de
numere naturale (număr variabil de componente)
3. problema comis-voiajorului
52
Problema drapelului – varianta nerecursivă
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define DIM 5
int posibil(int);
void afis_sol(void);
int x[DIM],n;
int main(void){
int k,h;
printf("\nIntrodu numarul de culori ale unui drapel(benzi,
<=DIM=5)\n");
scanf("%d",&n);
printf("Introdu numarul maxim de culori posibile ale unei benzi \n");
scanf("%d",&h);
53
k=0;
x[k]=0;
do {
while(x[k] < h) // mai sunt valori posibile pentru
// componenta k
{
x[k]++; //trec la urmatoarea valoare
if(posibil(k))
if(k==(n-1)) afis_sol(); //e gata solutie
else {k++; x[k]=0; } // trec la urmatoarea
// componenta cu
// valori de la zero
}//while
k--; // nu mai sunt valori pentru componenta k. Revin la
// componenta k-1
}while(!(k<0)); // m-am intors mai mult decat se putea,
// sau k>=0
}//main 54
int posibil(int k)
{
if(k==0)return 1; //initial totul este posibil
if(x[k-1]==x[k]) return 0; // doua culori alaturate
// identice - nu e posibil
return 1; //culorile alaturate nu sunt identice
}//posibil
void afis_sol(void)
{
for(int i=0;i<n;i++) //afisez solutia curenta pentru
// componentele drapelului
printf("%d ",x[i]);
printf("\n");
}//afis_sol
55
PROBLEMA DRAPELULUI - VARIANTA RECURSIVĂ
#include <stdio.h>
#define DIM 5
int posibil(int);
void afis_sol(void);`
void dr_rec(int k);
int x[DIM], n, h;
int main(void){
printf("\nIntrodu numarul de culori ale unui drapel(benzi,
<=DIM=5)\n");
scanf_s("%d",&n);
printf("Introdu numarul maxim de culori posibile ale unei
benzi \n");
scanf_s("%d",&h);
dr_rec(0); //apel functie recursive
}//main 56
void dr_rec(int k)
{
for(int i=1;i<=h;i++)
{ x[k]=i;
if(posibil(k))
if(k==(n-1)) afis_sol(); //solutie finala
else dr_rec(k+1); //trec la urmatoarea
//componenta
}
}//dr_rec
57
Descompunerea numărului - Varianta iterativă
#include<stdio.h>
#define DIM 20
int posibil(int k, int &s);
void afis_sol(int k);
int x[DIM], n;
int main(void){
int k,sum;
printf("\nNumarul ce va fi descompus( <=DIM=20) : ");
scanf_s("%d",&n);
printf("\n\tSolutii posibile :\n");
// initializari
k = 0;
x[k] = 0;
58
do {
while(x[k] < n)
{
x[k]++;
if(posibil(k, sum)) {
if(sum == n) // solutia finala
afis_sol(k);
else {
k++;
x[k] = 0;
}
}
} // bucla valori componenta
k--; // pas inapoi
} while(!(k < 0)); // bucla componente
59
}
Descompunerea numărului - Varianta recursivă
#include <stdio.h>
#define DIM 20
void back_SubSet(int n);
int posibil(int k, int &s);
void afis_sol(int k);
int x[DIM], n;
int main(void){
printf("\nNumarul ce va fi descompus( <=DIM=20) : ");
scanf_s("%d",&n);
printf("\n\tSolutii posibile :\n");
back_SubSet(0); // apel initial
}
60
void back_SubSet(int k)
{
int sum;
x[k] = 0;
// pentru toate valorile pe care le poate lua x[k]
while(x[k] < n)
{
x[k]++;
if(posibil(k, sum)) // solutie posibila
{
if(sum == n) // solutie completa
afis_sol(k);
else
back_SubSet(k+1);
}
} 61
}
int posibil(int k, int &s)
{
s=0;
if(k==0) return 1; // initial totul este posibil
if(x[k] > x[k-1]) {
for( int i=0; i<=k; i++)
s += x[i];
if(s <= n)
return 1;
}
return 0;
}
}//main
64
int posibil(int k)
{
if(k==0)return 1;
if(COST[x[k-1]][x[k]]!=0){ //drum direct
for(int i=0;i<k;i++) //orasul nu a mai fost ales
if(x[k]==x[i]) return 0;
return 1;
}
return 0;
}//posibil
65
void gene(int k) {
for(int i=0;i<n;i++){ // valori intre 0 si n-1
x[k]=i;
if(posibil(k))
if((k==(n-1))&& (COST[x[n-1]][x[0]]!=0)){
C=0;// stabilire cost pentru solutia determinata
for(int i=0;i<n-1;i++) C+=COST[x[i]][x[i+1]];
C+=COST[x[n-1]][x[0]];
if(C<cost_M){
for(int i=0;i<n;i++)
Y[i]=x[i];
cost_M=C; //salvare solutie cost minim
}
}
else gene(k+1);
} //for
66
} // end gene
Concluzii
67
CONCLUZII:
Etape specifice metodei backtracking:
Specificarea vectorului soluţie X[N]:
dimensiunea N,
69
determinarea petelor de culoare de arie maximă pentru o
imagine reprezentată sub forma unei matrici cu NL linii şi
NC coloane care conţine NG nuanţe de gri;
70
2. METODE DE PROGRAMARE
RECURSIVE / NERECURSIVE
71
Căutarea binară
Fie un şir de elemente ordonat. Se pune problema găsirii
unui element (cheia) în şir.
Descompunerea problemei :
72
Valoarea căutată: 23
73
VALOAREA CĂUTATĂ: 88
74
Funcţia de căutare va returna poziţia în şir dacă s-a găsit
elementul dat sau valoarea (-1) în caz de insucces:
int CautareBinara(int *p, int inc, int sfr, int val) //recursiv
{
int mij;
mij = (inc + sfr)/2;
if(p[mij] == val)
return mij;
76
Se cere să se mute discurile de pe tija A pe tija B
respectând următoarele reguli:
la fiecare pas se va muta un singur disc
discurile vor forma secvenţe descrescătoare pe oricare
din cele 3 tije, deci peste discul k se pot aşeza doar
discurile 1, 2, …, k-1
tija C va fi folosită ca şi tijă de manevră
77
Etapa1: Se deplasează discurile 1, 2, …, n-1 de pe tija A
pe C astfel:
78
Etapa2: Discul n se mută de pe tija A pe tija B astfel:
79
Etapa3: Se deplasează cele (n-1) discuri de pe tija C pe
B astfel:
80
Practic, problema turnurilor din Hanoi cu n discuri s-a
redus la rezolvarea aceleiaşi probleme cu n-1 discuri, pe
care ştim să o rezolvăm dacă știm pentru n-2 discuri
ş.a.m.d., ştim să o rezolvăm dacă o ştim rezolva pentru
n=1
81
Cele 3 tije pot să fie în situaţiile:
Sursă: discuri care trebuie transportate pe altă tijă
Destinaţie: tija pe care se afla discurile transferate de pe
sursă
Manevră: tija folosită pentru stocarea temporară a
discurilor
82
#include<iostream>
#using namespace std;
85
determinarea maximului unui şir cu ajutorul funcţiei
recursive m_max( ):
int m_max(int *vector, int pozi, int len);
86
#include<iostream>
using namespace std;
#define MAX 8
void citire_sir(int*, int);
void tip_sir(int*, int);
int m_max(int*, int, int);
int main( ){
int n, Maxim;
int vect[MAX];
cout << "Numarul de elemente mai mic de " << MAX <<"
este=";
cin >> n;
cout << "Introduceti cele n elemente\n";
citire_sir(vect,n);
Maxim=m_max(vect, 0, n);
cout << "Maximul sirului este " << Maxim <<'\n';
cout <<"Tipareste elementele sirului\n";
87
tip_sir(vect,n);
cin.get(); }
int m_max(int *vector, int pozi, int len)
{
int a,b;
if(len == 1)
return *(vector+pozi);
else
{
a = m_max(vector, pozi, len/2);
b = m_max(vector, len/2+pozi, len/2+len%2);
if(a>b)
return a;
else
return b;
} 88
}
void citire_sir(int* v, int n)
{
// corp functie
}
89
Ce stim dupa acest curs?
1. Cum se implementeaza in C/C++ un algortim recursiv?
2. Ce este o functie recursiva?
3. Ce este recursivitatea directa, respectiv cea indirecta?
4. Ce operatii au loc pe stiva la apelul unei functii?
5. Ce intelegem prin notiunea de stackoverflow?
6. Ce intelegem prin notiunea de time-out?
7. Cum se asigura iesirea din recursivitate?
8. Ce intelegem prin mecanismul de backtracking?
9. Pentru ce tip de probleme se aplica backtracking-ul?
10. Care sunt elementele de baza care se stabilesc la o pentru
rezolvarea unei probleme prin metoda backtracking?
11. Care este principiul metodei divide et impera?
90
91
PROGRAMAREA CALCULATOARELOR –
ALGORITMI
MODUL 1
CURS 3
3. TEHNICI DE CĂUTARE SI SORTARE
3.1. METODE DE CĂUTARE
Obiectiv: Căutarea unor obiecte pe
baza valorii unui câmp (cheie) asociat
fiecărui obiect
- dacă obiectele nu sunt ordonate - căutare directă
- dacă obiectele sunt ordonate găsirea unui obiect se
poate face mai rapid
2
Căutarea binară
int CautareBinara(int *p, int n, int val) //iterativ
{
int inc, sfr, mij;
inc = 0;
sfr = n-1;
mij = (inc + sfr)/2;
while((inc <= sfr) && (val != p[mij]))
{
if(val < p[mij]) sfr = mij - 1;
else inc = mij + 1;
mij = (inc + sfr) / 2;
}
if(p[mij] == val)
return mij;
else
3
return -1;
}
int CautareBinara(int *p, int inc, int sfr, int val) //recursiv – divide et
// impera
{
int mij;
mij = (inc + sfr)/2;
if(p[mij] == val)
return mij;
5
Funcţii de bibliotecă
Biblioteca standard (search.h) pune la dispoziţie funcţii
pentru căutare
int main( ){
int int_values[DIM] = { 1, 3, 2, 4, 5 };
int* int_ptr, key_value, num = 5; // key_value este valoarea cautata
printf("Dati valoarea cautata: ");
scanf("%d", &key_value);
int_ptr = (int*)_lfind(&key_value, int_values, (unsigned*)&num, sizeof(int),
(int (*) (const void*, const void*)) compare_int);
if (int_ptr != 0)
printf("Numarul %d este pe poz. : %lld la adresa %p\n", key_value, int_ptr -
int_values + 1, int_ptr);
else
printf("Numarul %d nu este in tabel\n", key_value);
}//end main( )
int compare_int(int* a, int* b){
return(*a - *b);
}//end compare_int( )
9
#include<search.h>
#include<stdio.h>
#include<string.h>
#define DIM 12
int cmp(const char* arg1, const char* arg2);
int addelem(const char* key, const char** tab, int nelem);
int main(void){
const char* luni[DIM] = {"ian", "feb", "mar", "apr", "mai", "iun" };
int i, nluni=6;
const char* key = "iul";
if (addelem(key, luni, nluni))
printf("Luna %s este deja in tablou.\n", key);
else {
nluni++;
printf("Luna \"%s\" a fost adaugata in tablou : ", key);
for (i = 0; i < nluni; i++)
printf("%s, ", luni[i]);
} 1
} 0
int addelem(const char *key, const char **tab, int nelem)
{
int oldn = nelem;
_lsearch(&key, tab, (size_t *)&nelem, sizeof(char *),
(int(*)(const void *,const void *))cmp);
return(nelem == oldn);
}
12
// utilizarea functiei de biblioteca bsearch()
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
int main(void){
int int_values[] = {1, 2 , 3, 4, 5};
float float_values[] = {1.1, 2.2, 3.3, 4.4, 5.5};
if (int_ptr)
printf("Valoarea %d a fost gasita!\n", int_value);
else
printf("Valoarea %d nu a fost gasita!\n", int_value);
num = sizeof(float_values)/sizeof(float);
14
//apel la functia de bibioteca bsearch() pentru sirul de numere reale
float_ptr = (float *)bsearch(&float_value, float_values, num,
sizeof(float),(int (*) (const void *, const void *)) compare_float);
if (float_ptr)
printf("Valoarea %3.1f a fost gasita!\n", float_value);
else
printf("Valoarea %3.1f nu a fost gasita!\n", float_value);
}//end main()
15
int compare_int(int *a, int *b)
{
return(*a - *b);
} //end compare_int()
16
Metode de sortare- Generalități
Sortarea constă în rearanjarea obiectelor într-o ordine
specifică, prin permutarea acestora
Pentru un set de obiecte S={a1, a2, ..., an}, prin sortare
rezultă setul S1={ak1, ak2, ..., akn}, astfel că, dându-se o
funcţie de ordonare f, este îndeplinită relaţia de ordine :
f(ak1) < f(ak2) <...< f(akn)
Sortarea se face în raport cu o cheie asociată obiectelor
Eficienţa unei metode de sortare se evaluează prin:
numărul de comparaţii ale cheii
numărul de permutări ale unui obiect
19
Sortarea prin interschimbare (bubble sort)
20
EXEMPLU BUBBLE-SORT
Exemplu : 9 7 5 6 2
7 9 5 6 2 -> 7 5 9 6 2 -> 7 5 6 9 2 -> 7 5 6 2 9
5 7 6 2 9 -> 5 6 7 2 9 -> 5 6 2 7 9
5 6 2 7 9 -> 5 2 6 7 9
5 2 6 7 9 -> 2 5 6 7 9
Exemplu : 9 2 5 6 7:
9 2 5 6 7 -> 2 9 5 6 7 -> 2 5 9 6 7 -> 2 5 6 9 7 -> 2 5 6 7 9
25679
23
void SortBubble(int *p, int n)
{
int i, j, temp, flag;
for(i=0; i<n; i++) {
flag = 1;
for(j=1; j<n-i; j++) {
if(p[j-1] > p[j]) {
// interschimbare
temp = p[j];
p[j] = p[j-1];
p[j-1] = temp;
flag = 0;
}
}
// daca nu s-a facut nici o interschimbare
if(flag==1) break;
} 24
}
//bubble sort (varianta do-while)
void SortBubbleD(int *p, int n)
{
int j, temp, flag;
do{
flag = 1;
for(j=0; j<n-1; j++) {
if(p[j] > p[j+1]) {
temp = p[j];
p[j] = p[j+1];
p[j+1] = temp;
flag = 0;
}
}//for
}while(flag == 0);
}
25
Sortarea prin selecţie simplă
se caută cel mai mic element şi se aduce pe prima
poziţie din şir prin interschimbare
apoi se consideră tabloul format din elementele
2,3,...,N şi se caută cel mai mic element care se
aduce pe prima poziţie din şirul curent, ...
Exemplu : 9 7 5 6 2
9 7 5 6 2 -> 2 7 5 6 9 -> 2 5 7 6 9 -> 2 5 6 7 9
26
void SortSel(int *p, int n){
int i, j, pozmin, temp;
for(i=0; i<n; i++) // parcurgeri
{
// cautare pozitie cel mai mic element din sirul curent
pozmin = i;
for(j=i+1; j<n; j++) {
if(p[pozmin] > p[j])
pozmin = j;
}
// interschimbare cu elementul de pe prima pozitie
temp = p[pozmin];
p[pozmin] = p[i];
p[i] = temp;
} 27
}
Sortarea prin inserţie simplă
se consideră pe rând tablourile formate din primele
2,3,...,N elemente din vector
se asigură că aceste tablouri sunt ordonate prin
aducerea noului element (2,3,...) pe poziţia
corespunzătoare valorii sale
acest lucru implică deplasarea spre dreapta, cu o
poziţie, a elementelor cu chei mai mari decât cea a
noului element, astfel ca acesta să ajungă înaintea
acelor elemente, dar după elementele cu chei mai mici
28
EXEMPLU INSERȚIE SIMPLĂ
Exemplu : 9 7 5 6 2
97 -> 79
7 9 5 -> 579
5 7 9 6 -> 5679
5 6 7 9 2 -> 25679
29
void SortIns(int *p, int n){
int i, j, temp;
for(i=1; i<n; i++) {
temp = p[i];
for(j=i-1; j>=0; j--) // tabloul curent
{
if(p[j] > temp)
p[j+1] = p[j]; // deplasare dreapta
else
break;
}
p[j+1] = temp;
} 30
}
Observaţie:
poziţia pe care trebuie adus noul element este
căutată secvenţial (pe un subşir deja ordonat),
astfel că algoritmul se poate îmbunătăţi
(elementele analizate fiind deja sortate), folosind
căutarea binară
31
void SortIns(int *p, int n){
int i, j, temp, inc, mij, sfr;
for(i=1; i<n; i++) {
temp = p[i];
// cautarea binara
inc = 0; sfr = i-1;
mij = (inc + sfr)/2;
while(inc <= sfr) {
if(p[mij] > temp)
sfr = mij-1;
else
inc = mij+1;
mij = (inc + sfr)/2;
} // → toate valorile incepand cu poz inc sunt mai
mari decat p[i] → le deplasam spre dreapta
for(j=i-1; j>=inc; j--)
p[j+1] = p[j]; // deplasare dreapta
p[inc] = temp; 32
}
}
ALGORITMUL SHELL SORT
ShellSort este un algoritm de sortare performant, bazat
pe sortarea prin inserție (InsertSort)
33
ALGORITMUL SHELL SORT
Algoritmul ShellSort realizează deplasări pe distanţe
mari, sortând elementele aflate la distanţe mari prin
metoda inserţiei.
h=4
7 10 1 9 2 5 8 6 4 3
* *
2 10 1 9 7 5 8 6 4 3
* *
2 5 1 9 7 10 8 6 4 3
* *
2 5 1 6 7 10 8 9 4 3
* * *
2 5 1 6 4 10 8 9 7 3
* * *
2 3 1 6 4 5 8 9 7 10
35
h=1
2 3 1 6 4 5 8 9 7 10
*
1 2 3 6 4 5 8 9 7 10
*
1 2 3 4 6 5 8 9 7 10
*
1 2 3 4 5 6 8 9 7 10
*
1 2 3 4 5 6 7 8 9 10
36
void ShellSort(int *p, int max){
int stop=0,temp=0,h=0, i, j , k;
while( h<= max/3)
h = 3*h + 1; // se porneste de la un h>max/3
while(h>0) {
for(int i=h; i<max; i++) { // se ia fiecare element de la pozitia h
// pana la sfarsit
temp=p[i];
j=i;
while((j>=h)&&(p[j-h]>temp)) {
p[j]=p[j-h]; // deplasare dreapta
j=j-h;
}
p[j]=temp;
} //for
h = (h-1)/3; // modificarea distantei de comparare
}// while 37
}
Observaţii:
toate exemplele anterioare realizează sortarea prin
ordonarea crescătoare a unor numere întregi
Temă:
modificaţi aceste funcţii de sortare pentru a realiza
sortări prin ordonare descrescătoare pentru toate
tipurile aritmetice
38
Exemplul 1: utilizarea unei funcţii de sortare:
#include <iostream>
using namespace std;
int main( ){
int i, n, *tab;
cout <<"Cate numere: ";
cin >> n;
42
Exemplul 3
#include <iostream>
using namespace std;
int fcmp(const char *s1, const char *s2);
void BubbleSort (const char **names, const int size);
int main(void) {
int dimc = 6;
const char *tabc[] = {"abc", "xyz", "acd", "axyz", "bc", "eltcti"};
BubbleSort(tabc, dimc); //sortare crescatoare dupa dimensiune
cout << "\nSirurile sortate: ";
for(int i=0;i<dimc; i++)
cout << tabc[i] << ", ";
cout << endl;
}
43
void BubbleSort (const char **names, const int size) {
int swapped;
do {
swapped = 0;
for (int i = 0; i < size-1; ++i) {
if (fcmp(names[i], names[i+1]) > 0 ) {
const char *temp = names[i];
names[i] = names[i+1];
names[i+1] = temp;
swapped = 1;
}
}
} while (swapped);
}
45
46
3.2.2. Metode avansate de sortare
47
SORTAREA PRIN INTERCLASARE (MERGE SORT)
Principiul metodei: se bazează pe obținerea din 2 tablouri
ordonate a unui tablou ordonat ce conține elementele
celor două.
Sortarea prin interclasare utilizează metoda Divide et
Impera
Se împarte tabloul inițial nesortat în secvențe din ce în ce
mai mici, astfel încât fiecare secvență să fie ordonată la
un moment dat și apoi interclasată cu o altă secvență din
tablou.
Practic interclasarea va începe când se ajunge la o
secvență formată din două elemente. Aceasta odată
ordonată se va interclasa cu o alta corespunzătoare, iar
apoi procesul va continua la următorul nivel ce implică
perechi de câte 4 elemente, etc.
48
49
#include<iostream>
using namespace std;
void interclas(int *a,int i,int m,int j);
// i- pozitia de inceput a primului subsir, m- pozitia de sfarsit a primului
subsir,
// j- pozitia de sfarsit al celui de-al doilea subsir
void divimp(int *a,int i,int j);
// i- indicele primului element din vector,
// j - indicele ultimului element din vector
#define DIM 1000
int main(){
int a[DIM],n;
cout<<"n="; cin>>n;
for(int i=0;i<n;i++){
cout<<"a["<<i<<"]=";
cin>>a[i];
} 50
divimp(a,0,n-1);
for(int i=0;i<n;i++)
cout<<a[i]<<' ';
} //main
53
Algoritmul QuickSort
Se bazează pe noţiunea de partiţie
Prin partiţionarea datelor se înţelege împărţirea
acestora în două grupuri, astfel încât toate elementele
cu chei mai mari decât o valoare dată se află într-un
grup, iar cele cu valori mai mici se află în celălalt grup
Etapele partiţionării :
se consideră un element x al tabloului numit şi element
pivot
se parcurge tabloul de la stânga până ce se găseşte un
element ai mai mare ca x
se parcurge tabloul de la dreapta până ce se găseşte un
element aj mai mic ca x
54
se interschimbă ai cu aj
se actualizează i şi j prin incrementare, respectiv
decrementare
se repetă paşii anteriori până când cele două scanări se
întâlnesc
în acest moment tabloul este partiţionat, adică :
în stânga lui x se găsesc numai elemente mai mici ca x
în dreapta lui x se găsesc numai elemente mai mari ca x
a[k] <= x, k = 0,...,i-1
a[k] >= x, k = j+1,...,n
a[k] = x, k = j+1,...,i-1
55
După prima partiţionare, şirul nu este încă ordonat
De aceea se continuă, la stânga şi la dreapta pivotului,
în acelaşi mod.
- algoritmul QuickSort are o natură recursivă
Descriere in pseudocod:
QuickSort is {
If (right-left) == 0 then
Return
Else
pivot = Tablou[right]; // sau Tablou[middle]
partition = Partitionare(left, right, pivot)
QuickSort(left, partition-1);
QuickSort(partition+1, right);
EndIf
} 56
Pivotul este elementul din mijlocul sirului
88 6 57 71 60 42 83 73 48 65
48 6 57 71 60 42 83 73 88 65
48 6 57 42 60 71 83 73 88 65
6 48 57 42 60 71 65 73 88 83
6 48 42 57 60 65 71 73 83 88
6 42 48 57 60 65 71 73 83 88
Pivotul este ultimul element din șir
88 6 57 71 60 42 83 73 48 65
inc sfr
65 6 57 71 60 42 83 73 48 88
inc
65 6 57 48 60 42 83 73 71 88
inc sfr
42 6 57 48 60 65 83 73 71 88
sfr inc inc
42 6 57 48 60 65 71 73 83 88
sfr inc
6 42 57 48 60 65 71 73 83 88
6 42 57 48 60 65 71 73 83 88
6 42 48 57 60 65 71 73 83 88
void QuickSort(int *p, int prim, int ultim)
{
int inc, sfr, pivot, temp, mij;
inc = prim;
sfr = ultim;
mij=(prim+ultim)/2;
pivot = p[mij];
// partitionare
do {
while(p[inc] < pivot)
inc++;
while(p[sfr] > pivot)
sfr--;
59
if(inc < sfr) {
temp = p[inc];
p[inc] = p[sfr];
p[sfr] = temp;
}
if(inc <= sfr) {
sfr--;
inc++;
}
}while(inc <= sfr);
// apel recursiv
if(prim < sfr)
QuickSort(p, prim, sfr);
if(inc < ultim)
QuickSort(p, inc, ultim);
60
}
Alegerea valorii pivot :
pivotul trebuie să reprezinte valoarea cheii unui element
din tablou
se poate alege la întâmplare, dar trebuie evitate
valoarea minimă, respectiv maximă
Valori uzuale :
elementul din mijlocul tabloului
ultimul element din tabloul partiţionat
valoarea mediană între primul, ultimul şi elementul din
mijlocul tabloului
61
Biblioteca standard (stdlib.h) pune la dispoziţie funcţia:
void qsort(void *base, size_t nelem, size_t width,
int(*fcmp)(const void *, const void *));
int main(void){
int i,n,tab[DIM];
printf("Introdu dimensiune tablou: ");
scanf("%d",&n);
printf("Introdu elemente tablou:\n");
for(i=0;i<n;i++){
printf("tab[%d] = ",i);
scanf("%d",&tab[i]);
}
fcmp=compara; //fcmp va avea adresa functiei de comparare 63
qsort(tab,n,sizeof(i),fcmp);
puts("Tabloul ordonat este");
for(i=0;i<n;i++)
printf("%d ",tab[i]);
}//main
int compara(const void * val1, const void* val2)
{
//return((*(int*)val1) - (*(int*)val2)); //ordonare crescatoare
return((*(int*)val2) - (*(int*)val1)); //ordonare descrescatoare
} //compara
64
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
struct datac {
int an;
int luna;
int zi;
};
struct pers {
char numep[12];
struct datac datan;
};
65
int cmp( const struct pers *a, const struct pers *b);
int main(void){
struct pers angaj[ ] = {
{"x1", {1980, 6,6}},
{"x2", {1960, 5, 5}},
{"x3", {1960, 1,5}},
{"x4", {1961, 12, 32}},
{"x5", {1980, 2, 29}}
};
int i;
int nang = sizeof(angaj)/sizeof(struct pers);
// apel functie de sortare
qsort((void *)angaj, nang, sizeof(angaj[0]), (int (*)(const void*, const
void*))cmp);
printf("Datele sortate :\n");
for (i = 0; i < nang; i++) {
printf("\t%s, %d, %d, %d\n", angaj[i].numep, angaj[i].datan.an,
angaj[i].datan.luna, angaj[i].datan.zi);
}
66
}
int cmp(const struct pers *a, const struct pers *b){
if((a->datan).an > (b->datan).an) return 1;
else
if((a->datan).an < (b->datan.an)) return -1;
else {
if((a->datan).luna > (b->datan).luna) return 1;
else if((a->datan).luna < (b->datan).luna) return -1;
else {
if((a->datan).zi > (b->datan).zi)
return 1;
else if((a->datan).zi < (b->datan).zi)
return -1;
else
if((strcmp(a->numep, b->numep)>0)) return 1;
else if((strcmp(a->numep, b->numep)<0))
return -1;
return 0;
} //else 67
}// else
}
68
PROGRAMAREA
CALCULATOARELOR 2
Modul 2, partea I
Curs 4
1
CUPRINS
2
1. CONCEPTE ALE PROGRAMĂRII OBIECTUALE
DE CE POO (PROGRAMARE ORIENTATĂ PE OBIECTE)?
Orice program este compus din 2 părți distincte:
- date (de intrare și/sau iesire) → tipuri de date
- operații → funcții
Tipuri de date utilizate în programare:
- tipuri predefinite
- tipuri de date utilizator (în C/C++ prin utilizarea struct,
union, enum și typedef); operațiile acționează tot asupra
unor tipuri predefinite de date, elementele componente nu
sunt protejate → programarea structurată
- clasa: tip abstract, reunește date și operații în cadrul
aceluiași tip de dată → Programarea ADT (Abstract Data
Type – abstractizare a datelor) 3
CONCEPTE ALE PROGRAMĂRII OBIECTUALE
6
OBIECTELE
Ce este un obiect? Un obiect modelează o entitate din
lumea reală sau imaginară.
8
Clasa = un șablon pentru generarea unor obiecte având
structura și comportamentul identice
9
Mesajele fac apel la serviciile pe care obiectul le poate
oferi. Obiectele comunică prin intermediul mesajelor
(emițătoare și receptoare). Mesajul - obiect, numele
metodei și parametrii care rafinează mesajul.
11
1. Încapsularea datelor:
gruparea datelor şi a operaţiilor permise asupra acestora
ascunderea informaţiei (pentru protejarea împotriva
distrugerii accidentale).
accesul la date prin intermediul metodelor
asigurată prin specificatorii de acces
2. Abstractizarea datelor:
− identifică proprietățile generale și esențiale, omițând
unele detalii neesențiale (Ex. – carnet șofer)
un obiect este caracterizat complet de specificaţiile
metodelor sale, detaliile de implementare fiind
transparente pentru utilizator (principiul lui Parnas)
modificarea structurii datelor membre are efect numai
asupra metodelor, iar dacă se păstrează specificaţiile
metodelor, nu afectează utilizarea obiectului 12
Pot exista la un moment dat mai multe obiecte de acelaşi
tip, adică instanţe ale aceleiaşi clase:
instanţele obiect vor avea date diferite, dar vor partaja
acelaşi cod corespunzător metodelor clasei din care
provin
13
3. Moștenirea
Clasa originală se numește clasa de bază sau
superclasă, iar noua clasă - clasa derivată sau sub-clasă.
Polimorfism
- static: supraîncărcarea funcțiilor (overloading)
- dinamic: existența unor funcții cu același nume și aceeași
semnătură în clase aflate într-o ierarhie de moștenire –
redefinire (overriding)
15
POO :
definire de tipuri abstracte (ADT) +
derivarea tipurilor +
[polimorfism (inclus de obicei în ADT și
derivare)]
17
Noţiuni introductive
18
Avem de-a face cu tipuri de date abstracte
19
Declararea unei clase
20
Specificator de acces
Antet clasa class <nume_clasa>
{
[private:]
Sectiune
[declaratii de date]
[declaratii de functii]
[protected:]
Sectiune
Corp clasa [declaratii de date]
[declaratii de functii]
[public:]
[declaratii de date]
Sectiune
[declaratii de functii]
} [lista de obiecte];
21
Membrii unei clase pot aparţine unor secţiuni delimitate de
etichete de acces sau specificatori de acces:
public, private şi protected
Specificatorii de acces:
permit ascunderea datelor şi metodelor
ordinea în care apar este nerelevantă
dacă nu este prezent nici un specificator de acces toate
datele membre şi toate metodele membre se consideră
că sunt private
22
Un membru care este public poate fi utilizat de orice altă
funcţie (din clasă sau din afara clasei)
Exemplu:
class Rectangle
{ Datele (campurile)
int x, y; sunt private
public:
void setVal(int, int);
Metodele sunt
int aria(void) { publice
return x*y; }
}; 24
Criterii de calitate ale unei clase (Bjarne Stroustrup)
O clasă trebuie să ofere un set mic, dar suficient și bine
definit de operații publice
O clasă trebuie să poată fi privită ca și o cutie neagră,
manevrabilă prin setul de operații
O clasă trebuie să aibă un set restrâns de date publice, pe
cât posibil, niciuna
O clasă trebuie să fie ușor modificabilă, fără ca acest lucru
să se răsfrângă asupra interfeței publice (cu excepția
extinderii acesteia)
25
Accesul la membrii unei clase
26
Accesul la membrii unei clase
27
Un obiect poate fi privit ca o generalizare a noțiunii de
variabilă, astfel încât toate aspectele referitoare la
variabile sunt păstrate:
- din punct de vedere al accesului avem obiecte
locale și globale
- din punct de vedere al alocării avem obiecte statice,
automatice și dinamice
28
Accesul din exterior la membrul public al unui obiect (dată
sau funcţie/metodă) se face cu operatorul punct(.) :
nume_obiect.data_membra
nume_obiect.metoda_membra(lista_param_actuali)
30
Definirea metodelor
31
Definirea metodelor
Dacă definirea metodei este realizată în interiorul clasei,
sintaxa definiţiei este identică cu cea a oricărei funcţii
întâlnite în programarea non-obiectuală
În acest caz avem aşa numitele metode inline pentru care
compilatorul va încerca o implementare inline:
class nume_clasa{
…
// metoda inline
tip_returnat nume_metoda (lista_parametri_formali)
{
// corp metoda
}
…
32
} [lista_de_obiecte];
O altă metodă de descriere a unei clase este de a declara
doar prototipul metodelor membre în interiorul clasei şi de a
le defini funcţionalitatea lor în exteriorul zonei de cod
alocate acesteia
În această situaţie, în momentul implementării corpului
metodei respective, trebuie să specificăm numele clasei
căreia îi aparţine metoda al cărei conţinut urmează să fie
scris
Pentru aceasta se foloseşte operatorul de rezoluţie sau
scop, simbolizat prin ::
34
int main (void){
Rectangle dr1, dr2; // declarare obiecte
dr1.setVal(4,5); // apel metoda
dr2.setVal(3,4); // apel metoda
cout<< “Aria obiectului 1: “ << dr1.aria( ); // apel metoda
cout<< “\nAria obiectului 2: “ << dr2.aria( ); // apel metoda
}
Operatorul de rezoluţie mai poate fi folosit pentru a distinge
între datele membre şi variabile locale cu acelaşi nume
dintr-un bloc (funcţie sau instrucţiune compusă) :
class Point
{
private:
int x, y;
public:
void setVal(int x, int y)
{
Point::x = x;
Point::y = y;
}
}; Parametrii
Date ale clasei metodei 36
class Access {
int nA;
int getA( ) { return nA; }
private: // redundant, specificatorul implicit e private
int nB;
int getB( ) { return nB; }
protected:
int nC;
int getC( ) { return nC; }
public:
int nD;
int getD( ) { return nD; }
};
37
int main( )
{
Access cAccess;
cAccess.nD = 5; // corect
std::cout << cAccess.getD( ); // corect
38
Pentru accesul la date putem utiliza funcții de tip accesor
(care returnează valoarea unei date membre – getter)
sau funcţii de tip mutator (care setează valoarea unei
date membre – setter)
class Date{
private:
int nMonth;
int nDay;
int nYear;
public:
// getters
int getMonth( ) { return nMonth; }
int getDay( ) { return nDay; }
int getYear( ) { return nYear; }
// setters
void setMonth(int Month) { nMonth = Month; }
void setDay(int Day) { nDay = Day; } 39
void setYear(int Year) { nYear = Year; }
};
Avantaje:
Dacă se doreşte modificarea datelor membre, se
modifică funcţiile accesor și/sau mutator şi, atât timp cât
nu se modifică specificaţiile acestora, utilizarea acestor
metode nu se modifică
41
Structuri și reuniuni în C++
42
STRUCTURI ÎN C++
În limbajul C structurile au doar date membre
În limbajul C++, o structură e asimilată unei clase, toți
membrii fiind implicit public:
o structură în C++ poate include metode
45
Autoreferinţa
47
Utilizări ale autoreferinţei :
class X{
int a; Atribut (camp)
public: al clasei
X(int a){
this->a=a; // sau X::a = a;
}
…}; Parametrul
metodei 49
EX. POINTERUL THIS
#include <iostream>
using namespace std;
class TimeClass {
int hours, minutes;
public:
TimeClass ( int new_hours, int new_minutes) // constructor
{ hours = new_hours; minutes = new_minutes; }
void setTime ( int new_hours, int new_minutes )
{ hours = new_hours; minutes = new_minutes; }
void getTime ( int ¤t_hours, int ¤t_minutes){
current_hours = hours; current_minutes = minutes; }
void copyTime( TimeClass &Time2);
};//TimeClass
50
void TimeClass::copyTime (TimeClass &Time2){
this->hours=Time2.hours;
this->minutes=Time2.minutes;};
int main(void){
TimeClass time1 (11,25), time2 (12,12),time3(1,1); // instantieri
int hrs, mins;
time1.getTime(hrs, mins);
cout<<"time1 is: " <<hrs<< ":" <<mins<<endl;
Time2.getTime(hrs, mins);
cout<<"time2 is: " <<hrs<< ":" <<mins<<endl;
time3.copyTime(time2);
time3.getTime(hrs, mins);
cout<<"\nAfter copying time2 to time3, the time is :
"<<hrs<<":"<<mins<<endl;
51
}//main
Constructori şi destructori
52
Printre funcţiile membre ale unei clase există două tipuri
particulare: constructori şi destructori
53
Teoretic constructorii și destructorii pot fi publici sau
privaţi, dar uzual se folosesc ca membri publici
54
Caracteristici comune constructori/destructori :
definirea obiectelor
class Rectangle {
int x,y;
public:
// constructor explicit vid
Rectangle(){
x=0; y=0; }
//constructor explicit cu parametri
Rectangle(int a, int b) {
x = a; y = b;
}
};
sau
class Rectangle {
int x,y;
public:
// constructor explicit cu parametri impliciti
Rectangle(int a=0, int b=0){
57
x=a; y=b; }
};
sau
class Rectangle {
int x,y;
public:
void setX(int);
void setY(int);
int aria(void) {
return x*y;
}
};
61
class Rectangle {
int x,y;
public:
// constructor explicit vid
Rectangle(){
x=0; y=0; }
//constructor explicit cu parametri
Rectangle(int a, int b) {
x = a; y = b;
}
// destructor
~Rectangle(){
cout<< " Obiectul a fost distrus! " <<endl;
}
};
62
Observații destructori:
class Rectangle {
int x,y;
public:
//constructor explicit cu parametri impliciți
Rectangle(int a=0, int b=0) {
x = a; y = b;
}
void setX(int);
void setY(int);
int aria(void) {
return x*y;
} 64
};
void Rectangle::setX(int a) {
if (a>0) x=a;
else x = 0;
}
void Rectangle::setY(int b) {
if (b>0) y=b;
else y = 0;
}
} 65
Exemplul 2:
// directive preprocesor pentru includerea bibliotecilor necesare
class Stiva
{
private:
int dim;
char *stack;
int next; // poz. urmatoare pe care se poate adauga in stiva
public:
Stiva( void ); // constr. explicit vid
Stiva( int ); // constr. cu param.
~Stiva( void ); // destructor
int push( char c );
int pop( char &c );
int isEmpty( void );
int isFull( void );
66
};
// constructori
Stiva :: Stiva(void) {
next = 0;
dim = 256;
stack = new char [dim];
}
// destructor
Stiva :: ~Stiva(void) {
delete [ ] stack;
} 67
// test stiva goala
int Stiva :: isEmpty(void) {
if (next <=0)
return 1;
else
return 0;
}
buf[0] = ‘0’;
i=0;
while(!mesaj.isEmpty( ))
mesaj.pop(buf[i++]);
cout << endl << buf << endl;
}
70
Constructorul de copiere (copy-constructor)
- Are rolul de a crea un obiect identic cu un obiect creat in
prealabil.
Considerăm clasa Stiva:
class Stiva {
private:
int dim;
char *stack;
int next;
public:
Stiva(int dim_i);
~Stiva(void);
// …
71
};
Stiva :: Stiva(int dim_i)
{
next = 0;
dim = dim_i;
stack = new char [dim];
}
74
class Stiva {
…
public:
Stiva(int dim_i=256);
~Stiva(void); Constructor de
Stiva(const Stiva &); copiere explicit
…
};
int main(void)
{
Stiva ob1(10);
Ambele instantieri
Stiva ob2(ob1); utilizeaza constructorul de
Stiva ob3=ob1; copiere
// …
}
76
Constructori cu conversie de tip
Sunt constructori care au primul parametru de tipul unui
atribut al clasei și dacă există alți parametri, aceștia
trebuie să aibă valori implicite.
class Student{
private: char nume[30];
int anul;
int nr_matricol;
public: Student(const char *n, int an=1, int nr=1234);
…
}; Valori implicite daca vrem
sa fie constructor cu
conversie de tip 77
Student::Student(char *n, int an, int nr) {
strcpy(nume, n);
anul=an;
nr_matricol=nr;
}
void main() {
Student s= "Popescu"; // instantiere ce foloseste constructorul cu
// conversie de tip
…
}
Obs. Constructorul convertește un sir de caractere (char *) într-
un obiect de tip Student
78
Concluzie: în funcție de tipul argumentelor pasate
constructorilor aceștia pot fi:
79
TIPURI DE OBIECTE
Automatice, sunt create de fiecare dată când apare o
definiție a unui obiect la execuție și sunt distruse când se
părăsește blocul unde au fost create
Statice (globale, locale), sunt create o singură dată,
când programul e pornit (în cadrul blocului pentru cele
locale) și sunt distruse la sfârșitul programului.
Dinamice, sunt create de programator în heap-ul
dinamic cu operatorul new și sunt distruse cu delete.
Derivate, aparțin claselor derivate, dar înainte se
apelează constructorul clasei de bază și apoi
constructorul din clasa derivată, dacă e necesar
Anonime, au o existență temporară, fiind necesare în
evaluarea expresiilor sau la transferul parametrilor pe
80
stivă
Alte observații legate de constructori:
82
EXEMPLE: CONSTRUCTORI EXPLICITI
#include <iostream>
using namespace std;
class C{
int x,y;
public: C(){
cout<<"\nCons. explicit fara param. Atributele x si y nedefinite \n";
cout <<"x = "<< x <<'\n';
cout <<"y= "<< y <<'\n'; }
C(int a){ x=a;
cout<<"\nCons. cu un param explicit; Atributul y nedefinit \n";
cout << "x = "<<x <<'\n';
cout << "y= "<<y << '\n'; }
C(int a, int b){ x=a;
y=b;
cout<<"\nCons. cu doi param. expliciti. Atribute definite \n";
cout <<"x = "<<x <<'\n'; 83
cout <<"y= "<< y <<'\n';
}
void mutator(int a, int b){ x=a;
y=b; }
void afis(){
cout<<"Afis. metoda"<<endl;
cout <<"x = "<< x <<'\n';
cout <<"y= "<< y <<'\n'; }
};//C class
int main(){
C ob1(10,20); //foloseste constructorul cu 2 parametri
ob1.mutator(7,7);
ob1.afis();
C ob2(10); // //foloseste constructorul cu 1 parametru
ob2.afis();
C ob3; //val nedefinita, constr. explicit fara param.
ob3.afis();
ob3.mutator(1,1);
ob3.afis(); cin.get();
}//main 84
85
CONSTRUCTORI PENTRU ALOCAREA DINAMICA A MEMORIEI
#include <iostream>
using namespace std;
const int MaxBufferSize = 1023;
class CFieldEdit {
private:
char* buffer;
int bufferSize;
public:
CFieldEdit(int fieldSize);
~CFieldEdit();
int getBufferSize() { return bufferSize; } //RetBufferSize
char* getBuffer() { return buffer; } //RetBuffer
}; //CFieldEdit Class
86
//begin constructor
CFieldEdit::CFieldEdit(int fieldSize) {
if (fieldSize > MaxBufferSize)
fieldSize = MaxBufferSize;
// daca lungimea introdusa > decat cea maxima prestabilita
// → se reduce la cea maxima prestabilita
buffer = new char[fieldSize];
bufferSize = fieldSize;
cout<<"\nAm construit un obiect de tip CFieldEdit";
} //CFieldEdit cons
//begin destructor
CFieldEdit::~CFieldEdit() {
delete[] buffer;
printf("\nAm distrus un obiect de tip CFieldEdit");
87
} //~CfieldEdit destructor
int main() {
CFieldEdit* aField;
char* str, c;
aField = new CFieldEdit(500);
str = aField->getBuffer(); // str va contine adresa buffer-ului
88
COPY-CONSTRUCTOR
#include <iostream>
using namespace std;
class ObiectSimplu {
public:
int valoare;
ObiectSimplu() {
valoare = 0;
}
ObiectSimplu(int v) {
valoare = v;
}
ObiectSimplu(const ObiectSimplu &o) { // copy-constructor
valoare = o.valoare;
} //nu e necesar, nu avem campuri ce necesita alocare dinamica
89
};//ObiectSimplu Class
class ObiectPointer {
public:
int * valoare;
ObiectPointer() {
valoare=new int;
*valoare = 0;
}
ObiectPointer(int v) {
valoare=new int;
*valoare = v;
}
};// ObiectPointer Class
Obs! Clasa nu are constructor de copiere explicit
90
class ObiectPointerCC {
public:
int * valoare;
ObiectPointerCC() {
valoare=new int;
*valoare = 0;
}
ObiectPointerCC(int v) {
valoare=new int;
*valoare = v;
}
ObiectPointerCC(const ObiectPointerCC & o) {
valoare=new int;
*valoare = *o.valoare;
} 91
};// ObiectPointerCC
int main() {
cout<<"Se creaza un obiect simplu a cu valoarea 2.\n";
ObiectSimplu a(2);
cout<<"Se copiaza a in b.\n";
ObiectSimplu b = a;
cout<<"Valoarea lui b este %d.\n", b.valoare;
cout<<"Se modifica valoarea lui a la 3.\n";
a.valoare = 3;
cout<<"Valoarea lui a este %d.\n", a.valoare;
cout<<"Valoarea lui b este %d.\n", b.valoare;
cout<<"\nSe creaza un obiect cu pointer c cu valoarea 10.\n";
ObiectPointer c(10);
cout<<"Se copiaza c in d.\n";
ObiectPointer d = c;
cout<<"Valoarea lui d este %d.\n", *d.valoare;
92
cout<<"Se modifica valoarea lui c la 13.\n";
*c.valoare = 13;
cout<<"Valoarea lui d este %d.\n", *d.valoare;
cout<<"Valoarea lui c este %d.\n", *c.valoare;
Modul 2
1
Curs 5
Argumente cu valori implicite
2
class Point
{
int x,y;
public :
Point(int a=0, int b=0) {
x=a; y=b;
}
};
…
Point p1; // echivalent cu Point(0,0);
Point p2(10); // echivalent cu Point(10,0);
Point p3(10,10);
3
Liste de iniţializare
4
Iniţializarea datelor membre se poate face:
în corpul constructorilor (prin atribuiri)
în liste de iniţializare ce apar între antetul constructorului
şi corpul acestuia :
5
Referinţe ca date membre
}
Membri constanţi
class Point {
const int x,y;
…
};
class Point {
const int x,y;
public :
Point(int a, int b) : x(a), y(b){;}
void print1(void) const;
void print2(void);
};
int main(void)
{
const Point pct(5,1);
pct.print1( );
//pct.print2( );
}
10
// Codul sursă App_Point.cpp
#include <iostream>
using namespace std;
#include "hPoint.h"
class Point {
private :
int x, y;
public:
Point( ) {x=0; y=0;}
Point(int a, int b) {x=a; y=b;}
};
class Rectangle {
Point topLeft;
Point botRight;
public:
Rectangle(int, int, int, int); 12
…
};
Constructorul clasei Rectangle trebuie să facă iniţializarea
obiectelor de tipul Point prin intermediul unei liste de
iniţializare:
13
Apelul unui constructor din alt constructor (incepand cu
C++1y)
14
int x;
double y;
public:
// constructor cu 1 param
MyClass(int a, double b) {
x=a;
y = b;
}
// constructor cu 2 param
MyClass(int c): MyClass(c, 0.) {
}
int getX( ) { return x; }
double getY( ) { return y; }
};
//main
#include <iostream>
using namespace std;
#include "Header.h"
int main( ) {
int a;
15
double b;
cout << "\nEnter an int: ";
cin >> a;
cout << "\nEnter a double: ";
cin >> b;
MyClass ob1(a, b);
cout << "\nValues of ob1 are: " << ob1.getX() << " " << ob1.getY();
cout << "\nEnter other int: ";
cin >> a;
MyClass ob2(a);
cout << "\nValues of ob2 are: " << ob2.getX() << " " << ob2.getY();
}
Tablouri de obiecte
16
Tablouri de obiecte
Observații:
pentru fiecare element din lista de iniţializare se
apelează constructorul cu parametri
dacă lista de iniţializare nu are suficiente elemente,
pentru elementele din tablou neiniţializate se apelează
constructorul fără parametri sau cel cu toti parametrii
impliciti
dacă la declarare se face inițializarea tuturor
elementelor utilizând constructorul cu parametri, atunci
constructorul fără parametri poate lipsi (excepție în
unele medii de programare)
18
Dacă constructorul poate fi apelat cu un singur parametru,
se admite o formă scurtă astfel:
19
Tablourile de obiecte pot fi create dinamic astfel:
Point *patrat = new Point[4];
…
delete [ ] patrat;
20
Rectangle.h
class Rectangle {
// membri privati
int height;
int width;
public:
// membri publici
Rectangle(int h=10, int w=10); // constructor explicit cu toti parametrii impliciti
//sau de la C++1y
// Rectangle(int h, int w);
// Rectangle()=default;
int get_area(void);
void set_values(int h, int w);
21
};
Rectangle::Rectangle(int h, int w) // constructor explicit
{
height = h;
width = w;
}
int Rectangle::get_area(void){
return height * width;
}
int main( )
{
int i;
cout << "\n\nTablou de obiecte initializat la declarare\n";
Rectangle group1[4]={
Rectangle( ), //echivalent Rectangle(10,10),
Rectangle(20,10),
Rectangle(30,10),
Rectangle(40,10)
};
23
for (i = 0 ; i < 4 ; i++)
cout << "\tAria dreptunghiului: " << group1[i].get_area() << "\n";
cout << "\n...................................\n\n";
// tablou de obiecte
Rectangle group2[4];
cout << "\nTablou de obiecte initializat cu metoda set_values()\n";
for (i = 1 ; i < 4 ; i++)
group2[i].set_values(i + 10, 10);
24
// tablou dinamic
Rectangle *group3 = new Rectangle[4];
cout << "\nTablou dinamic de obiecte initializat cu set_values()\n";
for (i = 1 ; i < 4 ; i++)
(group3+i)->set_values(i + 10, 10);
delete [ ]group3;
cout << "\n...................................\n\n";
25
// tablou dinamic
Rectangle *group4 = new Rectangle[4];
cout << "\nTablou de obiecte initializat prin constructor\n";
group4[0]=Rectangle(5,10);
group4[1]=Rectangle(15,20);
group4[2]=Rectangle(25,30);
group4[3]=Rectangle(35,40);
delete [ ]group4;
cout << "\n...................................\n\n";
cin.get();
}//main
26
27
Membri statici
28
Membrii unei clase, fie ei atribute(variabile) sau metode,
pot avea în declaraţia lor specificatorul static .
Variabile statice
Comportamentul datelor membre statice este similar cu
cel al variabilor statice obişnuite, dar primele nu sunt
accesibile decât prin intermediul clasei
Pentru datele nestatice ale unei clase există copii
distincte în fiecare obiect (valori distincte)
Datele statice există într-o singură copie:
dacă un obiect modifică o variabilă de tip static, noua
valoare va fi văzută în toate obiectele instanţiate din clasa
respectivă
29
Crearea, iniţializarea şi accesul la aceşti membri sunt
independente de celelalte obiecte ale clasei, motiv pentru
care se mai numesc şi variabile de clasă
Membrii statici ai unei clase pot fi declaraţi în oricare
secţiune (private, public sau protected), iar funcţiile
membre au acces la membrii statici la fel ca la oricare
membru
Un membru static poate fi referit din exterior astfel:
indicând numele clasei şi folosind operatorul de rezoluţie “::”,
chiar dacă nu există obiecte instanţiate ale clasei
specificând un obiect al clasei şi folosind un operator de
acces adecvat ( “.” pentru accesul prin obiect, respectiv “->”
pentru accesul prin pointer)
30
Variabilele statice trebuie redeclarate în exteriorul clasei,
în caz contrar, compilatorul va afişa un mesaj de eroare:
acest lucru se datorează faptului că în momentul
declarării unei variabile statice în interiorul clasei,
acesteia nu îi este alocată memorie, operaţia
efectuându-se prin redeclararea acesteia în exterior,
cu specificarea clasei de care aparţine respectiva
variabilă
în cadrul redeclarării, nu mai apare cuvântul cheie
static
31
class MyClass {
int x;
public:
static int n;
MyClass (int v) {
cout<<"\nApel constructor cu valoarea: "<<v<<"\n";
x = v;
n++;
}
// metoda de afisare (nerecomandat), de regula folosim getteri pentru accesul
// la date (vedeti exemplul urmator) → slide 30
void showVal(void) {
cout << "Date membre: "<< "x = " << x;
cout << ", n = " << n << "\n";
}
~MyClass( ) {
n--;
cout<<"\nApel destructor: n = "<<n<<"\n";
} 32
};
int MyClass::n; // variabila statică va fi astfel vizibilă
int main( ) {
cout<<"Acces prin numele clasei: n = "<< MyClass::n <<"\n";
MyClass a(3);
a.showVal();
cout<<"\nAcces prin obiectul a: n = "<< a.n <<"\n";
MyClass b(5);
b.showVal();
cout<<"\nAcces prin numele clasei: n = "<<MyClass::n<<"\n";
}
33
Metode statice
Pot exista metode precedate de specificatorul static
Caracteristici:
au acces doar la alţi membri de tip static ai clasei şi
bineînţeles, pot lucra cu membri globali
nu pot avea pointeri this
aceleiaşi metode
class MyClass {
int x;
static int y; //atribut privat
public:
35
void setX(int a){
x = a; }
static void setY(int b) {
y = b; }
int getX( ) {
return x; }
static int getY( ) { pentru acces la atribut privat
return y;
}
};
36
}
Domeniu de vizibilitate pentru clase
Clase interioare (locale, imbricate)
37
O clasă introduce un domeniu de vizibilitate la fel ca o
funcţie sau un bloc
38
Entităţile externe pot fi accesate folosind operatorul de
rezoluţie astfel:
int x;
class Point {
int x,y;
public:
Point(void);
…
};
Point::Point(int x) {
x=::x;
39
}
Declaraţia unei clase poate apărea în următoarele
ipostaze :
40
Clasele imbricate se folosesc atunci când o clasă
este utilizată doar de o altă clasă:
43
Clasele locale se folosesc atunci când acestea sunt
folosite numai în interiorul unui bloc (funcţie sau
instrucţiune compusă):
44
// Membrii statici și clasele locale
#include <iostream>
using namespace std;
45
// clasele locale NU POT avea membri statici
void f() {
class Local {
public:
//! static int i; // Eroare
// Cum poate fi definit i ?
} x;
int main() {
//Outer x;
f(); 46
} // main
#include<iostream>
using namespace std;
int x; // variabile globale
void f() { // definirea funcției
static int y; // variabila statică y poate fi utilizată de clasa locală
// int x; // variabila auto x nu poate fi utilizată de către clasa locală
extern int g(); // funcția externă g poate fi utilizată de clasa locală
class Local { // clasa locală
int g() {cout<<"\nextern local"; return 1;
//return x; // error, local variable x cannot be used by g()
} //g
public: int h() { return y; } // ok, y variabilă statică din blocul extern
int k() { return ::x; } // ok, x- variabilă globală
int l() { return g(); } // ok, g() este funcție externă
}; // class Local
Local ob;
cout<< ob.l(); 47
} // f()
Funcții prietene
Clase prietene
48
Funcţia prietenă nu este membră a clasei, dar are acces
la toţi membrii clasei în care este declarată
Declaraţia unei funcţii prietene se face în interiorul clasei
folosind cuvântul cheie friend
49
Exemplu: funcție friend independentă
class MyClass {
private: int m, n;
public:
friend float media(MyClass x);
void init(int x, int y) {
m = x;
n = y;
}
};
class Y {
public:
float media(X x) {
// corp metoda
}
…
};
class X {
private: int m, n;
public:
friend float Y::media(X x);
51
};
FUNCTII FRIEND, STRUCTURI ȘI CLASE
a) structura
struct Complex{
double re;
double im;
};
double modul (Complex *z){
return sqrt(z->re * z->re + z->im * z->im);
}
apel:
Complex z1 = {1,1};
double d = modul (&z1);
52
b) Metode membre în clase
class Complex{
double re, im;
public:
Complex(double x=0, double y=0) {
re = x;
im = y; }
double modul(){
return sqrt(re*re + im*im); }
...
}; //Complex class
apel:
Complex z1(1,1); 53
double d = z1.modul();
c) Functii friend în clase
class Complex{
double re, im;
public:
Complex(double x=0, double y=0) {
re = x;
im = y; }
friend double modul(Complex *z);
...
}; //class
double modul (Complex *z){
return sqrt(z->re*z->re + z->im*z->im);}
apel:
Complex z1(1,1); 54
55
Clase prietene
class Rectangle
{
int x, y;
public :
int aria(void) {
return x*y;
}
int setVal(int, int);
void convert(Square);
56
};
class Square
{
private :
int x;
public :
void setVal(int a) {
x=a;
}
friend class Rectangle; //Rectangle e friend cu Square
};
void Rectangle::convert(Square a)
{
x = a.x;
y = a.x;
} 57
Intrebari:
- Ce este o lista de initializare? Cand trebuie folosita?
- Ce este o clasa interioara (imbricata)? Cum
instantiem obiecte dintr-o clasa interioara?
- Ce este o clasa locala? Cum poate fi utilizata?
- Ce intelegeti printr-un atribut static al unei clase?
- Este suficient sa declaram un atribut static in clasa
pentru a putea fi folosit?
- Cum poate fi accesat din afara clasei un membru
static public?
- O metoda membra statica are access la toti membrii
clasei?
58
Intrebari
- Ce este o functie prietena? Cum se declara? Cum
poate accesa membri ai clasei pnetru care e prietena?
- O functie prietena unei clase poate fi fara parametri?
- O functie prietena unei clase poate fi declarata in
orice sectiune a clasei?
- Ce este o clasa prietena?
59
PROGRAMAREA
CALCULATOARELOR 2
Curs 6-7
1
Supraîncărcarea metodelor și
operatorilor
SUPRAÎNCĂRCAREA FUNCŢIILOR ȘI METODELOR
În general, se consideră că metodele aparțin claselor și
funcțiile sunt exterioare claselor, în limbajul C++, deci
tratarea lor se va face ca atare.
3
Compilatorul nu face distincţie între un parametru
transferat prin valoare şi unul transmis prin referinţă. În
acest caz trebuie utilizat un parametru suplimentar,
redundant (ce nu va fi folosit de funcţie):
5
Supraîncărcări de funcţii şi ambiguităţi
Ambiguităţile sunt legate de conversia automată a tipului
în C++:
float myFunc(float i);
double myFunc(double i);
Apel:
cout<< myFunc(1.9); // fara ambiguitati – double
Cout<<myFunc(1.9f); // fara ambiguitati - float
cout<< myFunc(10); // cu ambiguitati
6
Metodele unei clase pot fi supraîncărcate la fel ca orice
funcţie
Uzual supraîncărcarea se foloseşte în cazul
constructorilor:
de obicei, o metodă constructor este folosită pentru a
iniţializa anumite date din cadrul clasei
supraîncărcarea permite lucrul cu mai multe tipuri de date,
ce necesită iniţializări distincte
Noţiuni introductive
Supraîncărcarea operatorilor presupune atribuirea de noi
sensuri operatorilor, care să extindă aria de aplicabilitate a
lor și pentru alte tipuri de date, inclusiv obiecte din clase.
Să considerăm că avem o clasă Complex cu metode ce
realizează operațiile de bază și dorim să efectuăm operații
cu numere complexe :
Complex a, b, c, d, e;
e = mulcomplex(adcomplex(&a, &b), sccomplex(&c, &d));
8
Am putea scrie prin supraîncărcarea operatorilor pentru
obiecte de tip Complex, mai simplu, astfel:
e = (a+b)*(c-d) ;
9
Nu pot fi supraîncărcaţi operatorii:
. (punct)
:: (rezoluţie)
?: (condiţional)
.* (indirectare) (operator doar in C++)
sizeof
Operatori ce pot fi supraîncărcaţi:
prioritatea
asociativitatea
tipul clasei
12
Operatorii ->, =, [ ] şi ( ) și cei compuși pot fi
supraîncărcaţi numai cu metode membre
Forma generală:
tip_returnat Nume_clasa::operator#(lista_argumente)
{
// operatii
}
semnul # este o rezervare de loc pentru operatorul care
urmează să fie supraîncărcat 14
class Complex {
float re, im;
public:
Complex(float x=0, float y=0) {
re = x;
im = y;
}
void showVal(void) {
cout << "Date membre: "<< “re = " << re;
cout << ", im = " << im << "\n";
}
Forma generală:
friend tip_returnat operator#(lista_argumente) ;
void showVal(void) {
cout << "Date membre: "<< “re = " << re;
cout << ", im = " << im << "\n";
}
28
Expresie Operator Metoda membră Funcţie globală
@A + - * & ! ~ ++ -- A::operator@() operator@(A)
A@ ++ -- A::operator@(int) operator@(A,int)
+ - * / % ^ & | < >
A@B == != <= >= << >> A::operator@ (B) operator@(A,B)
&& || ,
= += -= *= /= %= ^=
A@B A::operator@ (B) -
&= |= <<= >>= []
A(B,C...) () A::operator( ) (B, C...) -
A->x -> A::operator->( ) -
30
Utilizatorul poate în anumite cazuri să impună realizarea
unor conversii cu operatorul cast. El poate fi utilizat în
C++ astfel:
(tip_cast) expresie
sau
tip_cast (expresie)
31
De exemplu: să presupunem că dorim să putem aduna 2
obiecte de tip Complex, dar și un obiect de tip Complex
cu un număr întreg
class Complex {
…
public:
friend Complex operator+(Complex , Complex );
friend Complex operator+(Complex , int);
friend Complex operator+(int, Complex);
};
class Complex {
…
public:
Complex(int x) {
re =x; im = 0;
}
friend Complex operator+( Complex c1, Complex c2);
};
34
Ca urmare este suficientă o singură funcţie operator şi
sunt posibile expresii care implică operanzi de tipul
Complex şi operanzi întregi :
…
Complex z(5,6), q, v;
q= z + 5; // echivalenta cu: q = operator+(z, Complex(5));
v=3+z; // echivalenta cu: v = operator+(Complex(3),z);
35
2. Altă variantă - utilizarea unei funcţii de conversie:
operator nume_tip ( ) {
// corp metoda
}
O astfel de funcţie va converti un obiect al clasei către
nume_tip în cursul unei conversii explicite (cast).
36
class Point {
private:
int x, y;
public:
Point (int x, int y) { Point::x = x; Point::y = y; }
Point (int x) { Point::x = Point::y = x; } // constructor de conversie de tip
int main ( ){
Point p(10,10), q(20,20);
(p+q).print( );
cout<<'\n';
(p + 10).print( ); // echivalent cu operator+(p, Point(10))
cout<<'\n';
Rectangle r(10,10,20,30);
q =r + p; // echivalent cu operator+((Point)r, p) 39
q.print( );
}
Concluzie: utilizarea obiectelor duce la situații în care
sunt necesare conversii. Aceste conversii nu pot fi
aplicate automat, spre deosebire de cazul tipurilor
predefinite.
40
a) Conversia din tip predefinit -> în tip abstract
Se realizează în mod automat prin apelul unui
constructor de conversie adecvat. Constructorul va avea
ca parametru tipul predefinit și eventual și alti parametri
care trebuie să fie toți impliciți.
50
// supraincarcarea operatorilor relationali: compara obiectul curent cu un alt obiect
52
int strsize(void)
{ return strlen(p); }
char* makestr(char *s)
{ return strcpy(p, s); }
operator char *(void)
{ return p; }
}; // clasa String
}
Supraîncărcarea operatorului de indexare [ ]
class Dictionar {
assoc *dict;
int dim; // dimensiunea dictionarului
int next; // urmatoarea pozitie libera in dictionar
public:
Dictionar(int size) {
dim = size;
next = -1;
dict = new assoc[size];
}
57
char * Dictionar::operator [ ](const char *pCuv) {
assoc *pAssoc;
for(pAssoc = &dict[next-1]; dict <= pAssoc; pAssoc--)
if(strcmp(pCuv, pAssoc->cuvant) == 0)
return pAssoc->definitie;
return 0;
}
int main( ) {
int size = 10;
Dictionar dict(size);
dict.addWord("Masina", "Definitia 1");
dict.addWord("Copac", "Definitia 2");
dict.addWord("Animal", "Definitia 3");
dict.addWord("Student", "Definitia 4");
cout << dict["Animal"]<<endl;
}
58
Exemplul 2: clasa Analize
class Analize;
class Pers
{
char nume[20];
double greutate;
int varsta;
friend Analize;
public:
void tip();
};// Pers
59
class Analize {
Pers *sir;
int n;
public:
Analize()
{
n=5;
sir= new Pers[5];
}
Analize(int nr)
{
n=nr;
sir= new Pers[n];
}
//Supraincarcare []
Pers* operator[](char *);
Pers* operator[](double);
Pers* operator[](int);
void introdu(); // functie pentru citire date persoane– trebuie definita 60
};//Analize
//Indexare dupa nume
Pers* Analize:: operator[](char *nume) {
for(int i=0;i<n;i++)
if(strcmp(sir[i].nume,nume)==0)return &sir[i];
return NULL;
}//op[]nume
62
Exemplul 3: BitVec
#include <iostream>
using namespace std;
typedef unsigned char uchar;
class BitVec {
uchar *vec;
short bytes;
public:
BitVec (const short dim);
BitVec (const char* bits);
BitVec (const BitVec&);
~BitVec (void) { delete [ ] vec; }
BitVec& operator = (const BitVec&);
int operator [ ] (const short idx);
void print (void); 63
};//BitVec class
BitVec::BitVec (const char *bits){ // 1011011101111
int len = strlen(bits);
bytes = len / 8 + (len % 8 == 0 ? 0 : 1); Ex: len=13 -> bytes=2
vec = new uchar[bytes]; vec[1] | (1<< 4) ->00010000
vec[1] | (1<< 2) ->00010000 | 000001
for (int i = 0; i < bytes; ++i)
00010100
vec[i] = 0; vec[1] | (1<< 1) -> 00010100 | 000000
for (int i = len - 1; i >= 0; --i) 00010110
if (*bits++ == '1') vec[i/8] |= (1 << (i%8)); }
BitVec::BitVec (const short dim){
bytes = dim / 8 + (dim % 8 == 0 ? 0 : 1);
vec = new uchar[bytes];
for (int i = 0; i < bytes; ++i)
vec[i] = 0; }
BitVec::BitVec (const BitVec &v){ bytes = v.bytes;
vec = new uchar[bytes];
64
for (int i = 0; i < bytes; ++i)
vec[i] = v.vec[i]; }
BitVec& BitVec::operator= (const BitVec &v){
int i;
if (this != &v) {
for (i = 0; i < (v.bytes < bytes ? v.bytes : bytes); ++i)
vec[i] = v.vec[i];
for (; i < bytes; ++i)
vec[i] = 0; Exemplu: idx=18
} vec[2] & (1 << 2) ? 1 : 0
return *this; -> se returneaza al 3-lea bit (de
la dreapta) din al 3-lea octet
}
inline int BitVec::operator [ ] (const short idx){
return vec[idx/8] & (1 << idx%8) ? 1 : 0; }
void BitVec::print(void){
cout <<"\nOctetii sunt in hexa: "<<endl;
for(int i=0; i<bytes; i++)
65
cout <<"\nOctet: "<< (i+1)<< hex <<": "<< (int)vec[i] << endl; }
int main (void){
BitVec v("1011011101111");
v.print( );
cout << endl << "\nBitii sunt: " << dec << endl;
for(int i=0; i< 16; i++)
cout << "Bit " << (i+1) << ": " << v[i] << endl;
BitVec vv(16);
cout<<"\n Noul vector initial este:";
vv.print();
vv=v;
cout<<"\n Noul vector dupa asignare este:";
vv.print();
cout << endl << "\nBitii sunt: " << dec << endl;
for(int i=0; i< 16; i++)
cout << "Bit " << (i+1) << ": " << vv[i] << endl;
66
}//main
Supraîncărcarea operatorului apel de funcţie ( ) -
iteratori
69
int& Matrix::operator( ) (const short r, const short c) {
if((r >= 0 && r< rows)&&(c >= 0 && c < cols))
return elems[r *cols + c];
else{
cout<<"\nIndici eronati! Oprire program! \n";
exit(0);
}
}
Matrix n=m;
cout<<endl;
for(i=0; i<2; i++) {
cout<<endl;
for(j=0; j<3; j++)
cout<<n(i, j)<<"\t";
71
}
}
SUPRAÎNCĂRCAREA OPERATORILOR DE INCREMENTARE/
DECREMENTARE (++/ --)
Operatori unari, pot fi prefixati și postfixati.
În cazul postfixat se adaugă un parametru redundant de tip int,
pentru a distinge semnăturile
Operațiile se fac în obiectul curent, care va fi returnat de funcția
operator
class Time{
private:
int hh; // ora
int mm; // minut
public:
Time(int m) {
if (m >= 0) {
hh = m / 60; // nr ore
mm = m % 60; // nr minute
}
else {
hh = mm = 0;
}
}
73
Time(int h = 0, int m = 0) {
if (((h >= 0) && (h < 24)) && ((m >= 0) && (m < 60))){
hh = h;
mm = m;
}
else { hh = mm = 0; }
} //Time
int getH(){
return hh;
}
void setH(int h) {
if ((h >= 0) && (h < 24))
hh = h;
else
hh = 0; }
int getM(){ 74
return mm;
}
void setM(int m) {
if ((m >= 0) && (m < 60))
mm = m;
else
mm = 0;
}
void show() {
cout << hh << ":" << mm << endl;
}
// supraincarcare operator de incrementare prefixata
Time& operator++() {
mm++;
if (mm == 60) {
hh++;
if (hh == 24)
hh = 0;
mm = 0;
}
75
return *this;
}
// supraincarcare operator de incrementare postfixata
Time operator++(int) {
Time temp = *this;
++(*this);
return temp;
}
// supraincarcare operator de decrementare prefixata
Time& operator--() {
if (mm == 0) {
hh--;
if (hh < 0) hh = 23;
mm = 59;
}
else
mm--;
return *this;
76
}
// supraincarcare operator de decrementare postfixata
Time operator--(int) {
Time temp = *this;
--(*this);
return temp;
}
77
// supraincarcare operator de atribuire compusa -=
Time& operator-=(int min) {
int h1, m1;
h1 = min / 60;
m1 = min % 60;
hh = hh - h1;
if (mm < m1) hh--;
if (hh < 0) hh = 24 + hh;
mm = mm - m1;
if (mm < 0) mm = 60 + mm;
return *this;
}
// supraincarcare operator de adunare +
Time operator+(Time tm) {
Time rez;
int nm = mm + tm.mm;
rez.hh = hh + tm.hh + nm / 60;
if (rez.hh > 23) rez.hh = rez.hh - 24;
rez.mm = nm % 60;
78
return rez;
}
// supraincarcare operator de scadere-
Time operator-(Time tm){
Time rez;
if (mm < tm.mm)
{
rez.hh = hh - tm.hh - 1;
rez.mm = 60 - (tm.mm - mm);
}
else
{
rez.hh = hh - tm.hh;
rez.mm = mm - tm.mm;
}
if (rez.hh < 0)
rez.hh = 24 + rez.hh;
return rez;
}
};
79
int main(){
Time tm1(1, 29);
cout << "Time 1: ";
tm1.show();
cout << endl;
Time tm2;
tm2 = --tm1;
cout << "Time 1 (tm2 = --tm1): ";
tm1.show();
cout << "Time 2 (tm2 = --tm1): ";
tm2.show();
cout << endl;
tm1 = ++tm2;
cout << "Time 1: (tm1 = ++tm2)";
tm1.show();
cout << "Time 2: (tm1 = ++tm2)";
tm2.show();
80
cout << endl;
tm1 = tm2++;
cout << "Time 1 (tm1 = tm2++): ";
tm1.show();
cout << "Time 2 (tm1 = tm2++): ";
tm2.show();
tm2--;
cout << "Time 2 (tm2--): ";
tm2.show();
81
cout << endl;
tm3 += 75;
cout << "Time 3 (+=75): ";
tm3.show();
tm3 -= 86;
cout << "Time 3 (-=86): ";
tm3.show();
}
82
Supraîncărcarea operatorilor new şi delete
83
Sintaxa generală de supraîncărcare locală (sau utilizator) a
acestor operatori cu metode statice:
new Nume_clasa ;
sau:
new Nume_clasa(par1, par2, …, parn) ;
85
Operatorii new şi delete au însă o particularitate:
au o supraîncărcare globală (standard sau predefinită)
oferită de limbaj şi pentru tipurile abstracte
pot avea o supraîncărcare locală unei clase
86
Supraîncărcarea globală permite alocarea tablourilor de
obiecte, pe când cea locală nu
În cazul în care se doreşte alocarea/dealocarea specială
de memorie pentru tablouri, se folosesc următoarele
sintaxe:
void *operator new[ ](size_t dim)
{
… // operatii de alocare de memorie
return pointer_la_noua_zona_de_memorie;
}
class Point {
double x, y;
public:
Point() { x = y = 0.0; }
Point(double x, double y) {
this->x = x;
this->y = y;
}
int getX() { return x; }
int getY() { return y; }
void setX(int x) { this->x = x; }
void setY(int y) { this->y = y; }
void * operator new(size_t size);
void operator delete(void * ptr);
void * operator new[](size_t size);
void operator delete[](void * ptr); 90
};
void* Point::operator new(size_t size){
void * ptr= malloc(size);
cout << “Utilizare operator new supraincarcat."<<endl;
return ptr;
}
void Point::operator delete(void * ptr){
cout << “Eliberare memorie cu operatorul delete supraincarcat" << endl;
free(ptr);
}
92
// utilizarea functiei operator globale operator new[]() pentru tipul float
93
Intrebări
- Ce operatori nu pot fi supraincarcati in C++?
- Cum pot fi supraincarcati operatorii in C++?
- Cum se face supraincarcarea cu metode membre?
- Cum se face supraincarcarea cu functii friend?
- Ce operatori pot fi supraincarcati doar cu metode membre?
- Metoda de supraincarcare a operatorului = se mosteneste intr-
un proces de derivare a claselor?
- Cand este necesara supraincarcarea operatorului =?
- Cum se face supraincarcarea operatorului =?
- Cum se realizeaza supraincarcarea operatorilor [] si ()?
- Cum se realizeaza supraincarcarea operatorilor new si delete?
Variantele supraincarcate pot fi folosite pentru alocarea
dinamica a tablourilor?
- Cum se face supraincarcarea operatorilor ++ si – prefixati si
94
postfixati?
PROGRAMAREA CALCULATOARELOR-
ALGORITMI
MODUL 2
CURS 8
CUPRINS
1. Moştenirea
• Noţiuni introductive
• Moştenirea simplă
• Moştenirea multiplă
• Constructori şi destructori pentru clasa derivată
2. Clase virtuale
3. Metode virtuale
4. Clase abstracte
5. Destructori virtuali
2
1. MOŞTENIREA
Noţiuni introductive
➢ Abstractizare
➢ Polimorfism
➢ Moștenire
4
CLASE ȘI MOȘTENIREA
Relații între clase
Intre clase putem avea următoarele relații:
A) asociere
B) dependență
C) agregare
D) moștenire
A) Asociere – relație între două sau mai multe clase care modelează
o interdependență între obiectele instanțiate din aceste clase;
Se folosesc: numele asocierii, săgeți de navigare, roluri, indicatori
de multiplicitate într-o reprezentare UML;
Implementarea e realizată prin: pointeri la obiecte sau tablouri de
pointeri la obiecte, sau prin asocieri abstracte în clase distincte cu
atribute și comportament propriu, printr-o implementare sub forma
unui dicționar, care e o asociere multiplicativă de tipul n ... m
5
implementată cu tablouri de hashing.
• Asocierea este o relație între 2 clase bazată pe referință.
Clasa A va conține o referință la clasa B. Asocierea poate fi
reprezentată printr-o linie între aceste clase, săgeata
indicând direcția de navigare. Dacă săgeţile sunt prezente la
ambele capete, asocierea permite navigarea în ambele
sensuri.
7
C) AGREGAREA
Caracterizată prin proprietatea a avea (to have). E o relație între
două sau mai multe clase astfel încât un obiect al unei clase
agregate devine parte constituentă a unei clase care a generat
agregarea.
Putem avea o agregare simplă sau una compusă
Agregarea simplă este la fel ca asocierea şi vazută deseori ca
o relaţie redundantă. O percepţie comună este că agegarea
simplă reprezintă relaţii de tipul one-to-many / many-to-many /
part-whole, care pot fi reprezentate şi prin asocieri (de aici si
redundanţa). In UML nu există o reprezentare diferită pentru ea,
unii developeri utilizează un romb gol pentru agregarea simplă.
8
class Asset { ... };
class Player {
List assets;
public void AddAsset(Asset newlyPurchasedAsset) {
assets.Add(newlyPurchasedAssest); ... }
...
};
9
AGREGAREA COMPUSĂ
În acest caz într-o clasă se instantiază un obiect dintr-
o altă clasa. Când o clasă B este instanţiată într-o
clasă A, clasa A controlează crearea şi durata
existenţei instanţelor clasei B. Când instanţa clasei A
este distrusă, la fel va fi şi instanţa clasei B.
O astfel de relaţie se reprezintă printr-o linie ce uneşte
cele 2 clase pe care este figurat un romb plin spre
clasa care deţine responsabilitatea creării obiectelor.
O astfel de relaţie nu este implementată prin clase
imbricate. Clasa care este instanţiată în altă clasă
poate fi utilizată şi de alte părţi ale aplicaţiei.
10
public class Piece { ... };
public class Player
{
Piece piece = new Piece(); /*Player owns the
responsibility of creating the Piece*/
...
};
11
D) Moștenirea - caracterizată prin proprietatea a fi (to
be). E folosită pentru a modela similaritățile și diferențele
dintre clase. Expresia, e un fel de ( is_a_kind_of ) e
folosită la nivelul clasei și e un (is_a) la nivelul
obiectelor. Doar având moștenire considerăm că avem
POO.
Avem mai multe tipuri de moștenire, nu toate disponibile
în orice limbaj OO.
12
TIPURI DE MOȘTENIRE
-prin specializare, subclasa este o varietate specializată
a clasei de bază
-prin specificare, clasa de bază defineste doar
comportamentul (interfeţe sau clase abstracte) care este
implementat în subclase
-prin construcţie, subclasa foloseste comportamentul
clasei de bază nefiind un subtip al ei (Stiva construită cu
Vector)
-prin extindere, subclasa adaugă noi functionalităţi clasei
de bază, dar nu va modifica nici un element moştenit
-prin limitare, subclasa limitează comportamentul clasei
de bază, fiind utilizată destul de rar
-prin combinare, când de fapt avem moştenire multiplă,
bazată pe mai multe clase de bază. 13
Clasa CPolygon conţine membri ce sunt comuni
celorlalte două clase
Clasele CRectangle şi CTriangle vor conţine și membri
ce corespund unor caracteristici specifice
14
Clasa de bază nu este afectată de crearea unei clase
derivate din ea şi nu trebuie recompilată
16
Moştenirea simplă
18
Ce poate adăuga o clasă derivată:
date membre noi
metode membre noi
constructori
destructor
funcţii prietene
Specificatorul de acces dă măsura în care clasa derivată
poate accesa membrii moşteniţi din clasa de bază
Tipuri de moştenire
Moştenire
Clasa de bază
public protected private
class Baza
{
protected:
int i, j;
public:
void setI(int a) { I = a; }
void setJ(int b) { j = b; }
void afiseaza( ) {
cout << i << ", " << j << " \n ";
}
};//Baza
21
class Derivata : public Baza {
public:
int inmulteste( ) {
return i * j; // corect, i si j raman protected
}
};//Derivata
int main(void)
{
Derivata obiect_derivat;
//obiect_derivat.i = 5; // gresit, i ramane protected
obiect_derivat.setI(12); //din Baza
obiect_derivat.setJ(17); // din Baza
obiect_derivat.afiseaza( ); // din Baza
cout<< "\n " este: " <<obiect_derivat.inmulteste( );
22
}
Moştenirea protected
class Baza
{
int x;
protected:
int y;
public:
int z;
Baza(int x=1, int y=1 ) {
this->x=x;
this->y=y;
}
void arata( ) {
cout << "\n --------Clasa de baza------";
cout << "\n Valoarea variabilei private x: "<<x;
cout << "\n Valoarea variabilei publice y: "<<y;
} 23
};//Baza
class Derivata: protected Baza
{
public:
void do_this(void) {
//x = 5; // gresit, x este private in Baza
y = 7; // corect, y este protected in Baza, ramane protected
z = 15; // corect, z este public in Baza, devine protected
}
};
int main(void){
Baza ob1;
Derivata ob2;
ob1.arata( ); // public in Baza cout<<ob1.z;
ob2.do_this( );
//ob2.arata( ); // gresit, arata( ) devine protected in Derivata
24
}
Moştenirea private
class Baza
{
protected: int a, b;
public:
int setA(int a){ this->a=a;}
int setB(int b){this->b=b;}
int aduna() {
return a+b;
}
int scade() {
return a-b;
}
void afis_baza( ) {
cout << a << " " << b << "\n";
} 25
};
class Derivata : private Baza
{
public:
int inmulteste() {
return a*b;
}
};
int main(void){
Baza obiect_baza;
obiect_baza.setA(1);
obiect_baza.setB(2);
Derivata obiect_derivat;
cout<< " \nProdusul este= " <<obiect_derivat.inmulteste();
//obiect_derivat.aduna(); // eroare, devine private
} 26
Este posibil ca membrii clasei de bază să fie exceptaţi
individual de la modul de acces stabilit prin declaraţia
clasei derivate, astfel încât să-şi păstreze atributele din
clasa de bază:
28
Exemplul 1:
class Baza1 { class Baza2 {
protected: protected:
int x; int y;
public: public:
void afiseaza_x( ) { void afiseaza_y( ) {
cout << " valoarea lui y
cout << " valoarea lui x este:
este: " << y <<" \n ";
“ << x <<”\n ";
}
}
void afis( ) {
void afis( ) { cout << " y = " << y << " \n
cout << " x = “ << x << " \n "; ";
} }
};//Baza1 };//Baza2
29
class Derivata: public Baza1, public Baza2 {
public:
void initializeaza(int i, int j) {
x = i;
y = j;
}
void afis( ) {
Baza1::afis( );
Baza2::afis( );
}
};
int main(void){
Derivata obiect_derivat;
obiect_derivat.initializeaza(100, 200);
obiect_derivat.afiseaza_x( ); // metoda mostenita din Baza1
obiect_derivat.afiseaza_y( ); // metoda mostenita din Baza2
obiect_derivat.afis(); // metoda din clasa Derivata
30
}
Observații:
31
În cazul moştenirii multiple, dacă clasele de bază conţin
membri cu acelaşi nume, referirea acestora printr-un obiect
din clasa derivată poate conduce la ambiguităţi:
pentru evitarea acestora se utilizează calificarea
completă
obiect_clasa_derivata.Nume_clasa_baza::nume_membru
Exemplu : dacă în exemplul anterior în clasa derivată nu
avem funcția afis(), printr-un obiect din clasa derivată
putem apela una din metodele afis() din clasele de bază.
obiect_derivat.afis( ); // ambiguitate
…
obiect_derivat.Baza1::afis( ); //corect
32
Exemplul 2:
class CPolygon
{
protected:
int width, height;
public:
void set_values (int a, int b) {
width=a; height=b;
} // scop didactic, se recomanda setteri individuali
};
class COutput
{
public:
void output (float i) {
cout << i << endl;
33
}
};
class CRectangle: public CPolygon, public COutput
{
public: int area ( ) { return (width * height); }
};
int main ( ) {
CRectangle re;
CTriangle trgl;
re.set_values (4,5); // metoda mostenita din CPolygon
trgl.set_values (4,5);
re.output (re.area( )); // metoda mostenita din COutput
trgl.output (trgl.area( ));
34
}
Constructori şi destructori pentru clasa derivată
36
class A {
// corp clasa
};
class B : public A {
// corp clasa
};
class C : public B {
// corp clasa
};
43
Copy-constructorul în clasa derivată
44
În cazul moştenirii multiple:
ordinea în care sunt apelaţi constructorii claselor de
bază este aceeaşi cu ordinea specificării claselor de
bază în declaraţia clasei derivate
această ordine poate diferi de ordinea specificată în lista de
iniţializare din constructorul clasei derivate
În general, ordinea operaţiilor în cazul creării obiectelor
derivate este următoarea:
se apelează constructorii claselor de bază în ordinea
precizată în declaraţia clasei derivate
se apelează constructorii altor obiecte membre din
clasa derivată, în ordinea declarării lor în clasa
derivată
se apelează constructorii clasei derivate
La distrugerea obiectelor derivate, operaţiile se
desfăşoară în ordine inversă 45
2. CLASE VIRTUALE
Considerăm următoarea situaţie:
class BB {
protected : int x;
BB
…
};
class B1 : public BB {
// corp clasa B1
}; B2
class B2 : public BB {
// corp clasa
};
class D : public B1, public B2 { D
// corp clasa
};
49
Dacă într-o ierarhie de clase, unele instanţe ale clasei de
bază sunt declarate virtuale, iar altele sunt declarate non-
virtuale, atunci obiectele claselor derivate vor conţine câte
un obiect al clasei de bază pentru fiecare instanţă non-
virtuală şi un singur obiect al clasei de bază pentru toate
instanţele virtuale
50
Constructorul clasei derivate D trebuie să precizeze
transferul de date către constructorul clasei de bază BB
pentru crearea unei copii unice a obiectului BB:
D::D(lista_param) : B1(lista_param), B2(lista_param),
BB(lista_param) {…}
class Person {
private:
string ptype;
string name;
public:
Person( const string & t, const string & n ) : ptype( t ), name( n ){ }
virtual ~Person( ) { }
const string& getName( ) {
return name;}
const string getPtype( ) {
return ptype; }
};//Person class
52
class Student : virtual public Person {
private:
int hours;
public:
Student( const string& n, int h ) : Person( "Student", n ), hours(h) { }
int getCreditHours( ) {
return hours;
}
}; //Student class
}//main
int getCreditHours( ) {
return hours;
}
};
54
class Employee : virtual public Person
{
private:
int hours;
public:
Employee( const char n[], int h ) : Person( "Employee", n), hours(h)
{}
int getVacationHours( ) {
return hours;
}
};
55
class StudentEmployee : public Student, public Employee
{
public:
StudentEmployee( const char n[], int ch, int vh ) :
Person( "StudentEmployee", n ),
Student( "ignored", ch ),
Employee( "ignored", vh )
{}
};
int main( ){
StudentEmployee se( “Popescu", 120, 15 );
63
EXEMPLU:
#include <iostream>
using namespace std;
class CPolygon {
protected:
int width, height;
public:
void set_values (int a, int b) {
width=a; height=b; }
int getWidth() { return width;}
int getHeight() { return height; }
virtual float area(void) {return 0; }
};//CPolygon class
66
Când un astfel de pointer indică spre un obiect derivat ce
conţine o metodă virtuală, compilatorul C++ determină
care versiune a metodei va fi apelată, în funcţie de tipul
obiectului spre care indică acel pointer
68
4. CLASE ABSTRACTE
Sunt clase care nu sunt utilizate direct, ci furnizează un
schelet pentru alte clase ce vor fi derivate din acestea
De obicei, toate metodele membre ale unei clase abstracte
sunt virtuale şi au implementări vide urmând să fie redefinite
în clasele derivate
70
class CPolygon {
protected:
int width, height;
public:
void set_values (int a, int b) {
width=a; height=b;
}
int main( ){
Base *Var = new Derived( );
// alte prelucrari
delete Var; 75
}
76
Exemplul 2:
enum Color {Co_red, Co_green, Co_blue};
CURS 9
CUPRINS
1. Sistemul de intrări-ieşiri din C++
• Introducere
• Clasa ios
Operaţii de I/E cu format
Utilizarea funcţiilor width(), precision() şi fill()
Funcţiile de I/E de tip “manipulator”
Crearea propriilor funcţii de tip manipulator
Verificarea stării sistemului de I/E
• Clasa ostream
• Clasa istream
• Funcţiile de tip inserter şi extractor
Redefinirea operatorului de ieşire
Redefinirea operatorului de intrare
• Operaţii de intrare-ieşire pe fişiere
• Operaţii de intrare-ieşire pe şiruri de caractere 2
1. SISTEMUL DE INTRĂRI-IEŞIRI DIN C++
Introducere
3
Sistemul de I/E din C++ operează prin stream-uri (fluxuri)
Un stream (flux) este un dispozitiv logic, care fie produce,
fie consumă informaţie şi este cuplat la un dispozitiv fizic
prin intermediul sistemului de I/E din C++
4
Flux de intrare
Dispozitiv de intrare Program
>> (extractie)
Flux de iesire
Dispozitiv de iesire Program
<< (insertie)
Flux bidirectional
5
La execuţia unui program în C++ se deschid în mod
automat următoarele stream-uri predefinite (ca urmare nu
trebuie declarate) :
cin Intrarea standard Tastatura
cout Ieşirea standard Ecranul
cerr Eroarea standard Ecranul
clog Versiunea “tampon” a lui cerr Ecranul
8
Operaţiile de I/E se bazează pe un model pe două nivele
9
Clasa ios
10
Clasa ios este clasa de bază pentru clasele istream,
ostream, fstream, stringstream
Exemplu:
15
cout.setf( ios::showbase | ios::hex);
Metoda unsetf( ), membră a clasei ios, şterge unul sau
mai mulţi indicatori de format:
Metoda:
şterge indicatorii specificaţi de parametrul flags
toţi ceilalţi indicatori rămân neschimbaţi
16
#include <iostream>
using namespace std;
int main(){
// afisarea valorilor folosind pozitionarile implicite
cout<< 123.33 << " salut! " << 100 <<'\n';
cout<< 10 << ' ' << -10 <<'\n';
cout << 100.0 <<'\n';
// schimbam formatul
cout.setf (ios::scientific| ios::showpos);
cout << 123.33 << " salut! " << -100 <<'\n';
cout.unsetf(ios::dec);
cout.setf (ios::hex);
cout <<10 << ' ' << -10 << '\n';
cout.unsetf (ios::scientific| ios::showpos);
cout.setf (ios::showpoint); 17
18
Utilizarea metodelor width( ), precision( ) şi fill( )
21
#include <iostream>
using namespace std;
int main(void){
cout.width(10);// latimea minima camp
cout<<"salut"<<'\n'; // implicit, alinierea se face la dreapta
cout.fill('%'); // stabilirea caracterului de completare
cout.width(10);
cout<<"salut"<<'\n'; // alinierea la dreapta
cout.setf(ios::left); // alinierea la stanga
cout.width(10);
cout<<"salut"<<'\n'; // alinierea se face la stanga
cout.width(10);
cout<<123.234567<< '\n'; // se utilizeaza formatul implicit
cout.width(10); // se stabileste latimea
cout.precision(3); // se stabileste precizia
22
cout<<123.234567<<'\n';
}
Clasa ios
Metodele de I/E de tip “manipulator”
23
Manipulatorii pot să apară în lanţul de operaţii de I/E
şi afectează doar stream-ul asociat, nu şi alte stream-
uri deschise în mod curent
Principalul avantaj: sunt mai uşor de utilizat şi permit
scrierea mai compactă a codului
Mulţi manipulatori se aseamănă cu flagurile din datele
membre de tip fmtflags ale clasei ios
24
Manipulator Scop Intrare/ Ieşire
dec Formatează datele numerice în zecimal Ieşire, intrare
int main(void)
{
cout << hex << 100 << endl;
cout << oct << 100 << endl;
cout << setfill( ‘X’)<< setw (10);
cout << 10 << endl << endl;
}
26
Exemplu:
#include <iostream>
#include<iomanip>
using namespace std;
int main(void) {
int a; bool b;
cout << "dati valoarea lui a in hexazecimal: ";
cin >> hex >> a;
cout << "\nValoarea lui a in zecimal: " << a << endl;
cin.setf(ios::boolalpha);
cout << "\ndati valoarea lui b: (true sau false) ";
cin >> b;
cout << "\nb ca valoare intreaga= " << b << endl;
27
}
Crearea propriilor funcţii de tip manipulator (de
tip utilizator)
28
Forma generală a manipulatorilor de ieşire fără
parametri:
ostream& nume_manipulator (ostream& stream)
{
// cod
return stream;
}
Observație:
Deşi manipulatorul are ca parametru o referinţă a
stream-ului cu care operează, nu se utilizează nici un
argument când acesta este inserat într-o operaţie de
ieşire
29
Sintaxa funcţiilor manipulator de intrare fără parametri
este următoarea:
istream& nume_manipulator (istream& stream)
{
// cod
return stream;
}
30
# include <iostream>
using namespace std;
int main(void)
{
cout<< init << 123.123456 << endl << endl;
}
31
Verificarea stării sistemului de I/E
33
Exemplul 1:
if(! (iosObject.rdstate() & ios::failbit))
{
//ultima operatie nu a esuat
}
Exemplul 2:
while (!in.eof())
{
in.get (car) ;
// verifică eroarea
if (! in.good() && !in.eof() ) {
cout << ”Eroare de I/E …..se termina programul\n” ;
return 1;
}
34
}
…
Obiectele stream (flux) pot fi testate direct folosind
operatori logici, in felul acesta se testează flagul ios::good
asociat fluxului
if (cin)
// cin este interpretat ca fiind de tipul bool
if (cin >> x)
// cin interpretat ca fiind de tip bool, după operația de
extracție
35
Clasa ostream
36
Principalele metode membre:
ostream(streambuf *);
ostream& put(char);
ostream& write(const char*, int n);
ostream& flush( ); // goleste zona tampon asociata
long tellp( ); // returneaza pozitia curenta in flux
ostream& seekp(long, seekdir = ios::beg);
unde pentru seekdir se poate asocia ios::{beg, cur, end}
37
Clasa istream
Această clasă permite citirea și interpretarea intrării din
secvențe de caractere
39
istream& getline(char*, streamsize n, char = '\n');
extrage si depune la adresa data cel mult n-1 caractere; extragerea
se opreste la intalnirea caracterului specificat sau la intalnirea EOF;
se pune delimitatorul de sir (‘\0’) la coada;
int gcount( ); // returneaza numarul de caractere extras
ultima data de functia read() sau getline()
istream& ignore(int n = 1, int = EOF); // sare peste n
caractere, dar le extrage din flux; se opreste
la intalnirea caracterului specificat;
long tellg( ); // returneaza pozitia curenta in flux
istream& seekg(long, seek_dir=ios::cur); // muta
pozitia curenta in flux, in raport cu pozitia de
inceput, de sfarsit sau in raport cu pozitia curenta
40
Funcţiile de tip “inserter” şi “extractor”
Sistemul de I/E din C++ permite supraîncărcarea
operatorilor de I/E
Operaţia de ieşire se numeşte inserţie (insertion), iar
operatorul (<<) este denumit operator de inserţie (insertion
operator):
această denumire provine de la faptul că un operator de ieşire
inserează informaţia în stream
43
Supraîncărcarea operatorului de intrare
Forma generală a funcţiilor extractor:
istream& operator>> (istream& stream, Tip &ob)
{
// corp extractor
return stream;
}
# include <iostream>
using namespace std;
class Coord {
int x, y;
public:
Coord( ) {
x=0; y=0;
}
Coord(int i, int j) {
x=i; y=j;
}
friend ostream &operator<< (ostream& , Coord ob);
friend istream &operator>> (istream& , Coord &ob);
};
45
// inserter pentru clasa Coord
ostream& operator<<(ostream& stream, Coord ob)
{
stream << "abscisa: " << ob.x << ", ordonata: " << ob.y
<< '\n';
return stream;
}
}
// functia main
int main (void){
Coord a(1,1), b(10,24);
cout << "Obiecte cu coordonate predefinite: " <<endl;
cout<< "\tObiect1: "<<a << "\tObiect2: "<<b;
cout<< "\nCoordonate noi: ";
cin >> a;
cout << "Afisare coordonate noi:"<<endl<<'\t';
cout << a;
47
Exemplu: supraincarcare operatori << si >> cu functii externe (nu sunt friend)
# include <iostream>
using namespace std;
class Coord {
int x, y;
public:
Coord() {
x = 0; y = 0;
}
Coord(int i, int j) {
x = i; y = j;
}
int getX() { return x; }
int getY() { return y; }
void setX(int x) {
this->x = 0;
}
void setY(int y) {
this->y = 0; 48
}
};
// inserter pentru clasa Coord
ostream& operator<<(ostream& stream, Coord ob){
stream << "abscisa: " << ob.getX()<< ", ordonata: " << ob.getY() << '\n’;
return stream;
}
50
În C++ toate stream-urile sunt identice, de aceea un
operator de tip inserter (de exemplu) supraîncărcat,
poate fi folosit pentru a trimite rezultatele pe ecran sau
într-un fişier
51
PROGRAMAREA
CALCULATOARELOR – ALGORITMI
MODUL 2
FISIERE
1
CURS 10
OPERAŢII DE I/E C++ CU FIŞIERE
Operațiile de I/E C++ cu fișiere implică operații cu
fluxurile asociate fișierelor
Pentru a realiza operaţii de I/E C++ cu fişiere, trebuie
să includem în program fişierul antet fstream.h (sau
fstream), care defineşte mai multe clase pentru
definirea fluxurilor pentru lucrul cu fișiere:
ifstream, ofstream şi fstream
2
!!! În C++, un fişier este deschis prin cuplarea lui la un
stream
Înainte de deschiderea fişierului trebuie să obţinem
mai întâi un stream
Pentru a crea un stream de intrare, el trebuie
declarat ca fiind de tip ifstream, iar pentru a crea unul
de ieşire, streamul trebuie declarat de tip ofstream
Streamurile care realizează ambele tipuri de operaţii
trebuie declarate de tip fstream
Exemplu:
ifstream in;
ofstream out;
fstream io;
3
Flux de intrare
(ifstream)
Fișier Program
Flux de iesire
(ofstream)
Fișier Program
Flux bidirectional
(fstream)
Fișier Program
4
Deschiderea/închiderea unui fişier
5
Lista valorilor posibile pentru al doilea parametru
este definită în clasa “ios_base”, prin membri publici
de tip openmode (mască de biți):
6
Exemplu:
ofstream out;
ofstream out("test", ios::out);
out.open("test", ios::out);
sau
ofstream out("test"); // open nu e obligatoriu
Putem generaliza:
ifstream fin ("test"); // in file
ofstream fout ("test"); // out file
fstream finout ("c:\\Test\\test"); // in-out file 8
Observație! Dacă operaţia de deschidere a fişierului
eşuează, stream-ul va avea valoarea zero și este setat
flagul failbit pentru streamul respectiv.
Înainte de utilizarea unui fişier trebuie să verificăm dacă
operaţia de deschidere a reuşit, folosind o secvenţă de
genul:
if (!stream_propriu) {
cout<< " Nu poate deschide fişierul\n ";
// trateaza eroarea}
sau
if ((stream_propriu.rdstate() & stream_propriu.failbit)) {
cout<< "Nu poate deschide fisierul \n" ;
// tratează eroarea
9
}
Putem folosi metoda is_open () pentru a testa dacă un
fișier a fost conectat la un stream:
bool is_open( ); // true deschidere reusita
// false deschidere esuata
10
Citirea/scrierea datelor în fişiere
11
I/E PENTRU FIȘIERE BINARE
13
Pentru a citi/scrie blocuri de date binare, se folosesc
metodele read()/write(), cu prototipurile:
#include <iostream>
#include <fstream>
using namespace std;
int main(void){
ofstream fout ("test"); // creaza un fisier de iesire
if (!fout) {
cout<<"Nu se poate deschide fisierul de iesire\n";
exit(0);
}
fout<<"Salut!\n";
fout<<100<<" "<<hex<<100<<endl;
15
fout.close( );
ifstream fin("test"); // deschide un fisier de intrare
if (!fin) {
cout<<"Nu se poate deschide fisierul de intrare\n";
exit(0);
}
char sir[80];
int i;
fin>>sir>>i;
cout<<sir<< " "<<i<<endl;
fin.close( );
}
16
Exemplul 2: ( scriere cu write(), citire cu read() si getline() )
#include <iostream>
using namespace std;
#include <fstream>
int main(void){
int numar=12;
ofstream out ("test.txt"); // creaza un fisier de iesire
if (!out) {
cout<<"Nu se poate deschide fisierul de iesire\n";
return 1;
}
out.write("Salut !\n", 8);
out.write((char *)&numar, sizeof(int)); // simbolul pt Form Feed
out.close( ); 17
ifstream in ("test.txt");
if ( !in ) {
cout<< "Nu poate deschide fişierul de intrare\n" ;
return 1;
}
char sir[20];
in.getline(sir, 19);
in.read ((char*) &numar, sizeof(int));
cout << sir << " " << numar << endl;
in.close( ) ;
return 0;
}
18
//Exemplu scriere sir de caractere, valori float si int separate prin spatii
#include <iostream>
#include <fstream>
using namespace std;
#include <stdlib.h>
int main(){
ofstream out("test.txt"); // deschidere fisier pentru operatii de iesire
if(!out) {
cout << "Fisierul test.txt nu poate fi deschis.\n";
exit(1);
}
out << "R " << 9.9 <<" "<< 10<< endl;
out << "T " << 9.9 <<" "<< 9 << endl;
out << "M " << 4.8 <<" "<< 4 <<endl;
out.close(); 19
ifstream in("test.txt"); // flux de intrare pentru fisierul test.txt
if(!in) {
cout << " Fisierul test.txt nu poate fi deschis.\n";
exit(1);
}
char item[20];
float cost;
int mark;
}
//Acelasi exemplu, citire pana la EOF
#include <iostream>
#include <fstream>
using namespace std;
#include <stdlib.h>
int main(){
ofstream out("test.txt"); // deschidere fisier pentru operatii de scriere
if (!out) {
cout << "Fisierul nu poate fi deschis.\n";
exit(1);
}
out << "R " << 9.9 << " " << 10 << endl;
out << "T " << 9.9 << " " << 9 << endl;
out << "M " << 4.8 << " " << 4 << endl;
//out<<std::ifstream::traits_type::eof(); //scriere explicita EOF 21
out.close();
ifstream in("test.txt"); // deschidere fisier pentru operatii de intrare
if (!in) {
cout << "Eroare deschidere fisier test.txt .\n";
exit(1);
}
char item[20];
float cost;
int mark;
while(1){
in >> item >> cost >>mark;
if(!in.eof())
cout << item << " " << cost <<" "<< mark<< "\n";
else
break;
22
}
//SAU
/*
int eof_m;
while (1){
in >> item >> cost >> mark;
eof_m = in.peek(); // citeste caracter din flux fara sa-l extraga
if (eof_m == -1) break;
cout << item << " " << cost << " " << mark << "\n";
};
*/
in.close();
}
23
Exemplul 3 ( utilizare getline() ):
#include <iostream>
using namespace std;
#include <fstream>
int main( ) {
char infname[30];
char outfname[30];
char buffer[101];
cout << "Numele fisierului sursa ";
cin >> infname;
ifstream in(infname);
if (!in) {
cout << "Nu s-a deschis fisierul " << infname << endl;
exit(0);
24
}
cout << "Fisierul destinatie: ";
cin >> outfname;
ofstream out(outfname);
if (!out) {
cout << "Nu s-a putut deschide fisierul " << outfname <<endl;
exit(0);
}
while (!in.eof( )) {
in.getline(buffer,100);
out << buffer << endl; // scriere in fisierul destinatie
}
in.close( ); // inchidere fisier sursa
out.close( ); // inchidere fisier destinatie
}
25
EXEMPLE METODA READ() :
// program ce utilizeaza metoda read() pentru a citi date dintr-un fisier
// care se creaza in prealabil
#include <iostream>
#include <fstream>
#include <stdlib.h>
using namespace std;
int main(){
char sir[11]=" ";
fstream io ;
io.open("test.txt", ios::out);
if (!io) {
cout<< "Nu poate deschide fisierul de scriere\n" ;
exit(1);}
io.write("Sir de test de verificat", 15);
26
io<< "abcdefghijk";
io.close();
io.open("test.txt", ios::in);
if (!io) {
cout<< "Nu poate deschide fisierul de citire\n" ;
exit(1);
}
while(1){
io.read(sir,10) ;
if (!io.eof())
cout<<sir ;
else break;
}
io.close() ;
}//main
27
FIȘIERE ÎN ACCES ALEATOR
#include <iostream>
#include <fstream>
#include <stdlib.h>
using namespace std;
}
fstream out;
out.open(argv[1], ios::in | ios::out | ios::binary) ;
if(!out){
cout<<"Nu poate deschide fisierul";
exit(1);
}
out.seekp(atoi(argv[2]),ios::beg) ;
out.put(*argv[3] ) ;
out.close() ;
cin.get();
}//main
30
Exemplu pentru operatii de scriere/citire obiecte in/din fisiere prin
supraincarcarea operatorilor >> respectiv << si acces aleator
#include <iostream>
#include <fstream>
using namespace std;
class Coord {
int x, y;
public:
Coord( ) { x=0; y=0; }
Coord(int i, int j) { x=i; y=j; }
friend ostream &operator<< (ostream& , Coord ob);
friend istream &operator>> (istream& , Coord &ob);
}; 31
// inserter pentru clasa Coord
ostream& operator<<(ostream& stream, Coord ob)
{
stream << ob.x << " " << ob.y << '\n';
return stream;
}
33
if(!fio){
cout<<"\n Deschiderea fisierului a esuat";
exit(0);
}
cout<<"\nScriem obiectul in fisier!\n";
fio<<a; // scriere obiect in fisier
40
Fişierul antet strstream.h (strstream, sstream)
conţine definiţiile claselor ostrstream (ostringstream),
istrstream (istringstream), strstream (stringstream)
ce implementează operaţii de I/E pe tablouri de
caractere
41
Clasele specificate sunt similare cu cele destinate
lucrului pe fişiere, fiind derivate din clasele iostream
corespondente şi ca urmare moştenesc metodele
membre ale acestora
42
Clasa ostrstream:
Controlează inserarea de elemente și codificarea
obiectelor într-un buffer (de tip strstreambuf)
ostrstream (void); // crează un obiect ostrstream cu
zonă tampon alocată dinamic
ostrstream (char *buf, int size, int mode = ios::out);
// utilizatorul specifică zona tampon
şi dimensiunea acestuia
streamsize pcount (void); // dă numărul de octeţi
memoraţi în buffer pentru un flux de ieşire
char* str (void); // blochează şi returnează buffer-ul
asociat fluxului; dacă acesta a
fost alocat dinamic, trebuie dealocat explicit
43
Exemplu:
ostrstream odyn; // zona tampon alocata dinamic
char *buf = odyn.str( );
…
delete buf;
char buffer[1024];
ostrstream ssta(buffer, 1024);
44
#include <strstream>
#include <iostream>
using namespace std;
int main(){
char str[80];
ostrstream outs(str, sizeof(str));
outs << 125 << " " << 17.25 <<" astea sunt numerele";
outs << ends; // terminatorul null
cout <<"Numarul de caractere="<< outs.pcount()<<endl;
// afiseaza numarul de caractere din outs
cout << str<<endl;
} 45
Clasa istrstream:
istrstream (const char *); // crează un obiect istrstream
ce utilizează un şir dat
istrstream (const char *, int n); // crează un obiect
istrstream ce utilizează primii n
octeţi dintr-un şir dat (mai sigură !)
strstreambuf *rdbuf( ) const; // returnează un pointer la
obiectul strstreambuf asociat streamului
Exemplu:
char data[128];
//...
istrstream istr(data, 128);
sau
istrstream istr(data);
46
#include <iostream>
#include <strstream>
using namespace std;
int main() {
istrstream s("15 10.75 Test pentru istrstream ");
int i;
float f;
char buf[100];
s >> i >> f>> buf; // intrarile sunt delimitate prin spatii albe
cout << "i = " << i << ", f = " << f<<", buf = " << buf << endl;
cout << s.rdbuf(); // afisam restul din obiectul istrstream
} 47
Clasa strstream:
48
#include <iostream>
#include <strstream>
using namespace std;
int main() {
strstream s;
int i; float f; char sir[20];
s<<"8.75 2111 este media la programare"<<ends;
s>>f>>i>>sir;
cout<<f<<" "<<sir<<" "<<s.str()<<" a grupei "<<i<<endl;
// cin.get();
}
49
PROGRAMAREA CALCULATOARELOR –
ALGORITMI
MODUL 3
CURS 11
CUPRINS
Introducere în generice C/C++
Funcţii şi metode template
Clase template
2
INTRODUCERE ÎN GENERICE C/C++
3
FUNCŢII ŞI METODE TEMPLATE
4
SINTAXA
template <class tipg1[,…class tipgi]> tip_ret numeFuncT (lista_par){
……………..
instructiuni
}
Sau, mai general:
#include <iostream>
using namespace std;
return 0;
}
Apelul:
compara(x, m);
este corect?
9
// MAXIMUL DINTRE 2 VALORI GENERICE DE ACELASI TIP
#include <iostream>
using namespace std;
#include <string>
template <typename T> T mmax(T x, T y) ;
int main(void){
int i=7,j=9 ;
string s1 ="Pg";
string s2 = "Gen";
// CName a, b ;
cout <<"\n Rezultate maxim tipuri generice\n";
int k= mmax(i,j) ;
cout <<k <<endl;
cout << mmax('A', 'B')<<endl;
cout << mmax(s1, s2) << endl;
10
//cout << mmax(10, 20.5) ; Err tipuri diferite
//CName c=mmax(a,b); //se va supraincarca mmax
// separat pentru obiecte, fara op. ?:
return 0; } //main
11
// ARGUMENTE DEDUSE IMPLICIT SAU SPECIFICATE EXPLICIT
#include <iostream>
using namespace std;
template <class T> T suma(T x, T y) ;
void main(void){
cout<<"\n Argumente deduse implicit sau specificate explicit\n";
cout<< suma(10,20)<<" int implicit"<<endl; //int
cout<< suma(10.0, 20.0)<<" double implicit"<<endl; //double
// suma (10, 20.0); //Eroare, tipuri diferite
cout<< suma <int> (10,20.0)<<" int explicit"<< endl; // Ok, int, specificare
// explicita
cout<<suma <double> (10, 20.0)<<" double explicit"<<endl; //Ok, double
} //main
template <class T> T suma(T x, T y){
return x+y;
}
Concluzie: Compilatorul poate crea atâtea versiuni pentru funcţie, câte12
sunt necesare pentru tratarea tipurilor utilizate.
FUNCŢII CU MAI MULTE TIPURI GENERICE
In declaraţia unei funcţii template putem avea mai multe tipuri
generice, separate prin virgulă. Compilatorul va crea practic
atâtea versiuni ale funcţiei câte îi sunt necesare, fiind
considerat un proces de supraîncărcare implicită.
#include <iostream>
using namespace std;
template <class X, class Y> void afis(X a, Y b);
int main(void){
afis(12, "abcd");
afis(12.8, 'c');
return 0;
}//main
template <class X, class Y> void afis(X a, Y b){
cout << "\n Prima valoare: "<<a;
cout << "\n A doua valoare: "<<b;
13
}
SUPRAÎNCĂRCAREA EXPLICITĂ A UNEI FUNCŢII TEMPLATE
14
Exemple:
//comparare valori, supraincarcare template
#include <iostream>
using namespace std;
template <class X> void compara(X a, X b); // caz template general
void compara(int a, int b) ; // caz exceptat
template <> void compara (float a, float b); //specializare explicita
void compara(int a, int b, int c); //supraincarcare explicita trei
parametri
int main(void){
int x, y, a, b, c;
float m, n;
double d, e;
cout << "\nPrimul numar intreg: "; cin >> x;
cout << "\n al doilea numar intreg: "; cin >> y;
15
cout << "\nPrimul numar flotant: "; cin >> m;
cout << "\n al doilea numar flotant: "; cin >> n;
cout << "\n Alti trei intregi";
cout << "\nPrimul numar intreg: "; cin >> a;
cout << "\n al doilea numar intreg: "; cin >> b;
cout << "\n al treilea numar intreg: "; cin >> c;
cout << "\nPrimul numar double: "; cin >> d;
cout << "\n al doilea numar double: "; cin >> e;
compara(x, y); // supraincarcare intregi caz exceptat
cout<<endl;
compara(d, e); // apel template general
cout<<endl;
compara(a, b, c); // supraincarcare semnatura diferita
cout<<endl;
compara<>(m, n); // apel specializare explicita
cout<<endl;
return 0;
}
16
template <class X> void compara(X a, X b){
cout << "\n Compar date ne-intregi!-template general\n";
if(a>b)cout << "\nValoarea "<<a<<" este mai mare.";
else cout << "\nValoarea "<<b<<" este mai mare.";
}// caz template general
int main(){
int a[] ={1,2,3,4,5};
scrie<int,5> (a);
return 0;
}
return 0;
}
Un antet simplu:
template <typename A>
Exemple:
c1) template-template functii
template <template <typename T> class A > void F(A<char>a){...}
c2) template-template pentru clase:
template<template <class T> class A> class X { };
template<class T> class Y { }; 22
X<Y> a;
RESTRICŢII PENTRU FUNCŢIILE GENERICE
Limitări:
1. Funcţiile generice realizează aceleaşi operaţii indiferent de tipul
de date pe care le primesc la intrare. (dezavantaj ce poate fi
eliminat prin supraîncărcare explicită, când putem impune o altă
abordare pentru acea metodă sau prin specializare).
2. Funcţiile template nu admit parametri template impliciţi, dar
admit parametri de apel impliciţi.
3. O funcţie virtuală nu poate fi template
4. Destructorii nu pot fi template;
Obs! Dacă o funcţie template se potriveşte uneia dintre
specializări şi unei supraîncărcări, atunci are prioritate
supraîncărcarea non-template. Pentru apelul specializării explicite
se va introduce după numele metodei construcţia < >.
Funcţiile template au suferit diferite modificări de la o
versiune la alta: '98, '0x,' 1y ‘, 2z 23
EXEMPLE:
a) template <class T1=int, int n=7> void F(T1 t) {…} // Err. Nu pot fi specificati
parametri template impliciti
} //main
}
CLASE TEMPLATE (GENERICE)
25
SINTAXA DE DEFINIRE A UNEI CLASE GENERICE
template <class tipg1[,… class tipgi]> class Class_name{
………….. };
sau
template <typename tipg1[,…typename tipgi]> class Class_name{
…………..
};
unde tipgi este numele care substituie tipul de date utilizat de clasa
Exemple:
template <typename T> class B{
T a;
…………..
}; //B
28
// test stiva plina
template <class STip> int Stiva <STip> ::isFull(void){
if(Next >= Dim) return 1;
else return 0;
}
// introducere in stiva
template <class STip> int Stiva <STip> ::push(STip c){
if(isFull()) return 0;
Stack[++Next] = c;
return 1;
}
// extragere din stiva
template <class STip> int Stiva <STip> ::pop(STip &c){
if(isEmpty()) return 0;
c = Stack[Next--];
return 0; 29
}
int main(){
Stiva<int> Si(20);
Stiva<double> Sf(10);
Stiva<char> Sc(5);
Si.push(10);
Sf.push(10.10);
Sc.push('A');
int a=10;
double b=10.11;
char c;
Si.pop(a);
Sf.pop(b);
Sc.pop(c);
cout << a << " , "<< b <<" , "<< c;
return 0; 30
}//main
INSTANŢIEREA CLASELOR GENERICE
Crearea unei noi definiţii a unei functii, clase sau membru al
unei clase utilizând declaraţia unui template se numeşte
instanţierea template-ului.
Clasa obţinută în acest mod dintr-o clasă generică este o
clasă instanţiată. Instanţierile pot fi implicite sau explicite.
a) Instanţiere implicită
Pentru instanţierea unui obiect poate fi utilizată instanţa
creată implicit de către compilator, caz în care template-ul
trebuie definit, nu doar declarat (metodele să fie definite).
Pentru referinţe/ pointeri la obiecte, nu este necesar ca
clasa să fie definită, este suficient să fie declarată
31
template <class T> class A; // declararea clasei A
int main(){
B <int> b; // declarare obiect- instantiere implicita
A <char> *pc; // declarare pointer, nu e nevoie definire clasa A
// A <float> obj; // Eroare, clasa e doar declarata si trebuie definita
….
}//main
37
A) METODE TEMPLATE IN CLASE NON-TEMPLATE
#include <iostream>
using namespace std;
class A{
public: template <typename T1, typename T2> void F(T1 t1, T2 t2){
cout<< "\nT1= " << t1;
cout<< "\nT2= " << t2;
}
}; //A
int main(){
A a;
a.F<int, char>(10,'A'); // specificare explicita
a.F(100, 'W'); // deducere
cin.get();
}
Dacă o metodă template este doar declarată în clasă, ea va fi definită în38
afara clasei folosind antetul template-ului:
// METODE TEMPLATE IN CLASE NON-TEMPLATE DEFINITE IN AFARA CLASEI
#include <iostream>
using namespace std;
class A{
public: template <typename T1, typename T2> void F(T1 t1, T2 t2);
}; //A
int main(){
A a;
a.F<int, char>(10,'A'); // specificare explicita
a.F(100, 'W'); // deducere
cin.get(); return 0;
}
template <typename T1, typename T2> void A:: F(T1 t1, T2 t2){
cout<< "\nT1= " << t1;
cout<< "\nT2= " << t2; 39
}
B) METODE TEMPLATE IN CLASE TEMPLATE
//membrii template in clase template
#include <iostream>
#include <string>
using namespace std;
template <class T, int DIM> class Stiva {
T st[DIM];
int next;
public:
Stiva(): next(0) { };
void push (T val) { st[next++] = val;}
T pop () {return st[--next];}
void display() const {
for (int i=next-1; i>=0;i--)
cout <<" "<< st[i] << " ;";
cout <<'\n';
}
40
};
int main(){
Stiva <string, 100> s1; // Stiva de string-uri
s1.push(" Unu ");
s1.push(" Doi ");
s1.push(" Trei ");
s1.display();
cout << s1.pop();
cout << s1.pop();
s1.display();
42
PARAMETRII TEMPLATE AI CLASELOR
Clasele template suportă aceleaşi tipuri de parametri ca şi
metodele template: tip, non-tip, template-template
Exemplu :
template <class T, int n, template <class N> class A> class B{
//……
}; //B tip, non-tip, template-template
template <class T > class X {
//………..
}; //X
int main (){
B <char, 10, X> b;
}
Parametrul N din definirea clasei B putea fi omis:
template <class T, int n, template <class > class A> class B{
//……
}; //B 43
PARAMETRI TEMPLATE IMPLICIŢI
Template-urile de clasă acceptă parametri template impliciţi, spre
deosebire de metodele generice
a) Parametri tip
}
b) Parametrii non-tip
- Au aceleaşi restricții ca la metodele template, adică pot fi
int, enum, pointeri la obiecte, atribute sau metode, sau pot
fi referinţe.
- Nu se admit tipuri reale (float, double), doar pointeri la
aceste tipuri
- Valorile implicite trebuie să fie expresii constante
c) Parametrii template-template
Apar atunci când o clasă template va admite ca şi argument
o altă clasă template. Astfel, o clasă A cu un parametru
template X poate fi instanţiată cu diferite argumente de tip
template de clasă.
45
Exemple :
//arg. implicite template-template
class D {
}; //D non template
int main ( ){
A <C> a1; // X==C, C<T> == C<int>
A < > a2; // X==B, B<T> == B<D>
//A<D> a3; // Err D e clasa non template 46
}
SPECIALIZĂRILE CLASELOR
Şi clasele template admit specializări la fel ca şi metodele, iar în
plus permit şi specializări parţiale.
Specializarea se face când se doreşte o definiţie diferită de cea
generală, pentru anumite tipuri.
Numele specializării este format din numele clasei la care se
adaugă lista de argumente template cuprinsă între paranteze
unghiulare, < >.
47
template <typename T> class A {
//…..
}; //A template primar
Nume
specializare
template <> class A <int>{
//….
}; // Specializare explicita A <int> pt. T = int
48
Tipurile pentru care se face specializarea se specifică ca şi argumente
template imediat după numele clasei, între < >.
Corpul specializării ar trebui să difere de corpul clasei primare.
50
Specializarea explicită trebuie să furnizeze argumente
pentru toţi parametrii template.
int main(){
Str <int> s1;
cout <<"\n Tip generic, int \n";
s1.read();
s1.display();
Str <char> s2; //specializare
s2.read();
s2.display();
cin.ignore();
cin.get();
return 0;
51
}//main
b) Specializarea parţială
Dacă specializăm o clasă template păstrând cel puţin un parametru
generic, atunci avem o specializare parţială. Specializările parţiale reduc
generalitatea template-ului primar. Ele nu sunt condiţionate de existenţa
definiţiei complete a template-ului primar.
53
MOŞTENIREA ŞI COMPOZIŢIA CLASELOR TEMPLATE
C++ e considerat un limbaj multi-paradigmă. Suportă :
- programarea structurată, procedurală, din limbajul C
- P.O.O
- programarea generică
- programarea funcţională odată cu funcţiile lambda
introduse în C++1y
54
A) CLASA TEMPLATE MOȘTENEȘTE CLASA NON-TEMPLATE
//mostenire clasa non-template
class A {
//....
};//A
class B: public A
{
//...
};//B non-template
Când un obiect conţine cel puţin un membru care la rândul lui e obiect
al cărui tip diferă de cel al clasei
//continere
template <typename T> class A {
T x; // obiect continut
//....
};//A template baza
template <typename T1, typename T2> class B
{
A<T1> a; // a obiect continut -- compozitie
T2 b; // b obiect continut
};//Derivat
}//main
RESTRICŢII IMPUSE CLASELOR TEMPLATE
Sunt necesare următoarele restricţii în cazul implementării şi
folosirii claselor template:
la fiecare instanţiere a tipului template suntem obligaţi să
înlocuim tipul generic cu un tip concret;
specificarea tipului se face sub formă de parametru al clasei
template;
la fiecare instanţiere, tipul concret va lua locul tipului
generic;
Tipurile generice folosite în declaraţiile claselor template pot fi
înlocuite cu:
tipuri de date predefinite (int, float, etc.)
pointeri la funcţii;
59
expresii constante.
#include <iostream>
using namespace std;
template <class X, int n> class myTemplate{
X tab[n];
int i;
public:
void init(){ for(i=0; i<n; i++){
cout << "\nElementul "<<i+1<<": ";
cin >> tab[i]; }
}//init
61
Intrebari:
63
PROGRAMAREA CALCULATOARELOR –
ALGORITMI
MODUL 3
CURS 12
BIBLIOTECA STL
CUPRINS
Introducere STL
Containere
Iteratori
Algoritmi
Obiecte funcţii
2
INTRODUCERE STL (STANDARD TEMPLATE LIBRARY)
STL este o bibliotecă de componente, cum ar fi
containerele şi algoritmii, ce asigură interoperabilitatea
între componentele predefinite (incluse în limbaj) şi cele
definite de utilizator
Astfel un algoritm STL poate opera şi pe containere
definite de utilizator, iar algoritmi definiţi de utlizator pot
opera şi pe containere STL, dacă respectă anumite
cerinţe
k j
4
STRUCTURA STL (CONT.)
In acest caz trebuie proiectate i*j*k versiuni de cod pentru
a acoperi toate situaţiile posibile
Dacă am utiliza funcţiile template (generice), axa “i”
poate lipsi şi ramân necesare j*k versiuni de cod:
de exemplu, vom avea o singură implementare a listei
înlănţuite pentru toate tipurile de date.
Următorul pas este de a face algoritmii să lucreze pentru
tipuri diferite de containere (tablouri, liste,…).
In acest mod vor mai fi necesare doar j+k versiuni de
cod.
5
COMPONENTE STL
Principalele componente ale STL sunt:
containere:
obiecte ce pot păstra şi administra obiecte; conţin structurile de
date suportate de STL;
sunt definite clase generice ce conţin aceste structuri şi metode
pentru manipularea datelor;
algoritmi:
proceduri ce pot opera pe diferite containere;
iteratori:
abstractizări ale accesului algoritmilor la containere, astfel încât
algoritmii să poată opera pe diverse containere
obiecte funcţii (functor):
clase ce au definit operatorul apel de funcţie ( );
adaptor:
încapsulează o componentă pentru a furniza o altă interfaţă (de 6
exemplu pentru a obţine o stivă dintr-o listă)
DIAGRAMA STL
7
AVANTAJE STL
eficienţa: nu folosesc derivarea claselor şi nici funcţii
virtuale şi ca urmare, STL este foarte apropiată de
codificarea directă
siguranţa tipurilor (type safety): prin utilizarea
extensivă a şabloanelor
flexibilitatea: suport mai bun pentru programarea
generică; algoritmii generici pot fi aplicaţi pe diferite
structuri, inclusiv cele predefinite în C++ (şiruri,
tablouri)
structura simplă: biblioteca este destul de mică
Compilarea separată a template-urilor
Mecanism generalizat de iniţializare a listelor, etc.
8
UTILIZARE STL
tot ce ţine de STL este plasat în spaţiul de nume
standard:
acesta trebuie specificat printr-o directivă using :
10
containere secvență: datele sunt ordonate liniar şi permit
căutări pe baza cheii (array, vector, forward_list, list, deque).
Sunt colecții ordonate în care fiecare element are o anumită
poziție. Termenul “ordonat” nu are semnificația de crescător/
descrescător, ci se referă la o anumită poziție. Poziția depinde
de momentul și locul inserării, dar este independentă de
valoarea elementului.
12
Au mai apărut:
forward_list - container secvenţial pentru liste simplu
înlănţuite
unordered_map, unordered_set - containere asociative de
tip hash ce pot fi şi multimap şi multiset
array - container de tip tablou unidimensional de
dimensiune fixă
bitset - container tablou de tip bool, etc.
13
Categorie Container Caracteristici
Secventiale
list Lista dublu înlănţuită ce permite inserări rapide în orice poziţie
15
STL ORDERED AND SEQUENCE CONTAINERS -
HTTPS://WWW.GEEKSFORGEEKS.ORG/THE-C-STANDARD-TEMPLATE-LIBRARY-
STL/
16
CONTAINERE STL ADAPTIVE SI NEORDONATE:
17
CONTAINERE (CONT.)
Declararea unui container se face astfel:
Container <Tip_concret> c;
Exemplu :
vector < int> tab1(10) ;
vector <float> tab2(20) ;
18
Container de tip vector
gestionează elementele sale într-un tablou dinamic
acces aleatoriu.
Structura unui vector arată astfel :
vector <int> v ;
vector <bool>; este o specializare a containerului vector
introdusă în noile versiuni ale C++. 19
CONTAINER: VECTOR
#include <vector>
Funcţii:
begin() returnează un iterator către primul element
end() returnează un iterator după ultimul element
push_back(...) adaugă un element la sfârşitul vectorului
pop_back(...) extrage un element de la sfârşit
swap(…) interschimbă elementele a doi vectori,
insert( , ) inserează un element
erase( , ) şterge un element (sau mai multe)
size() returnează numărul de elemente din vector
capacity() dă capacitatea (în număr de elemente) înainte
de a face o nouă realocare
reserve() alocă spaţiu pentru un număr de elemente
resize( , ) redimensionează un vector
empty() returnează True dacă vectorul este gol
20
[] operator de acces
EXEMPLU: VECTOR
/* operatii de adaugare elemente in vector, interschimbare si asignare
vectori */
#include < iostream >
#include < vector >
using namespace std;
void print (vector < double >&vector_);
}
// vector de valori intregi
#include <iostream>
#include <vector>
//using namespace std;
int main( ) {
std::vector<int> v = { 0, 1, 2, 3, 4, 5 };
for (const int& i : v) // acces prin referinta la valori constante
23
std::cout << i << ' ';
std::cout << '\n';
for (auto i : v) // acces prin valoare
std::cout << i << ' ';
std::cout << '\n';
for (auto&& i : v) // acces prin rvalue reference - tipul lui i este int&
std::cout << i << ' ';
std::cout << '\n';
const auto& cv = v;
for (auto&& i : cv) / /acces prin rvalue reference, tipul lui i este const int&
std::cout << i << ' ';
std::cout << '\n’;
} //
//vector, swap( )
#include <vector>
#include <iostream>
using namespace std;
int main( ){
vector <int> vec1, vec2;
vec1.push_back(4);
vec1.push_back(7);
vec1.push_back(2);
vec1.push_back(12);
cout << "vec1 data: ";
for (auto &i: vec1) cout << i << ' ‘;
cout << endl;
vec2.push_back(11);
vec2.push_back(21);
vec2.push_back(30);
cout << "vec2 data: ";
for (auto &i : vec2) cout << i << ' ';
cout << endl; 24
cout << "The number of elements in vec1 = " << vec1.size() << endl;
cout << "The number of elements in vec2 = " << vec2.size() << endl;
cout << endl;
28
CONTAINER: DEQUE
#include <deque>
Funcţii
begin() iterator la inceput
end() iterator la sfarsit
push_front(...) adauga element la inceput
pop_front(...) extrage primul element
push_back(...) adauga element la sfarsit
pop_back(...) extrage ultimul element
swap( , ) interschimba continutul
insert( , )
size()
capacity()
empty() 29
[]
EXEMPLU: DEQUE
/*Adauga elemente la inceputul si sfarsitul cozii, sterge de la
inceput, modifica valori, afiseaza */
#include < iostream >
#include < deque >
using namespace std;
int main ()
{
deque< int > d;
d.push_back (4);
d.push_back (9);
d.push_back (16);
d.push_front (1);
30
for (int i = 0; i < d.size (); i++)
cout << "d[" << i << "] = " << d[i] << endl;
cout << endl;
d.pop_front ();
d[2] = 25;
for (int i = 0; i < d.size (); i++)
cout << "d[" << i << "] = " << d[i] << endl;
return 0;
}
31
stack și queue folosesc containerul deque <T> ca adaptor
implicit.
template <typename T, typename Container = deque<T> >
class stack;
template <typename T, typename Container = deque<T> >
class queue;
32
La containerele asociative elementele în containere sunt
referite de cheie și nu de poziția lor absolută în container. Aici
avem:
Set - containere care stochează elemente unice după o
anumită ordine. Fiecare element este identificat prin valoare,
fiecare valoare este unica (reprezinta chiar cheia). Nu se poate
modifica valoarea elementului, dar poate fi inserat sau sters din
container.
template < typename T, // set::key_type/value_type
typename Compare = less<T>, // set::key_compare/value_compare
typename Alloc = allocator<T> > // set::allocator_type
class set;
Compare - criteriul de ordonare din set cu argumentul implicit,
less<T>. (ordonare crescatoare)
Tipic sunt implementate ca arbori de cautare binara.
33
35
ITERATORI (CONT.)
Iteratorii unei colecţii particulare (container) sunt definiţi
în clasa asociată colecţiei, sub forma unor construcţii
typedef :
std::vector<std::string>::iterator
std::vector<std::string>::const_iterator (iterator ce indica
o valoare constantă)
36
ITERATORI (CONT.)
Fiecare iterator trebuie să asigure :
accesul la elementul indicat (pointat) de iterator :
*iterator,
iterator -> , pt. colectii de tip map
38
ITERATORI - UTILIZARE
Utilizarea unui iterator se face astfel:
Nume_Container ::iterator first, last; //declarare
first = Nume_container.begin(); //asignare
last = Nume_container.end();
sau:
//initializare
Nume_Container ::iterator first= Nume_container.begin();
Nume_Container ::iterator last= Nume_container.end();
39
CATEGORII DE ITERATORI
input Citeşte un element la un moment dat, în direcţia înainte
(forward)
output Scrie un element la un moment dat, în direcţia înainte
forward Citeste sau scrie un element la un moment dat, în
direcţia înainte
bidirectional Citire sau scriere, înainte sau înapoi
random access La fel ca cei bidirecţionali, plus salturi la orice distanţă
în cadrul colecţiei
Iteratori
de intrare
Iteratori cu Iteratori Iteratori
acces aleator bidirectionali forward
Iteratori
de iesire
40
OPERATORI PENTRU ITERATORI
43
EXEMPLE -- VECTOR
#include <iostream>
#include <vector>
using namespace std;
int main(){
vector<int> coll; // declarare container
// adauga elemente in container
for (int i=1; i<=6; ++i) {
coll.push_back(i);
}
// afisare elemente
for (int i=0; i<coll.size(); ++i) {
cout << coll[i] << ' ‘; // acces prin indexare
}
cout << endl;
44
return 0;
}
// SET
#include <iostream>
#include <set>
using namespace std;
int main(){
typedef std::set<int> IntSet; // tip colectie
IntSet coll; // container de tip set
// inserare elemente - elem vor fi ordonate
coll.insert(3);
coll.insert(1);
coll.insert(5);
coll.insert(4);
coll.insert(1);
coll.insert(6);
coll.insert(2); 45
// afisare elemente
IntSet::const_iterator pos;
for (pos = coll.begin(); pos != coll.end(); ++pos)
{
std::cout << *pos << ' ‘; // acces prin iterator
}
std::cout << std::endl;
return 0;
}
46
// LIST – cu extragere elem.
#include <iostream>
#include <list>
using namespace std;
int main(){
list<char> coll; // container lista
// adauga elemente
for (char c='a'; c<='z'; ++c) {
coll.push_back(c);
}
// se afiseaza primul element dupa care acesta este extras
while (! coll.empty()) {
cout << coll.front() << ' ‘; // afisare element din fata
coll.pop_front(); // extragere element din fata
}
cout << endl;
return 0;
} 47
// LIST – cu iterator
#include <iostream>
#include <list>
using namespace std;
int main(){
list<char> coll; // container lista
// adauga elemente
for (char c='a'; c<='z'; ++c) {
coll.push_back(c);
}
// afisare elemente
list<char>::const_iterator pos;
for (pos = coll.begin(); pos != coll.end(); ++pos) {
cout << *pos << ' '; // acces prin iterator
}
cout << endl;
return 0;
48
}
// MAP
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main(){
// container de tip map: cheia de tip string, valoarea de tip float
typedef map<string,float> StringFloatMap;
StringFloatMap coll;
// adauga elemente
coll["TVA"] = 0.19;
coll["Pi"] = 3.1415;
coll["Numar oarecare"] = 4983.223;
coll["Null"] = 0; 49
// afisare elemente
StringFloatMap::iterator pos;
for (pos = coll.begin(); pos != coll.end(); ++pos)
{
cout << "cheie: \"" << pos->first << "\" "
<< "valoare: " << pos->second << endl;
}
return 0;
}
#include <iostream>
#include <deque>
using namespace std;
int main(){
deque<float> coll; // container de tip deque
// inserare elemente
for (int i=1; i<=6; ++i) {
coll.push_front(i*1.1);
}
// afisare elemente
for (int i=0; i<coll.size(); ++i) {
cout << coll[i] << ' ‘; // acces prin indexare
}
cout << endl; 51
return 0;
}
ALGORITMI
Algoritmii STL sunt funcţii generice ce operează pe
containere
Aceste funcţii nu au containerele ca argumente, ci doar
iteratori ce specifică parte sau containerele în intregime
Astfel, algoritmii pot opera inclusiv pe tipuri de date ce nu
sunt containere
Se realizează astfel o decuplare între algoritmi şi
containere prin intermediul iteratorilor
Este foarte important ca acele containere să suporte
iteratorii necesari unui algoritm (vezi documentaţie)
Algoritmii se afla in biblioteca <algorithm>, initial au fost
integrati peste 60 de algoritmi, acum fiind aproximativ 80
de algoritmi 52
ALGORITMI (CONT.)
Exemplu :
template <class ForwardIterator>ForwardIterator min_element
(ForwardIterator first, ForwardIterator last);
Acest algoritm necesită un container ce suportă cel puţin
un iterator de tip « forward »
Tentativa de a utiliza un algoritm pe un container ce nu
asigură iteratorii necesari conduce la erori, uneori ciudate
53
CATEGORII DE ALGORITMI
54
Operaţii pe secvenţe, ce produc modificări
Copiază o secvenţă într-o altă secvenţă (copy…)
Interschimbă valori sau domenii de valori (swap…)
Transformă o secvenţă sau două secvenţe într-o nouă
secvenţă (transform…)
Inlocuieşte elementele specificate (replace…)
Populează un domeniu cu o valoare (fill, fill_n)
Populează un domeniu cu valori generate (generate,
generate_n)
Şterge elemente (remove…)
Şterge duplicate (unique, unique_copy)
Inversează o secvenţă (reverse, reverse_copy)
Rotaţia elementelor (rotate, rotate_copy)
Amestecă aleator elementele (random_suffle)
Partiţionare cu elemente ce satisfac un predicat (partition,55
stable_partition)
Sortări şi operaţii asociate
Sortare domeniu (sort…)
Plasarea unui element în poziţia în care ar fi în urma sortării
(nth_element)
Caută limitele sau poziţia unei valori într-o secvenţă sortată
(lower_bound, upper_bound, equal_range,
binary_search)
Interclasează două secvenţe sortate (merge,
inplace_merge)
Operaţii de tip Set pe secvenţe sortate (includes, set_union,
set_intersection, set_difference,
set_symmetric_difference)
Operaţii de tip Heap (push_heap, pop_heap, make_heap,
sort_heap)
Caută elementul minim sau maxim (min, max,
min_element, max_element)
Determină ordinea lexicografică a doua secvenţe
(lexicographical_compare)
Generează permutări pentru o secvenţă (next_permutation, 56
prev_permutation)
Operaţii numerice generalizate
Insumează toate elementele unei secvenţe
(accumulate)
Calculează produsul scalar a două secvenţe
(inner_product)
Calculează sume parţiale (partial_sum)
Calculează o nouă secvenţă pornind de la sumele
parţiale în altă secvenţă (adjacent_difference)
57
OBIECTE FUNCŢII (FUNCTORI)
Reprezintă modalitatea STL de a reprezenta date
executabile
https://docs.microsoft.com/en-us/cpp/standard-
library/functional?view=msvc-170 58
Exemplu:
class XYZ{
public:
//definire operator „apel de functie"
return-value operator( ) (arguments) const;
...
};
// definire obiect
XYZ foo;
...
//apel operator( ) pentru obiectul foo
foo(arg1, arg2); // obiect apelat ca o functie
Echivalent cu:
59
foo.operator( ) (arg1, arg2);
FUNCTORI (CONT.)
Generatoare
Există algoritmi ce parcurg un domeniu apelând un
obiect funcţie la fiecare pas şi atribuie rezultatul
elementului curent
Predicate
Se folosesc pentru a testa anumite condiţii
Operatorul paranteză () trebuie definit astfel încât să
returneze ceva ce poate fi testat
Algoritmii ce au în nume sufixul _if folosesc un obiect
funcţie pentru a testa fiecare element pentru o condiţie
Un predicat dereferenţiază un singur element pentru
teste
Un predicat binar (BinaryPredicate) dereferenţiază 60
două elemente pentru a le compara
EXEMPLE ALGORITMI
for_each
template <class InpIter, class Function>
void for_each(InputIter first, InputIter last, Function F);
Cerinţe:
InputIter trebuie să aibă cel puţin tipul input
Functorul F() trebuie să asigure operatorul
operator()(T), unde T este tipul dereferenţiat de
InputIter 61
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main(){
int years[ ] = { 1942, 1952, 1962, 1972, 1982, 1992 };
const unsigned yearCount = sizeof (years)/sizeof (years[0]);
int* location = find (years, years + yearCount, 1972);
return 0;
}
64
#include <iostream>
#include <algorithm>
using namespace std;
return 0;
}
65
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
return 0; 66
}
binary_search():
template<class FwdIt, class T>
return 0; 68
}
sort():
template <class RandomAccessIterator>
void sort(RandomAccessIterator first,
RandomAccessIterator last);
int main () {
sort (array, array + 6);
for (int i = 0; i < 6; i++)
cout << array[i] << ' ';
cout << endl;
cin.get();
return 0;
} 70
#include <iostream>
#include <algorithm>
#include <functional>
#include<iterator>
using namespace std;
int array[ ] = { 1, 50, -10, 11, 42, 19 };
int main () {
int count = sizeof (array) / sizeof (array[0]);
ostream_iterator <int> iter (cout, " "); // iter afiseaza elementele
cout << "Inainte de sortare: ";
copy (array, array + count, iter); // copiaza fiecare elem unde indica
iter
cout << "\nDupa sortare: ";
sort (array, array + count, greater<int>()); // ultimul param e functorul
copy (array, array + count, iter);
cout << endl;
cin.get(); 71
return 0;
}
#include <vector>
#include <algorithm>
#include <iostream>
#include<iterator>
using namespace std;
int main(){
vector<int> myVec;
vector<int>::iterator item;
ostream_iterator<int> out(cout," ");
73
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main(){
ostream_iterator<int> out(cout," ");
// genereaza valori in vector
int myVec[11];
for ( long i=0; i<10; i++ )
myVec[i] = i;
75
accumulate
Insumează valorile din domeniu
template<class InIt, class T> T accumulate(InIt first,
InIt last, T val);
template<class InIt, class T, class Pred> T
accumulate(InIt first, InIt last, T val, Pred pr);
76
#include <vector>
#include <algorithm>
#include <numeric>
#include <iostream>
using namespace std;
int mult (int initial, int element) { return initial * element; }
int main () {
vector <int> v(5);
};
int Fibonacci::operator() () {
int r = v1 + v2;
v1 = v2;
v2 = r;
return v1;
}
int main (){
vector <int> v1 (10);
Fibonacci generator;