Sunteți pe pagina 1din 14

POO

LABORATOR 2
Elemente specifice limbajului C++

1. Spații de nume (namespace)


Un spațiu de nume oferă o modalitate de a organiza obiecte și funcții. Acest lucru ajută la evitarea conflictelor
de nume și este o caracteristică a limbajului C ++, nu este prezentă în limbajul C.
De exemplu, codul din biblioteca standard este într-un spațiu de nume numit std.
Definirea spațiului de nume începe cu cuvântul cheie namespace urmat de numele spațiului, ca mai jos:
namespace nume {
listă_declaratii;
}
Utilizarea spațiului de nume:
➢ Putem identifica membrii spațiilor de nume folosind operatorul de rezoluție ::.
➢ Putem utiliza o directivă using namespace nume_spațiu, pentru a facilita accesul la toți membrii
spațiului de nume respectiv (a se vedea Exemplul 1).
➢ Putem utiliza o directivă using pentru un anumit membru al unui spațiu de nume pentru a facilita
accesarea acelui membru al spațiului de nume (a se vedea Exemplul 2). Acest lucru reduce
semnificativ șansa unui conflict de nume.

Exemplul 1 – utilizarea directivei using namespace pentru spațiul de nume std


#include <iostream>
#include<cmath>

using namespace std; // se pot accesa toate elementele din spațiul de nume std

int main(void)
{
int num = 7;
double r = sqrt(num);
r = round(r * 1000) / 1000; // 3 zecimale
cout << "Radical din " << num << " este " << r;
return 0;
}
Exemplul 2 – utilizarea directivei using pentru obiectul cout din spațiul de nume std
#include <iostream>
#include<cmath>

using std::cout; //utilizarea obiectului cout

int main(void)
{
int num = 7;
double r = sqrt(num);
r = round(r * 1000) / 1000; // 3 zecimale
POO

cout << "Radical din " << num << " este " << r;
return 0;
}
În Exemplul 2, declarațiile folosesc operatorul de rezoluție :: pentru a crea un nume complet. Un nume complet
constă din spațiul de nume, operatorul de rezoluție și membrul spațiului de nume. De exemplu, numele
complet pentru obiectul cout este std :: cout.
Dacă nu folosim o directivă using, putem utiliza numele complet, ca în Exemplul 3.
Exemplul 3 – utilizarea numelui complet pentru obiectul cout
#include <iostream>
#include<cmath>

int main(void) {
int num = 7;
double root = std::sqrt(num);
root = std::round(root * 1000) / 1000; // round to 3 decimal places
std:: cout << "The square root of " << num << " is " << root;
return 0;
}

Definirea unui spațiu de nume


În exemplul de mai jos, sunt definite două spații de nume:
#include <iostream>
using namespace std;

namespace unu {
int a;
void afisare() {
cout << " spațiul unu: a = " << a << endl;
}
}
namespace doi {
double a;
void afisare() {
cout << " spațiul doi: a = " << a << endl;
}
}
int main(void) {
unu::a = 10;
unu::afisare();
doi::a = 3.7;
doi::afisare();
return 0;
}
Operatorul de rezoluție :: permite:
- accesul la o dată globală redefinită local.
- accesul la o entitate aparţinând unui spaţiu de nume, calificând-o cu numele spaţiului urmat de
operatorul de rezoluţie.
- accesul la o dată/funcţie membru ascunsă, calificând-o cu numele clasei urmat de operatorul de
rezoluţie.
Operatorul :: are prioritate maximă.
POO

Exemplul 4
#include<iostream>
using namespace std;

int x = 7; // variabila globala

int main()
{
int x = 10; // variabila locala
cout << "Valoarea globala x: " << ::x;
cout << "\nValoarea locala x: " << x;
return 0;
}

Exemplul 5
#include<iostream>
using namespace std;

class A
{
public:

void mesaj();
};

// Definitia functiei in afara clasei, folosind operatorul de rezolutie ::


void A::mesaj()
{
cout << "Apelare functie mesaj()";
}

int main()
{
A a;
a.mesaj();
return 0;
}

2. SupraÎncărcarea funcțiilor
Supraîncărcarea funcţiilor presupune existenţa mai multor funcţii diferite, dar cu acelaşi nume, în acelaşi
proiect. Restricţia care se impune la supraîncărcarea funcţiilor şi care este şi criteriul după care compilatorul
deosebeşte între ele funcţiile cu acelaşi nume, este ca tipul şi/sau numărul de parametri al funcţiilor
supraîncărcate să difere. Atenţie: tipul returnat de funcţii nu asigură de regulă suficiente informaţii pentru ca un
compilator să poată decide ce funcţie să folosească la apelare. Aşadar, funcţiile supraîncărcate pot să difere şi
prin tipul returnat, dar trebuie neapărat să difere prin numărul sau tipul parametrilor.

Exemplul 1
#include <iostream>
#include <math.h>

using namespace std;


float Media(int a, int b)
POO

//Media aritmetica a 2 intregi


{
cout << "media aritmetica a 2 intregi" << endl;
float Med = float(a + b) / 2;
return Med;
}

float Media(int a, float b)


//Modulul mediei aritmetice ponderate a unui intreg cu un real
{
cout << "modulul mediei aritmetice ponderate";
cout << " a unui intreg cu un real" << endl;
double Med = (0.25 * a + 1.25 * b) / 1.5;
return Med;
}

float Media(int a, int b, int c)


//Media geometrica a 3 intregi
{
cout << "media geometrica a 3 intregi" << endl;
float exp = 1. / 3;
float Med = pow(a * b * c, exp);
return Med;
}

void Media(int a, int b, float c)


//Media armonica a 2 intregi si un real fara //returnare de valoare
{
cout << "media armonica a 2 intregi si un ";
cout << "real fara returnare de valoare";
cout << endl;
float Med;

if (a != 0 && b != 0 && c != 0)
{
Med = 3 / (1 / a + 1 / b + 1 / c);
cout << "Media armonica testata Ma=";
cout << Med << endl;
}
else
{
cout << "Media armonica nu se poate";
cout << " calcula!" << endl;
}
}

int main()
{
double x, z;
float y;

cout << endl << "Se apeleaza ";


cout << "Media(7,8)=" << Media(7, 8) << endl;

//cout << endl << "Se apleaza ";


//cout << "Media(7,8.5)=" << Media(7,8.5);
//cout << endl;

cout << endl << "Se apeleaza ";


y = 8.5;
cout << "Media(7,y=8.5)=" << Media(7, y);
cout << endl;
POO

cout << endl << "Se apeleaza ";


x = Media(6, 8);
cout << "Media(6,8)=x=" << x << endl;

cout << endl << "Se apleaza ";


cout << "Media(5,8,9)=" << Media(5, 8, 9) << endl;

// z este de tip double. Ce se intampla?


cout << endl << "Se apeleaza ";
z = Media(5, 8, 9);
cout << "Media(5,8,9)=z=" << z << endl;

//cout << endl << "Se apeleaza ";


//float t = Media(5,8,9.5);
//cout << "Media(5,8,9.5)=t=" << t << endl;

y = 1.7;

cout << endl << "Se apeleaza ";


Media(3, 2, y);

//cout << endl << "Se apeleaza ";


//Media(3,2,1.7);

cout << endl << "Se apeleaza ";


Media(1, 5, 0);

cout << endl << "Se apeleaza ";


Media(1, y, 0);

cout << endl << "Se apeleaza ";


Media(1, 0, y);
return 0;
}

3. Argumente implicite
Argumentele implicite sunt o altă caracteristică a limbajului C++. Dacă supraîncărcarea funcţiilor permite
programatorului să utilizeze aceleaşi nume pentru mai multe funcţii, argumentele implicite permit unei funcţii să
fie utilizată ca şi cum ar fi mai multe funcţii.
La declararea funcţiei se pot atribui valori pentru o parte sau toţi parametrii funcţiei. Respectivii parametri
se vor numi în continuare impliciţi şi pot lipsi la apelul funcţiei, valorile lor fiind date de cele implicite. Şi în acest
caz însă există o restricţie: parametrii impliciţi sunt întotdeauna plasaţi la sfârşitul listei de parametri formali ai
funcţiei. De asemenea, nu sunt admise situaţii de genul: ultimul parametru implicit să fie prezent în apelul
funcţiei, iar penultimul să lipsească.

Exemplul 1
#include <iostream>
using namespace std;

// Prototipul functiilor – aici sunt specificate argumentele implicite


int fun1(int = 1, int = 2, int = 3);
int fun2(int, int, int = 3);
POO

int main() {
cout << fun1(4, 5, 6) << endl; // Nu se folosesc valorile implicite
cout << fun1(4, 5) << endl; // 4, 5, 3(implicit)
cout << fun1(4) << endl; // 4, 2(implicit), 3(implicit)
cout << fun1() << endl; // 1(implicit), 2(implicit), 3(implicit)

cout << fun2(4, 5, 6) << endl; // Nu se folosesc valorile implicite


cout << fun2(4, 5) << endl; // 4, 5, 3(implicit)
// cout << fun2(4) << endl;
// error: too few arguments to function 'int fun2(int, int, int)'
}

int fun1(int n1, int n2, int n3) {


return n1 + n2 + n3;
}

int fun2(int n1, int n2, int n3) {


return n1 + n2 + n3;
}

Exemplul 2
/*****************************
Parametri Impliciti
salut.cpp
*****************************/

#include <iostream>
#include <stdio.h>
using namespace std;

//Atentie la spatiul dintre * si = :


void Afis(const char* Sir0, const char*,const char* = "studentule", const char* = "!");
//Ultimii 2 parametri sunt impliciti

//Este gresita varianta de mai jos?


//void Afis(const char *Sir0, const char *Sir1, const char *Sir2="studentule", const char *Sir3="!");

//Ce greseala sesizati in varianta urmatoare?


//void Afis(const char *Sir0, const char *Sir1, const char *Sir2="studentule", const char *Sir3);

void main()
{
char Nume[20];

cout << "Introduceti un nume: ";


cin >> Nume;
cout << endl;

Afis("0-> ", "Salut ");


Afis("1-> ", "Salut ", Nume);
Afis("2-> ", "Spor la lucru, ");
Afis("3-> ", "Iti place C++, ", Nume, "?");

//Apeluri incorecte:
//Afis("4-> ",);
//Afis("5-> ","Iti place C++, ", ,"?");

}
POO

void Afis(const char* Sir0, const char* Sir1, const char* Sir2, const char* Sir3)
{
cout << Sir0 << Sir1 << Sir2 << Sir3 << endl;
}

4. Referințe
O referință este un alt nume sau un alias pentru o variabilă care deja există. Definirea unei referințe nu ocupă
memorie suplimentară. Orice operație facută cu referința este efectuată asupra variabilei la care se referă.
Sintaxa definirei unei referințe:

type &numeNou = numeVariabila;


// sau
type& numeNou = numeVariabila;
// sau
type & numeNou = numeVariabila;

int i = 5;
int& j = i;
Variabila j este o referință la variabila i. Modificarea lui j va modifica și valoarea lui i și invers, i și j vor avea
întotdeauna aceeași valoare.
Exemplul 1 – exemplificarea referințelor
#include <iostream>
using namespace std;

int main() {
int numar = 88;
int& refNumar = numar; // Declararea unei referinte (alias) la variabila numar

cout << numar << endl;


cout << refNumar << endl;

refNumar = 99;
cout << refNumar << endl;
cout << numar << endl;

numar = 55;
cout << numar << endl;
cout << refNumar << endl;
}

Exemplul 2 – pointeri vs. referințe


#include <iostream>
using namespace std;

int main() {
int var1 = 88, var2 = 22;

int* pVar1 = &var1; // pointerul pVar1 indica catre variabila var1


*pVar1 = 99;
cout << *pVar1 << endl;
POO

cout << &var1 << endl;


cout << pVar1 << endl;
cout << &pVar1 << endl;

pVar1 = &var2; // pointerul pVar1 indica catre variabila var2

int& refVar1 = var1; // Crearea unei referinte la var1


refVar1 = 11;
cout << refVar1 << endl;
cout << &var1 << endl;
cout << &refVar1 << endl;
//refVar1 = &var2; // Error! Reference cannot be re-assigned
// error: invalid conversion from 'int*' to 'int'
refVar1 = var2; // refVar1 este alias pentru var1.
// i se atribuie valoarea lui var2 (adica 22) variabilei referinta refVar1 (deci si lui var1).
var2++;
cout << refVar1 << endl;
cout << var1 << endl;
cout << var2 << endl;
}

În general există două modalităţi de realizare a transferului parametrilor:


- prin valoare (call by value);
- prin referinţă (call by reference).
Metoda implicită de transfer a parametrilor în limbajul C/C++ este cea prin valoare.
Transferul prin valoare al parametrilor
La transferul prin valoare al parametrilor, se face alocare (rezervare) de memorie pentru parametrii formali într-
o zonă specială de memorie numită stivă (stack). În celulele de memorie astfel rezervate se copie, în
momentul apelului, valorile parametrilor efectivi de apel, eventual convertite, aşa cum s-a arătat, la tipul
parametrului formal corespunzător. Operaţiile ce se execută în funcţie afectează celulele de memorie
rezervate parametrilor formali şi nu au nici un efect asupra parametrilor efectivi.
Ca urmare, la apelul prin valoare, transferul parametrilor este unidirecţional, adică dinspre funcţia apelantă
spre cea apelată, nu şi invers. Datorită acestui mod de transmitere a parametrilor, în C transferul parametrilor
de ieşire (dinspre funcţia apelată spre funcţia apelantă) trebuie făcut prin intermediul pointerilor.
Transferul parametrilor prin valoare are avantajul de a realiza o protecţie a parametrilor efectivi împotriva
alterărilor accidentale ce ar putea fi operate de către funcţia apelantă.
Exemplul 3
#include <iostream>
using namespace std;

void Interschimbare(int a, int b)


{
int tmp;
tmp = a;
a = b;
b = tmp;
};
int main()
{
int x = 1;
POO

int y = 2;
cout << "x=" << x << " y=" << y << endl;
Interschimbare(x, y);
cout << "x=" << x << " y=" << y << endl;
return 0;
}

Pentru a putea schimba valori ale unor variabile din funcţia apelantă prin modificări ale parametrilor efectivi de
apel, trebuie folosit mecanismul de transmitere al parametrilor prin referinţă. În acest caz, se transmit funcţiei
apelate, transparent faţă de utilizator, adresele parametrilor efectivi de apel. De aceea acest transfer se
numeşte prin referinţă sau prin adresă.
Transferul prin referință al parametrilor, folosind pointerii
Exemplul 4
#include <iostream>

using namespace std;

void Interschimbare(int* a, int* b)


{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
};

int main()
{
int x = 1;
int y = 2;
cout << "x=" << x << " y=" << y << endl;
Interschimbare(&x, &y);
cout << "x=" << x << " y=" << y << endl;
return 0;
}

Transferul prin referință al parametrilor, folosind referințe


Exemplul 5

#include <iostream>

using namespace std;

void Interschimba(int& a, int& b)


{
int tmp;
tmp = a;
a = b;
b = tmp;
};

int main()
{
int x = 1;
int y = 2;
POO

cout << "x=" << x << " y=" << y << endl;
Interschimba(x, y);
cout << "x=" << x << " y=" << y << endl;
return 0;
}
Tipul referinţă poate funcţiona şi ca tip returnat de o funcţie, caz în care apelul de funcţie poate figura şi în
stânga operatorului de atribuire.
Observaţie: în astfel de situaţii este important ca locaţia de memorie spre care se referă să nu fie eliberată la
încheierea execuţiei funcţiei (de exemplu referinţa către o variabilă locală!).
Exemplul 6
#include <iostream>
using namespace std;

int& fctRef(int&);
int* fctPointer(int*);

int main() {
int var1 = 8;
cout << "In main() &var1: " << &var1 << endl;
int& valRet = fctRef(var1);
cout << "In main() &valRet: " << &valRet << endl;
cout << valRet << endl; // 64
cout << var1 << endl; // 64

int var2 = 9;
cout << "In main() &var2: " << &var2 << endl;
int* pvalRet = fctPointer(&var2);
cout << "In main() pvalRet: " << pvalRet << endl;
cout << *pvalRet << endl; // 81
cout << var2 << endl; // 81
}

int& fctRef(int& rNumber) {


cout << "In fctRef(): " << &rNumber << endl;
rNumber *= rNumber;
return rNumber;
}

int* fctPointer(int* pNumber) {


cout << "In fctPointer(): " << pNumber << endl;
*pNumber *= *pNumber;
return pNumber;
}

Următorul program are o eroare logică gravă, deoarece funcția returnează o referință la o variabilă locală.
Variabila locală are domeniul de existență local în cadrul funcției, iar valoarea ei este distrusă după ieșirea din
funcție.
Exemplul 7
#include <iostream>
using namespace std;

int* fctPointer(int);
int& fctRef(int);
POO

int main() {
int var = 8;
cout << var << endl; // 8
cout << *fctPointer(var) << endl; // ??
cout << fctRef(var) << endl; // ??
}

int* fctPointer(int var) {


int rezultatLocal = var * var;
return &rezultatLocal;
}

int& fctRef(int var) {


int rezultatLocal = var * var;
return rezultatLocal;
}

5. For-ul îmbunătățit (ranged-based for loop )


C++11 introduce for-ul îmbunătățit pentru parcurgerea unui tablou:
Exemplul 8
#include <iostream>
using namespace std;

int main() {
int numere[] = { 11, 22, 33, 44, 55 };

// pentru fiecare element nr din vectorul numere


for (int nr : numere) {
cout << nr << " ";
}
cout << endl;
// pentru a modifica elementele, se utilizeaza referinte
for (int& nr : numere) {
nr = 99;
}
for (int nr : numere) {
cout << nr << " ";
}
cout << endl;
for (auto& nr : numere) {
cout << nr << " ";
}
return 0;
}

6. Struturi de date
Spre deosebire de tablou, o structură stochează elemente (câmpuri) de tipuri diferite, dar tot în locații
consecutive de memorie. După cum se va vedea, structurile de date reprezintă trecerea către clasele din C++.
struct nume_structura
{
// membrii structurii
}
POO

Exemplul 1
#include <iostream>
using namespace std;

enum ZilePeSapt { Luni, Marti, Miercuri, Joi, Vineri, Sambata, Duminica };


enum LuniPeAn { Ian, Feb, Mar, Apr, Mai, Iun, Iul, Aug, Sep, Oct, Nov, Dec };

struct Data
{
ZilePeSapt NumeZi;
int Zi;
LuniPeAn Luna;
int An;
};

int main()
{
LuniPeAn OLuna, AltaLuna;
Data DataNasterii, DataOarecare;

OLuna = Ian;
AltaLuna = Oct;

DataNasterii.NumeZi = Luni;
DataNasterii.Zi = 25;
DataNasterii.Luna = AltaLuna;
DataNasterii.An = 1982;

DataOarecare = DataNasterii;

cout << "Anul nasterii: " << DataNasterii.An << endl;


return 0;
}

Exemplul 2
În continuare se prezintă o aplicaţie a structurilor de date, în care este disponibil un pachet de cărţi, care
urmează a se amesteca. Rulaţi programul pas cu pas şi urmăriţi structura pachetului de cărţi şi felul în care se
modifică acesta.
/*****************************
Structuri de date:
Declaratii si incluziuni
carti.hpp
*****************************/
#include <iostream>
enum Culori { Inima, Frunza, Romb, Trefla };
struct Carte
{
int Numar;
Culori Culoare;
};

const int Jack = 11;


const int Dama = 12;
const int Rege = 13;
const int As = 14;

void Init(Carte* Pachetul); ////Initializarea pachetului


POO

void AfisareCarte(Carte c);


void AfisarePachet(Carte* Pachetul);
void Amestec(Carte* Pachetul); //Amesteca pachetul

/*****************************
Structuri de date
Functia principala
carti.cpp
*****************************/

#include "Carti.hpp"
using namespace std;
void main()
{
Carte Pachet[52]; //Pachet este un sir de 52 de carti

Init(Pachet);
cout << "Pachetul de carti ordonat:" << endl;
AfisarePachet(Pachet);
Amestec(Pachet);
cout << endl << "Pachetul de carti amestecat:";
cout << endl;
AfisarePachet(Pachet);
}

/*****************************
Structuri de date
functii.cpp
*****************************/
#include "carti.hpp"
using namespace std;

void Init(Carte* Pachetul)


//Initializeaza pachetul de carti
{
int num;
int j = 0;

for (num = 2; num <= 14; num++)


{
Pachetul[j].Numar = num;
Pachetul[j].Culoare = Inima;
Pachetul[j + 13].Numar = num;
Pachetul[j + 13].Culoare = Frunza;
Pachetul[j + 26].Numar = num;
Pachetul[j + 26].Culoare = Romb;
Pachetul[j + 39].Numar = num;
Pachetul[j++ + 39].Culoare = Trefla;
}
};

void AfisarePachet(Carte* Pachetul)


//Afiseaza toate cartile din pachet
{
for (int j = 0; j < 52; j++)
{
AfisareCarte(Pachetul[j]);
cout << " ";
if (!((j + 1) % 13)) cout << endl;
//salt la rand nou la fiecare 13 carti
}
};
POO

void AfisareCarte(Carte c)
//Afiseaza o carte din pachet
{
if (c.Numar >= 2 && c.Numar <= 10) cout << c.Numar;
else switch (c.Numar)
{
case Jack: cout << "J"; break;
case Dama: cout << "Q"; break;
case Rege: cout << "K"; break;
case As: cout << "A"; break;
}
switch (c.Culoare)
{
case Inima: cout << "i"; break;
case Frunza: cout << "f"; break;
case Romb: cout << "r"; break;
case Trefla: cout << "t"; break;
}
};

void Amestec(Carte* Pachetul)


/*Amesteca pachetul:
Fiecare carte este interschimbata cu o alta
din pachet aleasa in mod aleator */
{
for (int j = 0; j < 52; j++)
{
double i;
i = double(rand()) / (double)(RAND_MAX + 1);
int k = int(52 * i);
Carte Temp = Pachetul[j];
Pachetul[j] = Pachetul[k];
Pachetul[k] = Temp;
}
};

Exerciții propuse
1. Explicați rezultatul execuției codului de mai jos:
int* ptr = nullptr;
int a[] = { 12,32, 45,67,89, 90 };
ptr = a + 1;
cout << " *(ptr + 1) = " << *(ptr + 1) << endl;
cout << " *ptr + 1 = " << *ptr + 1 << endl;

2. Scrieți o funcție care returnează o referință la valoarea minimă a două numere întregi date ca
parametri. Folosind această funcție, afișați minimul dintre două numere întregi citite de la consolă.
3. Scrieți o funcție care convertește caracterele unui șir în majuscule. Funcția are prototipul după cum
urmează: void strToMajuscula (string &). Apelați această funcție într-un program de test.

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