Sunteți pe pagina 1din 166

- 3 -

Programarea calculatoarelor


ndrumar







C
C
+
+
+
+













Cuprins


Lucrarea nr. 1....................................................................................................... 7
Fundamentele limbajului C++. Partea I. .................................................................... 7
Identificatori ........................................................................................................... 7
Tipuri de date fundamentale................................................................................... 8
Constante ................................................................................................................ 9
Variabile ............................................................................................................... 13
Declaraia typedef................................................................................................. 18
Tipuri de date derivate.......................................................................................... 19
Tipuri de date definite de utilizator ...................................................................... 23
Operaii de intrare / ieire cu consola ....................................................................... 31
Lucrarea nr. 2..................................................................................................... 37
Fundamentele limbajului C++. Partea II.................................................................. 37
Operatori i expresii ............................................................................................. 37
Alocarea dinamic de memorie folosind operatorii new i delete ....................... 41
Operatorul de rezoluie (::) ................................................................................... 43
Conversii de tip n expresii................................................................................... 44
Instruciuni............................................................................................................ 45
Lucrarea nr. 3..................................................................................................... 52
Funcii n C/C++.......................................................................................................... 52
Funcii ................................................................................................................... 52
Definiii de funcii ................................................................................................ 52
Declaraii de funcii. Prototipuri........................................................................... 53
Transferul parametrilor......................................................................................... 54
Rezultatul unei funcii. Instruciunea return......................................................... 56
Pointeri de funcii ................................................................................................. 57
Parametri cu valori implicite ................................................................................ 59
Supradefinirea (suprancrcarea) funciilor.......................................................... 61
Funcii inline......................................................................................................... 64
Lucrarea nr. 4..................................................................................................... 68
Clase i obiecte (partea I)............................................................................................ 68
Tipul class............................................................................................................. 69
Tipurile struct i union.......................................................................................... 72
Autoreferina. Cuvntul cheie this .................................................................... 73
Constructori i destructori .................................................................................... 74
Crearea, iniializarea i eliminarea obiectelor ...................................................... 75
Constructor de copiere.......................................................................................... 77
Lucrarea nr. 5..................................................................................................... 83
Clase i obiecte (partea II) ..........................................................................................83
Manevrarea dinamic a obiectelor........................................................................83
Transferul obiectelor ca parametri sau rezultat ....................................................84
Clase cu membri obiecte.......................................................................................87
Tablouri de obiecte ...............................................................................................91
Pointeri ctre membrii unei clase. Operatorii ( .* ) i ( ->* ) ..............................93
Membri statici ai unei clase ..................................................................................95
Funcii i clase prietene unei clase........................................................................97
Lucrarea nr. 6................................................................................................... 102
Supradefinirea operatorilor .....................................................................................102
Funcia operator ..................................................................................................102
Supradefinirea operatorilor folosind funcii membre clasei ...............................103
Supradefinirea operatorilor folosind funcii prietene clasei ...............................105
Supradefinirea operatorilor unari........................................................................107
Supradefinirea operatorului de atribuire (=) .......................................................108
Supradefinirea operatorului [].............................................................................114
Supradefinirea operatorilor new i delete...........................................................117
Lucrarea nr. 7................................................................................................... 121
Conversii de tip definite de utilizator ......................................................................121
Procedee de definire a conversiilor.....................................................................121
Supradefinirea operatorului cast folosind funcii membre clasei .......................122
Supradefinirea operatorului cast folosind funcii prietene clasei .......................124
Conversii de tip folosind constructori.................................................................127
Lucrarea nr. 8................................................................................................... 132
Motenirea. Clase derivate .......................................................................................132
Declararea clasei derivate ...................................................................................132
Constructori i destructori pentru clasa derivat ................................................136
Constructorul de copiere pentru o clas derivat................................................138
Redefinirea funciilor membre............................................................................141
Compatibilitatea ntre o clas derivat i clasa de baz. Conversii de tip..........142
Supradefinirea operatorilor n clasele derivate...................................................144
Lucrarea nr. 9................................................................................................... 148
Crearea unei ierarhii de clase...................................................................................148
Motenirea multipl ..................................................................................................151
Lucrarea nr. 10................................................................................................. 158
Clase virtuale ......................................................................................................158
Funcii virtuale....................................................................................................162


- 7 -
Lucrarea nr. 1


Fundamentele limbajului C++. Partea I.

Identificatori
Identificatorii limbajului C++, ca i n limbajul C standard, sunt formai
cu ajutorul caracterelor alfanumerice i liniua de subliniere (underscore), _.
Primul caracter al unui identificator nu poate fi o cifr. De exemplu:
Raza // valid
mesaj // valid
_maxx // valid
a5 // valid
5a // invalid

Din mulimea identificatorilor posibili, se remarc cuvintele cheie
(reserved keywords) ale limbajului, identificatori a cror semnificaie nu poate
fi modificat de programator (Tabelul nr. 1).
Cuvintele cheie din tabel care ncep cu semnul underscor reprezint
variabile interne.
Tabelul nr. 1. Cuvinte cheie ale limbajului
Cuvinte cheie ale limbajului C++
_asm asm auto break case _cdecl
cdecl char class const continue _cs
default delete do double _ds else
enum _es _export extern _far far
_fastcall float for friend goto _huge
huge if inline int _interrupt interrupt
_loadds long _near near new operator
_pascal pascal private protected public register
return _saveregs _seg short signed sizeof
_ss static struct switch template this
typedef union unsigned virtual void volatile
while
Programare C++ - Lucrarea nr. 1

Tipuri de date fundamentale

Tipul unei date determin dimensiunea zonei de memorie ocupate i
valorile pe care le poate lua.
Tipurile datelor se pot grupa n tipuri fundamentale i tipuri derivate.
Tipurile de date fundamentale cuprind tipurile aritmetice de baz i
tipul void.
Exist patru tipuri aritmetice de baz, specificate prin cuvintele cheie:
char, int, float i double. Gama de valori poate fi extins prin modificatori de
tip desemnai prin cuvintele cheie: signed, unsigned, short, long. Tipurile
ntregi ce se obin prin combinarea tipurilor de baz cu modificatorii de tip sunt
prezentate n Tabelul nr. 2, iar cele reprezentate n virgul mobil n Tabelul nr.
3.
Tabelul nr. 2. Tipuri de date ntregi
Tip Spaiu de
memorie
ocupat
Domeniu de valori
char = signed char 8 bii -128127
unsigned char 8 bii 0255
int = signed int = short int =
signed short int
16 bii -3276832767
unsigned int = unsigned short int 16 bii 065535
long int = signed long int 32 bii -21474836482147483647
unsigned long int 32 bii 04294967295

Tabelul nr. 3. Tipuri de date n virgul mobil
Tip Spaiu de memorie ocupat Domeniu de valori
float 32 bii +/-(3.4E-383.4E+38)
precizie 7 cifre
double 64 bii +/-(1.7E-3081.7E+308)
precizie 15 cifre
long double 80 bii +/-(3.4E-49321.1E4932)
precizie 19 cifre
Tipul fundamental void indic absena oricrei valori i se utilizeaz n
Programare C++ - Lucrarea nr. 1

urmtoarele situaii: declaraia unei funcii fr parametri sau care nu returneaz
un rezultat, tipul pointer generic i conversii de tip cu operatorul cast pentru
pointeri.
Iat cteva exemple de expresii care returneaz ca valoare spaiul de
memorie ocupat de date de diferite tipuri:

sizeof (long int); // expresia returneaz valoarea 4
sizeof (unsigned char) // expresia returneaz valoarea 1
sizeof (long double) // expresia returneaz valoarea 10


Observaie: C++ admite delimitatorii /* */ pentru inserarea de comentarii care
se pot ntinde pe mai multe rnduri i, n plus, introduce
delimitatorul // pentru includerea de comentarii de sfrit de linie.
Tot textul care urmeaz dup delimitatorul // pn la sfritul liniei
este considerat comentariu.

Constante

Constantele sunt valori fixe (numerice, caractere sau iruri de caractere),
care nu pot fi modificate n timpul execuiei programului. Tipul constantei este
determinat de compilator pe baza valorii i sintaxei utilizate. Ele rmn n
memorie pe toat durata execuiei programului.
Constante ntregi

Tipul constantei este determinat pe baza valorii, sau prin utilizarea unui
sufix (U sau u pentru unsigned, respectiv L sau l pentru long).
Constantele ntregi pot fi zecimale, octale sau hexazecimale.

Constante zecimale (baza 10)
Constantele zecimale se disting prin faptul c prima cifr este diferit de
0, cum ar fi:

23 // tip int
23u // tip unsigned int
32768 // tip long int
77UL // tip unsigned long int


Constante octale (baza 8)
Programare C++ - Lucrarea nr. 1

Constantele octale sunt valori avnd prima cifr 0. Cifrele 8 i 9 sunt
ilegale.

-067 // tip int
067u // tip unsigned int
020000000 // tip long int
055ul // tip unsigned long
089 // eroare, prima cifr indic reprezentarea n octal i numrul include
// cifrele 8 i 9

Constante hexazecimale (baza 16)
Constantele hexazecimale se disting prin prefixul 0x sau 0X. Pot conine
cifre mai mari de 9 (af, sau AF).

0x7FFF // tip int
0X8000 // tip unsigned int
0xffu // tip unsigned int
0x10000 // tip long int
0xFFul // tip unsigned long int

Constante n virgul mobil

Constantele n virgul mobil sunt valori raionale a cror reprezentare
conine n general urmtoarele cmpuri:
parte ntreag
punct zecimal
parte fracionar
e sau E i un exponent cu semn (opional)
sufix de specificare a tipului: f sau F (foreaz tipul float) sau l sau L
(foreaz tipul long double).
Se pot omite partea ntreag sau partea fracionar (dar nu amndou),
punctul zecimal sau litera e (E) i exponentul (dar nu amndou).
Tipul implicit pentru constantele n virgul mobil este tipul double.

2.1 // valoare 2,1 (tip double)
11.22E5 // valoare 11,22 x 10
5
(tip double)
-.33e-2 // valoare 0,33 x 10
-2
(tip double)
.5 // valoare 0,5 (tip double)
1. // valoare 1 (tip double)
1.f // valoare 1 (tip float)
0.L // valoare 0 (tip long double)
Programare C++ - Lucrarea nr. 1

Constante caracter

Constantele caracter sunt reprezentate de unul sau mai multe caractere
ncadrate de apostrofuri, de exemplu: a,A,\n i ele sunt de tip char.
Pentru a specifica o serie de caractere neafiabile, delimitatorii (), (),
caracterul (\), etc. se utilizeaz secvenele escape (secvene de evitare) (vezi
Tabelul nr. 4).

Tabelul nr. 4. Secvene escape
Secven Caracter Descriere
\a alarm (bell) sun alarma
\b BS backspace
\f FF formfeed
\n LF linefeed
\r CR carrige return
\t TAB tab orizontal
\v VT tab vertical
\\ \ backslash
\ apostrof
\ ghilimele
\? ? semnul ntrebrii
\o orice caracter ir de cifre octale
\xH orice caracter ir de cifre hexazecimale

Exemple de utilizare a constantelor caracter:

#include <stdio.h>

void main()
{
putch(?); // se afieaz caracterul ?
putch(63); // se afieaz caracterul ? , 63 fiind valoarea
// corespunztoare n codul ASCII
printf(\n%c,\077); // se afieaz caracterul ?
printf(\n%c,\x3F); // se afieaz caracterul ?
}

Observaie: n C, toate constantele de un singur caracter au tipul int i sunt
reprezentate intern pe 16 bii cu octetul mai semnificativ 0 pentru
valoarea caracterului mai mic dect 128, sau 1 (0xFF) pentru
valori n intervalul 128255.
Programare C++ - Lucrarea nr. 1

Constante caracter duble (specifice pentru C++)

Limbajul C++ permite specificarea unor constante alctuite din dou
caractere, reprezentate pe 16 bii (tipul int) ca n exemplul care urmeaz:

ab // reprezentarea n memorie ca ntreg de valoare 25185 (0x62 0x61)
\t\t // reprezentarea n memorie ca ntreg de valoare 2313 (0x09 0x09)

Primul caracter este memorat n octetul mai puin semnificativ.

Observaie: Constantele duble pot ridica probleme de portabilitate, ele nefiind
recunoscute de alte compilatoare.

Constante iruri de caractere

Constantele iruri de caractere sunt alctuite dintr-un numr oarecare de
caractere, ncadrate ntre ghilimele.
irurile de caractere se memoreaz n tablouri de tipul char, cu dimensiune
egal cu numrul de caractere cuprinse ntre ghilimele, la care se adaug
terminatorul de ir \0.

Exemplu de constanta sir de caractere

Caracterele ce alctuiesc irul pot fi secvene escape:

\tMesaj 1\n\tMesaj 2

irurile constante adiacente se concateneaz i formeaz un singur ir:

Programare orientata pe
obiecte

Pentru scrierea irurilor lungi se poate utiliza simbolul (\) care semnaleaz
continuarea irului pe rndul urmtor:

Exemplu de sir \
scris pe doua randuri



Programare C++ - Lucrarea nr. 1

Variabile
Declaraii de variabile

Toate variabilele trebuie declarate nainte de a fi folosite.
Declaraia unei variabile (obiect) precizeaz numele (identificatorul) cu
care va fi referit, cruia i poate asocia o serie de atribute, cum ar fi:
- tipul datei poate fi tip fundamental sau definit de utilizator i determin
structura, gama de valori, dimensiunea spaiului ocupat n memorie;
- clasa de memorare stabilete zona de memorie n care se va plasa
informaia asociat identificatorului (segment de date, registru, stiv,
heap) i delimiteaz durata sa de alocare;
- domeniul reprezint poriunea de program n care poate fi accesat
informaia asociat identificatorului, el fiind determinat de poziia
declaraiei;
- durata de via a identificatorului reprezint perioada ct exist efectiv n
memorie i este corelat cu clasa de memorie;
- legtura precizeaz modul de asociere a unui identificator cu un anumit
obiect sau funcie, n procesul de editare a legturilor.
Atributele se pot asocia identificatorilor n mod implicit, n funcie de
poziia i sintaxa declaraiei sau explicit prin utilizarea unor specificatori.
Sintaxa unei declaraii de variabil impune specificarea tipului, avnd
forma general:

tip_var nume_var;

tip_var este un specificator de tip de date oarecare, standard, pointer sau
definit de utilizator.

float * r; // declararea variabilei r de tip pointer la float,
unsigned int n; // declararea variabilei n de tip unsigned int

Se pot face declaraii de variabile cu iniializare utiliznd sintaxa:

tip_var nume_var= valoare_initiala;

sau se pot declara mai multe variabile de acelai tip utiliznd sintaxa:

tip_var nume_var1<=val_initiala1>,< nume_var2<= val_initiala2>>,...;

double real=2.5; // declararea variabilei real de tip double, iniializat cu valoarea 2.5
Programare C++ - Lucrarea nr. 1

char c1, c2=a, ch; // declararea a trei variabile de tip char, c1, c2 i ch, variabila c2
// fiind iniializat cu valoarea a

Poziia declaraiei determin cele dou domenii de existen fundamentale:
- Domeniul bloc (local)
Identificatorii cu domeniu bloc se numesc locali i sunt rezultatul unor
declaraii n interiorul unui bloc (au domeniul cuprins ntre declaraie i
sfritul blocului) sau sunt parametrii formali din definiia unei funcii
(au ca domeniu blocul funciei).
- Domeniul fiier (global)
Identificatorii cu domeniu fiier se numesc globali i sunt declarai n
afara oricror funcii (domeniul este cuprins ntre declaraie i sfritul
fiierului).

#include <stdio.h>

// zona declaraiilor globale

int functie (int, float);
int a; // se declar variabila cu numele a, creia i se rezerv o zon de
// memorie de 2 octei (16 bii) localizat n segmentul de date,
// deoarece declaraia se afl n zona declaraiilor globale

// definiii de funcii

void main (void)
{
unsigned char c; // se declar variabila automatic cu numele c creia i se rezerv un
// octet n segmentul de stiv, variabila fiind o variabil local
float r=2.5; // se declar variabila automatic cu numele r creia i se rezerv 4
// octei n segmentul de stiv, variabila fiind o variabil local care este
// iniializat cu valoarea 2.5
}

int functie (int n, float q) // se declar variabilele locale n i q, crora li se aloc 2,
// respectiv 4 octei n segmentul de stiv
{

}

Observaii: 1. ntr-un bloc inclus n domeniul unei declaraii este permis o
declaraie local a aceluiai identificator, asocierea fiind ns fcut
unui alt obiect cu alocarea altei zone de memorie.
2. Spre deosebire de C, care impune gruparea declaraiilor locale la
nceputul unui bloc, C++ permite plasarea declaraiilor n interiorul
blocului, bineneles nainte de utilizarea obiectului.
Programare C++ - Lucrarea nr. 1


void main (void)
{

for (int i=5; i<8; i++) // declararea lui i este imediat urmat de utilizarea sa
{}
for {i=0; i<5; i++) // i a fost declarat anterior
{}

}

Clasa de memorare se poate preciza prin specificatorii auto, static,
extern, register. Aceti specificatori precizeaz modul de alocare a memoriei i
timpul de via pentru variabile i legtur pentru funcii i variabile.

auto
Declaraia auto specific o variabil automatic i se poate utiliza pentru
variabile cu domeniul local (cu spaiu alocat pe stiv). Variabilele ce se declar
n interiorul unui bloc sunt implicit automatice, astfel c declaraia auto este rar
folosit.

void fct()
{
auto float r; // declararea unei variabile automatice cu declarare explicit
double s; // declararea unei variabile implicit automatic
...
}

static
Declaraia static a unei variabile locale foreaz durata static fr a
modifica domeniul de existen. Variabilele statice i pstreaz valoarea ntre
dou apeluri succesive ale blocurilor care le conin, asigurndu-se n acelai
timp o protecie, dar ele nu pot s fie accesate din blocuri care nu constituie
domeniul lor de existen. Variabilele statice pot fi declarate cu iniializare, n
caz contrar, implicit se iniializeaz cu valoarea 0, similar variabilelor globale.

#include <stdio.h>

int fct()
{
static int a=2; // se declar o variabil local funciei, cu durat static
return (a++); }
void main(void)
{
int n;
Programare C++ - Lucrarea nr. 1

n=fct();
printf (\n Prima atribuire : n= %d, n);
n=fct();
printf (\n A doua atribuire : n= %d, n);
}

Programul afieaz:

Prima atribuire : n= 3 // la primul apel al funciei, variabila a are valoarea
// iniial 2, ea fiind apoi incrementat
A doua atribuire : n= 4 // la al doilea apel al funciei, variabila a are la nceput
// valoarea 3 (valoare datorat apelului anterior al
// funciei), valoarea fiind apoi incrementat

register
Declaraia register are ca efect memorarea variabilei ntr-un registru al
procesorului i nu n memorie, avnd ca rezultat creterea vitezei de execuie a
programului. Aceste variabilele pot fi variabile locale, nestatice, de tip int sau
char. Numai dou variabile pot fi memorate simultan n registre, n cazul
existenei mai multor declaraii register, cele care nu pot fi onorate vor fi tratate
de compilator ca variabile obinuite.

register char c; // declararea variabilei c cu memorare ntr-un registru al procesorului

extern
Specificatorul extern indic legtura extern i asigur durata static
pentru variabile locale i globale sau pentru funcii (acestea au implicit legtur
extern i durat static).
Pentru identificatorii cu legtur extern sunt permise mai multe declaraii
de referin, dar o singur definiie. De exemplu, n cazul unui proiect n care o
variabil se folosete n mai multe fiiere, ea se va defini global ntr-un fiier, n
celelalte fiind declarat extern fr definire. Compilatorul i va aloca o singur
dat memorie.

// Fiier Ex_prj.cpp

#include <stdio.h>

extern int m; // se declar legtur extern pentru variabila m

void main(void)
{

scanf(%d, &m);
printf(\nm= %#x, m);
Programare C++ - Lucrarea nr. 1


}

// Fiier Ex_prj1.cpp

...
int m ; // declaraia variabilei m



n exemplul anterior, cele dou fiiere, Ex_prj.cpp i Ex_prj1.cpp, se
includ ntr-un proiect utiliznd opiunea project a meniului principal din
mediul de programare C/C++. Variabila m este declarat global ntr-unul dintre
fiiere, ea putnd fi referit din cellalt fiier datorit specificrii legturii
extern .

Modificatori de acces

Modificatorii de acces ce pot fi utilizai sunt const i volatile i ei pot
controla modul de modificare a unei variabile.

const
Variabilele const nu pot fi modificate pe parcursul execuiei unui
program. Instruciunile care ncearc modificarea variabilelor const genereaz
erori la compilare.

const int a=99; // declaraia variabilei a const
a++; // eroare, a este const
a+=5; // eroare, a este const

O variabil pointer constant nu se poate modifica, n schimb se poate
modifica obiectul indicat.
n cazul unui pointer ctre un obiect constant, valoarea pointerului este
modificabil, dar nu i valoarea obiectului.

void main()
{
int var1=25, var2=44;
const int var3=7;
int * p1=&var1; // declaraie cu iniializare
int * p2=&var3; // eroare, nu se poate converti const int * la int *
const int * p3=&var3; // corect, tipul de date corespund
var3++; // eroare, se ncearc modificarea unui obiect constant
Programare C++ - Lucrarea nr. 1

*p3=15; // eroare, se ncearc modificarea unui obiect constant
int * const p4=&var1; // corect, se declar un pointer constant
p4=&var2; // eroare, se ncearc modificarea unui pointer constant
*p4++; // corect, se modific valoarea obiectului var2 care nu
// este declarat constant
}

n mod uzual se folosesc funcii cu parametri pointeri ctre obiecte
constante atunci cnd se dorete protejarea la modificare a acestora.

Un exemplu este o funcie care realizeaz criptarea unui ir de caractere:

#include <stdio.h>
#include <string.h>

void criptare(const char *sir_i, char *sir_c); // parametrul sir_i este declarat const pentru a
// evita modificarea lui n interiorul funciei
// criptare()
void main()
{
char sir_initial[80], sir_criptat[80];
sir_initial=Exemplu criptare;
criptare(sir_initial, sir_criptat);
printf(\nSirul criptat este: %s, sir_criptat);
}

void criptare( const char *sir_i, char *sir_c)
{
while (*sir_i)
{
*sir_c = * sir_i+1 ; // criptarea se face prin modificarea codului ASCII
sir_i ++ ;
sir_c ++ ;
}
*sir_c=\0;
}

volatile
Variabilele volatile pot fi modificate din exteriorul programului (de
exemplu servirea unei ntreruperi).


Declaraia typedef

Specificatorul typedef nu declar un obiect, ci asociaz un nume unui tip
Programare C++ - Lucrarea nr. 1

de date. Sintaxa este:

typedef tip_data identificator_tip

typedef unsigned char octet; // tipul unsigned char este echivalent cu octet
octet var; // variabila var se declar de tip octet


Tipuri de date derivate
Pointeri de date

Tipul pointer (indicator) reprezint adrese ale unor zone de memorie
(adrese de variabile sau constante).
Exist, de asemenea, pointeri generici, numii pointeri void, care pot
conine adresa unui obiect de orice tip.

Declararea variabilelor pointer

Sintaxa declaraiei unui pointer de date este:

tip * id_ptr;

Simbolul * precizeaz c id_ptr este numele unei variabile pointer, iar tip
este tipul obiectelor a cror adres o va conine (tipul de baz al id_ptr).
Compilatorul interpreteaz zona de memorie adresat de pointer ca obiect
de tipul indicat in declaraie, cu toate atributele tipului: dimensiunea zonei de
memorie necesar i semnificaia informaiei coninute.
Declararea unui pointer generic se face folosind sintaxa:

void * id_vptr;

n cazul acestei declaraii, dimensiunea zonei de memorie adresate i
interpretarea informaiei nu sunt definite, iar proprietile difer de ale celorlali
pointeri de obiecte.

int * ptr; // pointer la ntreg
int * tabptr[10]; // tablou de pointeri ctre ntregi
float **pptr; // dubl indirectare; pointer la pointer

Este important c orice variabil pointer trebuie iniializat cu o valoare
Programare C++ - Lucrarea nr. 1

valid, 0 (NULL) sau adresa unui obiect nainte de a fi utilizat. In caz contrar,
efectele pot fi grave deoarece la compilare sau n timpul execuiei nu se fac
verificri ale validitii valorilor pointerilor.

Operatori specifici.

Exist doi operatori unari care permit folosirea variabilelor pointer:
operatorul & se folosete pentru aflarea adresei unei variabile oarecare i
operatorul * pentru accesul la variabila adresat de un pointer.

#include <stdio.h>

void main(void)
{
int var=20, *pvar;
printf("\nVariabila var se afla la adresa %p, &var);
printf("si are valoarea var= %d", var);
pvar=&var;
printf("\nVariabila pvar are valoarea %p, pvar);
printf("si adreseaza obiectul: %d", *iptr);
*iptr=50;
printf("\nNoua valoare a lui var este %d, var);
}

Programul afieaz:

Variabila var se afl la adresa: FFF4 si are valoarea iv= 20
Variabila pvar are valoarea: FFF4 si adreseaza obiectul: 20
Noua valoare a lui var este 50

Situaia:
tipl * id_pl;
tip2 * id_p2;
id_pl=&id_p2;

nu genereaz erori dac tip1 i tip2 sunt identice, sau, n caz contrar dac tip1
este void.
Dac se folosete un pointer void, pentru orice referire a obiectului adresat
este necesar precizarea explicit a tipului utiliznd operatorul cast.

void main()
{ void *p1;
float *p2, var=1.5;
p2=&var;
p1=p2;
Programare C++ - Lucrarea nr. 1

printf(\nValoarea referita de p1: %f , * (float *) p1);
}

Variabile referin

C++ ofer posibilitatea de a declara identificatori ca referine de obiecte
(variabile sau constante). Referinele, ca i pointerii, conin adrese. Pentru a
declara o referin la un obiect se folosete simbolul &, folosind sintaxa:

tip & id_referinta = nume_obiect;

tip este tipul obiectului pentru care se declar referina, iar simbolul &
precizeaz c id_referinta este numele unei variabile referin, iar nume_obiect
este obiectul a crui adres va fi coninut n id_referinta.

int n; // se declar n de tip ntreg
int * p=&n; // se declar pointerul p cu iniializare adresa lui n
int &r=n; // se definete r referina lui n

n=20; // n primete valoarea 20
*p=25; // n primete valoarea 25
r=30; // n primete valoarea 30

n exemplul anterior, att p, ct i r, acioneaz asupra variabilei n.
Atunci cnd se acceseaz o variabil prin referina sa, nu este necesar s
se foloseasc adresa, acest lucru realizndu-se automat.
Spre deosebire de pointeri, care la un moment dat pot primi ca valoare
adresa unei alte variabile, referinele nu pot fi modificate, ele fiind practic o
redenumire a variabilei a cror adres o conin (se creeaz un alias al respectivei
variabile).
n utilizarea referinelor, trebuie avute n vedere urmtoarele restricii:
referinele trebuie iniializate n momentul declarrii;
referinelor nu li se pot modifica locaiile la care se refer;
nu sunt permise referine la cmpuri de bii, referine la referine i
pointeri la referine, deci nici tablouri de referine.

int &r ; // eroare, se declar o referin fr iniializare

int &r=20 ; // corect, se declar o referin la o constant

const int i = 20;
int &r = i ; // eroare, se declar o referin ntreag la o constant ntreag

Programare C++ - Lucrarea nr. 1

const int i = 20;
const int &r = i ; // corect, se declar o referin constant ntreag la o constant
// ntreag

Observaie: Referinele de sine stttoare sunt rar folosite. n schimb, utilizarea
parametrilor formali referin permite transferul prin referin
simplu i eficient, fr recurgerea la parametri formali pointeri.

Tablouri de date

Tabloul de date (sau masiv de date) este o colecie de date de acelai tip,
plasate ntr-o zon contigu de memorie.
Sintaxa declaraiei unui tablou cu N dimensiuni este:

tip_element nume_tablou [dimensiune1][dimensiune2][dimensiuneN]

Zona de memorie rezervat conine

dimensiune1 x dimensiune2 x...x dimensiuneN

elemente de tipul tip_element.
Referirea unui element de tablou se face cu operatorul de indexare [] sub
forma:
nume_tablou [indice1][indice2]...[indiceN]

Declaraia unui tablou se poate face cu iniializarea sa folosind sintaxa:

declaratie_tablou={list_valori};
Lista de valori trebuie s conin constante de tip compatibil cu tipul de
baz al tabloului, n ordinea plasrii n memorie.
float vect1[5]; // se declar un tablou cu 5 elemente float, fr iniializare
vect1[0]=1.5; // elementului de index 0 i se atribuie valoarea 1.5
int vect2[10]={2,7,-1,0,9,15,-5,22,6,11}; // se declar un tablou cu 10 elemente int cu
// iniializare
int mat[2][3]={{3,5,-3},{2,-1,0}}; // se declar un tablou bidimensional cu 2*3
// elemente de tip ntreg, cu iniializare
mat[1][2]= 23; // elementului de indeci 1, respectiv 2, i se
// atribuie valoarea 23

Tablourile unidimensionale cu elemente de tip char sunt folosite pentru
memorarea irurilor de caractere. Pentru a marca sfritul unui ir de caractere,
dup ultimul caracter se adaug un octet cu valoarea 0 (\0), numit i terminator
Programare C++ - Lucrarea nr. 1

de ir.

char sir1[10]; // se declar un tablou unidimensional, cu elemente char
// (ir de caractere), fr iniializare
char sir3[ ]=sir de caractere; // se declar un tablou cu 17 elemente char (ir de
// caractere) cu iniializare (ultimul caracter depus n
// tablou este terminatorul de ir \0
sir3[0]=S; // primul caracter al irului primete valoarea S

Numele unui tablou fr index este un pointer constant de tipul
elementelor tabloului i are ca valoare adresa primului element al tabloului.

float tab[20], *ptr;
ptr=tab; // atribuire valid, pointerul ptr va conine adresa
// primului element al tabloului
&tab[0] = = tab ;
&tab[2] = = tab+2 ;
tab[0] = = *tab ;
tab[2] = = *(tab+2) ;
tab++ ; // eroare, tab este un pointer constant, deci nu se poate incrementa
ptr++ ; // corect, ptr este un pointer la float, nu a fost declarat constant

Tablourile multidimensionale reprezint tablouri cu elemente tablouri,
astfel nct numele tabloului (fr index) este un pointer de tablouri.

float mat[10][10]; // mat reprezint un tablou de pointeri float
float *p; // p este un pointer float
p=mat; // eroare, tipurile pointerilor difer
p=(float*)mat; // corect, s-a folosit o conversie explicit de tip
mat = = &mat[0][0]; // expresie adevrat
mat+1 = = &mat[1][0]; // expresie adevrat
*(mat+9) = = mat[9][0]; // expresie adevrat
Tipuri de date definite de utilizator

Tipurile de date fundamentale i derivate nu pot acoperi totdeauna
necesitile de organizare a informaiei, n numeroase situaii fiind necesar
asocierea de date de tipuri diferite.
Vom prezenta n continuare posibilitile oferite de limbajul C pentru
definirea unor tipuri de date cu un grad de complexitate sporit. Acestea sunt:
- enumerarea, care este o list de identificatori cu valori constante de tip
ntreg;
- structura, care este o colecie de date de tipuri diferite referite cu acelai
nume;
- cmpul de bii, care este un membru al unei structuri cruia i se aloc n
Programare C++ - Lucrarea nr. 1

memorie un numr de bii n interiorul unui cuvnt;
- uniunea, care permite utilizarea aceleiai zone de memorie de obiecte de
tipuri diferite.
Noiunile puse n discuie n aceast lucrare de laborator sunt cele comune
celor dou limbaje, urmnd ca, ntr-o lucrare ulterioar tratarea acestora s se
completeze cu aspecte specifice limbajului C++.

Enumerarea

Tipul enumerare const dintr-un ansamblu de constante ntregi, fiecare
asociat unui identificator. Sintaxa declaraiei este:

enum <id_tip_enum> {id_elem<=const>,} <lista_id_var>;

unde: id_tip_enum = nume tip enumerare;
id_elem = nume element;
const = constant de iniializare a elementului.

Dac declaraia nu specific o constant, valorile implice sunt 0 pentru
primul element, iar pentru celelalte valoarea elementului precedent incrementat
cu o unitate.
Identificatorii elementelor trebuie s fie unici n domeniul lor (diferii de
numele oricrei variabile, funcie sau tip declarat cu typedef).

enum boolean {false, true}; // valoarea identificatorului false este 0, iar a lui true este 1
sau
typedef enum {false, true} boolean; // declaraia este echivalent declaraiei anterioare

# include <stdio.h>

enum boolean {false, true}; // valoarea identificatorului false este 0, iar a lui true este 1

void main()
{
boolean op1=false, op2;
op2=true;
printf(\n op1=%d\nop2=%d, op1, op2);
}

Tipul enumerare faciliteaz operarea cu variabile care pot lua un numr
mic de valori ntregi, asociindu-se nume sugestive pentru fiecare valoare.
Programul devine mai clar i mai uor de urmrit.

Programare C++ - Lucrarea nr. 1

Structuri

Structura este o colecie de date referite cu un nume comun. O declaraie
de structur precizeaz identificatorii i tipurile elementelor componente i
constituie o definiie a unui nou tip de date.
Sintaxa declaraiei unui tip structur este:

struct id_tip_struct {
tip_elem1 id_elem1;
tip_elem2 id_elem2;

tip_elemN id_elemN;} lista_id_var_struct;

unde: struct = cuvnt cheie pentru declararea tipurilor structur;
id_tip_struct = numele tipului structur declarat;
tip_elemK = tipul elementului K, unde K=1N;
id_elemK = numele elementului K;
lista_id_var_struct = lista cu numele variabilelor de tipul declarat,
id_ti_struct.

Pot s lipseasc, fie numele structurii, fie lista variabilelor declarate, dar
nu amndou. De regul se specific numele tipului structur definit, aceasta
permind declaraii ulterioare de obiecte din acest tip.

struct data // se declar tipul de date data, ca structur cu 3 membri de tip
// unsigned int
{ unsigned int zi;
unsigned int luna;
unsigned int an;
};
struct persoana // se declar tipul de date persoana ca structur cu 2
// membri ir de caractere i un membru de tip data
{
char nume[15];
char prenume[15];
data data_n;
} pers1, pers2; // odat cu declaraia tipului de date persoana, se declar
// dou obiecte din acest tip de date

Se poate declara un tip structur cu ajutorul declaraiei typedef cu sintaxa:

typedef struct {
tip_elem1 id_elem1;
tip_elem2 id_elem2;
Programare C++ - Lucrarea nr. 1


tip_elemN id_elemN;} id_tip_struct;

unde semnificaia denumirilor este identic cu cea anterioar.
Se reia exemplul anterior, utiliznd typedef pentru declaraia structurilor:

typedef struct
{
unsigned int zi;
unsigned int luna;
unsigned int an;
} data;

typedef struct
{
char nume[15];
char prenume[15];
data data_n;
} persoana;


Elementele structurii se numesc generic membrii (cmpurile) structurii.
Tipurile membrilor pot fi oarecare, mai puin tipul structur care se definete,
dar pot fi de tip pointer la structura respectiv.
Iniializarea unei variabile structur se face prin enumerarea valorilor
membrilor n ordinea n care apar n declaraie.

persoana pers={Ionescu, Adrian, 10, 10, 1975}; // declararea unui obiect persoana
// cu iniializare

Referirea unui membru al unei variabile de tip structur se face folosind
operatorul de selecie (.) (punct), sub forma:

nume_variabil . nume_cmp

printf(\nNumele: %s, pers.nume);
printf(\nPrenume: %s, pers.prenume);
printf(\nData nasterii: %d.%d.%d, pers.data_n.zi, pers.data_n.luna, pers.data_n.an);

Referirea unui membru al unei structuri indicate de un pointer se face
folosind operatorul de selecie indirect (->) (sgeat).

persoana * p_pers; // se declar un obiect pointer la persoana

puts(\nIntroduceti numele:);
Programare C++ - Lucrarea nr. 1

scanf(%s, &p_pers->nume);
puts( \nIntroduceti prenumele:)
scanf(%s, &p_pers->prenume);
puts( \nIntroduceti data nasterii:);
scanf(%d.%d.%d, &p_pers->data_n.zi, &p_pers->data_n.luna, &p_pers->data_n.an);

Operatorul de atribuire admite ca operanzi variabile structur de acelai
tip, efectund toate atribuirile membru cu membru.

persoana p1={Popescu, George, 5, 12, 1982}, p2; // p1 se declar cu iniializare
p2=p1; // prin atribuire, p2 preia, membru
// cu membru datele din p1
printf(\nNumele:%s, p2.nume);
printf(\nPrenume:%s, p2.prenume;
printf(\nData nasterii: %d.%d.%d, p2.data_n.zi, p2.data_n.luna, p2.data_n.an);

Transferul unui parametru de tip structur se poate face prin valoare.
Parametrul formal i cel efectiv trebuie s fie de acelai tip.

#include <stdio.h>

typedef struct complex {int re, im;} ;

complex suma(complex a, complex b)
{
complex c;
c.re=a.re+b.re;
c.im=a.im+b.im;
return c;
}

void main()
{
complex c1, c2, c3;
scanf("%d", &c1.re);
scanf("%d", &c1.im);
scanf("%d", &c2.re);
scanf("%d", &c2.im);
c3=suma(c1, c2);
printf("\n%i %i", c3.re, c3.im);
}

Transferul unui parametru de tip structur se poate face prin referin.
Parametrii efectivi trebuie s fie adrese de structuri de acelai tip cu parametrii.

#include <stdio.h>

Programare C++ - Lucrarea nr. 1

typedef struct complex {int re, im;} ;

complex suma(complex *a, complex *b)
{
complex c;
c.re=a->re+b->re;
c.im=a->im+b->im;
return c;
}

void main()
{
complex c1, c2, c3;
c1.re=12;
c1.im=-7;
c2.re=29;
c2.im=35;
c3=suma(&c1, &c2);
printf("\n%i %i", c3.re, c3.im);
}

Pentru transferul parametrilor de tip structur se pot folosi variabile
referin de structur de acelai tip.

#include <stdio.h>
typedef struct complex {int re, im;} ;
complex suma(complex &a, complex &b)
{
complex c;
c.re=a.re+b.re;
c.im=a.im+b.im;
return c;
}

void main()
{
complex c1, c2, c3;
puts(\nIntroduceti valorile:
scanf(%d, &c1.re);
scanf(%d, &c1.im);
scanf(%d, &c2.re);
scanf(%d, &c2.im);
c3 = suma(c1,c2);
printf(\nc3=%d + i* %d", c3.re, c3.im);
}

Programare C++ - Lucrarea nr. 1

Cmpuri de bii

Cmpul de bii este un membru al unei structuri sau uniuni care are alocat
un grup de bii n interiorul unui cuvnt de memorie.
Declaraia unei structuri cu membrii cmpuri de bii se face sub forma:

struct id_tip_struct
{
tip_elem1 id_elem1 : lungime;
tip_elem2 id_elem2 : lungime;

tip_elemN id_elemN : lungime;
}
lista_id_var_struct;

unde lungime reprezint numrul de bii utilizai pentru reprezentarea n
memorie a cmpului respectiv.
Restriciile care se impun la declararea cmpurilor de bii sunt:
tipul poate fi int signed sau unsigned;
lungimea este o constant ntreag cu valoarea n domeniul 0-15;
nu se poate evalua adresa cmpului, deci nu se poate folosi
operatorul & pentru acel cmp;
nu se pot folosi tablouri de cmpuri de bii.
Referirea membrilor se face ca pentru orice structur, folosind operatorii
de selecie direct sau indirect (punct sau sgeat).

#include <stdio.h>

typedef struct
{
unsigned int zi:5; // se declar membrul zi cu reprezentare pe 5 bii
unsigned int luna:4; // se declar membrul luna cu reprezentare pe 4 bii
unsigned int an:11; // se declar membrul an cu reprezentare pe 11 bii
} DATE;

void main()
{
DATE data_n; // se declar un obiect de tip DATE
unsigned int aux1, aux2, aux3;
puts(Introduceti data :)
scanf(%2d.%2d.%4d, &aux1, &aux2, &aux3); // este necesar utilizarea unor variabile
// auxiliare pentru citirea valorilor,
// deoarece pentru cmpurile de bii nu se
// poate face referire la adres
Programare C++ - Lucrarea nr. 1

data_n.zi=aux1;
data_n.luna=aux2;
data_n.an=aux3;
....}

Se poate observa c zona de memorie ocupat de un obiect date este de 3
octei, spre deosebire de structura care nu include cmpuri de bii care ocup 6
octei.

printf(data_n ocupa %d octeti, sizeof(data_n));

Uniuni

Uniunea permite utilizarea n comun a unei zone de memorie de ctre mai
multe obiecte de tipuri diferite. Sintaxa de declarare a unei uniuni este similar
declaraiei unei structuri:
union id_tip_uniune {
tip_elem1 id_elem1;
tip_elem2 id_elem2;

tip_elemN id_elemN;} lista_id_var_uniune;
sau
typedef union {
tip_elem1 id_elem1;
tip_elem2 id_elem2;

tip_elemN id_elemN;} id_tip_uniune;

Spaiul alocat n memorie corespunde tipului de dimensiune maxim,
membrii uniunii utiliznd n comun zona de memorie.


#include <stdio.h>

struct octet{ unsigned int b0:1; // se declar o structur care ocup un octet de memorie,
unsigned int b1:1; // cu acces separat, prin membrii si, la fiecare bit n
unsigned int b2:1; // parte
unsigned int b3:1;
unsigned int b4:1;
unsigned int b5:1;
unsigned int b6:1;
unsigned int b7:1;
};
Programare C++ - Lucrarea nr. 1


union intreg
{ char val; // se declar o uniune ce ocup un octet de memorie care poate
octet bit; // fi accesat ca i char prin membrul val, sau ca octet prin
// membrul bit
};
void main ()
{
intreg i;
i.val=22;
// se afieaz fiecare bit al uniunii separat:
printf("\n0x%x se reprezinta in binar: %d%d%d%d%d%d%d%d", i.val, i.bit.b7,
i.bit.b6, i.bit.b5, i.bit.b4, i.bit.b3, i.bit.b2, i.bit.b1, i.bit.b0);
}

Programul afieaz:

0x16 se reprezinta in binar 00010110




Operaii de intrare / ieire cu consola

C++ permite utilizarea funciilor de intrare/ieire C, ns dispune de un
sistem de intrare/ieire conceput n spiritul POO, mai flexibil i mai comod.
Acest sistem este prezentat sumar n continuare.
n C++ sunt predefinite dispozitive logice de intrare/ieire standard
similare celor din limbajul C:

cin = console input = dispozitiv de intrare consol, tastatura (echivalent
cu stdin din C);
cout = console output = dispozitiv de ieire consol, monitorul (echivalent
cu stdout din C).
cerr = dispozitiv de ieire pentru afiarea erorilor, fr memorare
(echivalent cu stderr din C).
clog = dispozitiv de ieire pentru afiarea erorilor, cu memorare (nu are
echivalent n C).

Transferul informaiei cu formatare este efectuat de operatorul >> pentru
intrare (de la cin) i de << pentru ieire (ctre cout), deci:

cin >> var; /* citete var de la cin */
Programare C++ - Lucrarea nr. 1

cout << var; /* scrie var la cout */

Sunt posibile operaii multiple, de tipul:

cin >> var1 >> var2 ... >> varN;
cout << var1 << var2 ... << varN;

n acest caz, se efectueaz succesiv, de la stnga la dreapta, scrierea la cout,
respectiv citirea de la cin a valorilor varl ... varN.

Tipurile datelor transferate ctre cout pot fi:
- toate tipurile aritmetice;
- iruri de caractere;
- pointeri de orice tip n afar de char.

Tipurile datelor citite de la cin pot fi:
- toate tipurile aritmetice;
- iruri de caractere.
Controlul formatului pentru ambele operaii este posibil, dar nu este
obligatoriu deoarece exist formate standard. Acestea sunt satisfctoare pentru
dialogul cu consola efectuat n exemplele din capitolele urmtoare.
Pentru citirea/scrierea irurilor de caractere se poate specifica o constant
ir sau un pointer de caractere. Din acest motiv, pentru afiarea adresei unui ir
este necesara o conversie explicit la pointer de alt tip, de exemplu (void *).
Valorile adreselor se afieaz n hexazecimal.
Operaiile de citire de la tastatur efectuate cu operatorul >> sunt similare
cu cele efectuate cu scanf(). Delimitatorii cmpurilor introduse sunt spaiu, tab,
linie noua, etc. In cazul citirii unui caracter nevalid, citirea este ntrerupt i
caracterul rmne n tamponul de intrare genernd probleme similare cu cele
care apar la utilizarea funciei scanf0.
Utilizarea dispozitivelor i operatorilor de intrare/ieire C++ impune
includerea fiierului antet iostrearm.h. Exemplul urmtor este elocvent pentru
elegana i simplitatea dialogului cu consola n C++.

#include <iostream.h>
void main()
{
int i;
char nume[21];
float r;
cout < < "introduceti un numar intreg si apoi un numar real: ;
cin>> i >> r;
cout << "\nAti introdus: "<< i << "si" << r <<'\n';
Programare C++ - Lucrarea nr. 1

cout << "Introduceti numele dvs: ;
cin >> nume;
cout<< "Salut, " < < nume << " !\n ;
}

Programul afieaz:

Intruduceti un numar intreg si apoi un numar real:15 3.1416
Ati introdus:15 si 3.1416
Introduceti numele dvs: ANDREI
Salut, ANDREI !

Pentru afiare se pot utiliza expresii:

#include <iostream.h>

int main() {
int num;
cin >> num;
cout << num + 1; } // se afieaz rezultatul returnat de expresia num+1
Pentru citirea/scrierea irurilor de caractere se poate specifica o constant
ir sau un pointer de caractere. Din acest motiv, pentru afiarea adresei unui ir
este necesar o conversie explicit la pointer de alt tip, de exemplu (void *).
Valorile adreselor se afieaz n hexazecimal.

#include <iostream.h>

void main()
{
char sir[20]=Sir de caractere; // se declar un ir de caractere cu iniializare
cout<<sir<<\n; // se afieaz coninutul sirului de caractere sir
cout<<*sir<<\n; // se afieaz primul caracter din irul de caractere sir
cout<<&sir<<\n; // se afieaz adresa la care se afl variabila pointer sir
cout<<(void*)sir<<\n; // se afieaz adresa la care se afl variabila pointer sir
cin>>*sir; // se citete un alt caracter pentru prima poziie a irului
cout<<sir<<\n; // se afieaz coninutul irului de caractere sir
cin>>sir; // se citete o nou valoare pentru irul de caractere
cout<<sir<<\n; // se afieaz coninutul irului de caractere sir
char * p_sir=abc; // declar un pointer la tipul char ce se iniializeaz cu
// adresa irului constant abc
cout<<p_sir<<\n; // se afieaz coninutul irului de caractere ctre care
// pointeaz p_sir
cout<<*p_sir<<\n; // se afieaz primul caracter din irul constant de
// caractere
cout<<&p_sir<<\n; // se afieaz adresa la care se afl variabila pointer
// p_sir
cout<<(void*)p_sir<<\n; // se afieaz adresa coninut de variabila pointer p_sir,
Programare C++ - Lucrarea nr. 1

// deci adresa la care se afl irul constant abc
}

Programul afieaz:

Sir de caractere
S
0xffe0
0xffe0
s //caracterul introdus de la tastatur
sir de caractere
Alt sir de caractere // irul introdus de la tastatur
Alt sir de caractere
abc
a
0xfff4
0x00be

Concatenarea irurilor de caractere la afiare se poate realiza ca n
exemplul urmtor:

#include <iostream.h>

int main()
{
cout << "Acesta este un sir prea lung "
"pentru a fi scris pe un singur rand.\n"
"El poate fi continuat pe randul urmator.\n"
}

Programul afieaz:

Acesta este un sir prea lung pentru a fi scris pe un singur rand.
El poate fi continuat pe randul urmator.


Observaie: Trebuie reinut c operatorii >> i << i pstreaz i semnificaia
din C - operatori de deplasare bit cu bit la dreapta, respectiv la
stnga.


Pentru formatarea intrrilor i ieirilor sunt definii o serie de
manipulatori. De exemplu, ntregii pot fi nsoii de dec, oct, hex, pentru
reprezentarea n zecimal, octal, respectiv hexazecimal. Valoarea implicit este
dec. De asemenea, se poate folosi endl care insereaz caracterul \n i golete
Programare C++ - Lucrarea nr. 1

tamponul de ieire i ends care insereaz caracterul \0 (terminator de ir de
caractere).

// Specificarea formatului de afiare cu utilizarea manipulatorilor

#include <iostream.h>

int main() {
cout << "un numar in zecimal: " << dec << 15 << endl;
cout << "in octal: " << oct << 15 << endl;
cout << "in hex: " << hex << 15 << endl;
cout << "un numar in virgula mobila: " << 3.14159 << endl;
cout << "caracter neafisabil (escape): " << char(27) << endl;

// Convertirea unui numar n zecimal, octal i hexazecimal

#include <iostream.h>
int main()
{
int numar;
cout << "Introdu un numar zecimal: ";
cin >> numar; // citirea unui numr zecimal
cout << "valoarea in octal = 0" << oct << numar <<endl; // afiarea numrului in octal
cout << "valoarea in hex = 0x" << hex << numar<<endl; // afiarea numrului n
// hexazecimal
cout << "Introdu un numar hexazecimal: ";
cin >>hex>> numar; // citirea unui numr hexazecimal
cout << "valoarea in octal = 0" << oct << numar<<endl; // afiarea numrului in octal
cout << "valoarea in zecimal ="<< dec<< numar<<endl; // afiarea numrului n zecimal

cout << "Introdu un numar octal: ";
cin >>oct>> numar; // citirea unui numr octal
cout << "valoarea in zecimal="<< dec << numar<<endl; // afiarea numrului n zecimal
cout << "valoarea in hex = 0x" << hex << numar<<endl; // afiarea numrului n
// hexazecimal
}



Exerciii:

1. S se creeze un fiier cu extensia .h n care s se declare funcii cu
diferite prototipuri, cu liste de parametri i valori returnate de tip void,
char, int sau float. Funciile vor fi definite ntr-un fiier cu extensia .cpp
ce include fiierul header definit anterior, ele afind numele funciei,
valorile argumentelor i tipul returnat. Un al treilea fiier, tot cu extensie
Programare C++ - Lucrarea nr. 1

.cpp, va include headerul definit i va conine funcia main() ce va apela
toate funciile definite. S se compileze i s se execute programul.

2. Ce vor afia urmtoarele secvene de program:

a. #include<iostream.h>
void main ()
{ int x=5,y;
x=x<<3;
cin>>y;
cout<<(x+y);}



b. #include<iostream.h>
void main()
{ int x,y;
cin>>x>>y;
x=x/y;
cout<<"x="<<x;}



c. #include<iostream.h>
void main()
{ char *p;
p="salut";
cout<<p<<" salut";}


d. #include<iostream.h>
void main()
{ int x=2,y=7,z=4;
char w='f';
cout<<x<<y<<" "<<z;
cout<<"\n"<<w;}

3. Care va fi valoarea final a variabilei "j", n urma rulrii secvenelor de
mai jos:

a. ..........
int i=3;
int &j=i; j++;
i=7; j--;
......


b. .................
float i=3.14;
int &j=i;
j+=3;i-=1;
j++;
..............
4. Care va fi valoarea final a variabilei "i", n urma rulrii secvenelor de
mai jos:

a. ..........
const int i=3;
int &j=i; j++;
......


b. ................
float i=3.14;
int &j=i;
j+=3;i-=1;
j++;
..............


- 37 -
Lucrarea nr. 2



Fundamentele limbajului C++. Partea II.

Operatori i expresii

Expresiile sunt componente importante n alctuirea oricrui program.
Expresiile sunt combinaii de operanzi (date) i operatori. C++ posed toi
operanzii limbajului C, completnd lista cu operanzi proprii: operatorii new i
delete utilizai pentru alocarea dinamic de memorie, operatorul de scop ( :: ),
operatorul pointer la membru ( .* ) i forma sa echivalent ( ->. ) .
Operatorii limbajului C++ sunt prezentai n tabelul urmtor:

Tabelul nr. 5. Operatorii limbajului C++
[ ] ( ) . -> ++ --
& * + - ~ !
/ % << >> < >
<= >= = = != ^ |
&& | | ? : = * = / =
% = + = - = <<= >>= &=
^ = | = , # ##
sizeof
new delete :: .* ->*

Observaie: Operatorii # i ## aparin preprocesorului.

Nivelurile de prioritate i ordinea de evaluare n cazul n care operatori
consecutivi intr n alctuirea aceleiai expresii sunt prezentate n Tabelul nr. 6.
Programare C++ - Lucrarea nr. 2

- 38 -

Tabelul nr. 6 Prioritatea i ordinea de evaluare a operatorilor
Clasa de
prioritate
Operatori Asociativitate
1 () [] - :: . de la stnga la dreapta
2 ! ~ + - ++ -- & *
(tip) sizeof new delete
de la dreapta la stnga
3 .* ->* de la stnga la dreapta
4 * / % de la stnga la dreapta
5 + - de la stnga la dreapta
6 << >> de la stnga la dreapta
7 < <= > >= de la stnga la dreapta
8 = = != de la stnga la dreapta
9 & de la stnga la dreapta
10 ^ de la stnga la dreapta
11 | de la stnga la dreapta
12 && de la stnga la dreapta
13 || de la stnga la dreapta
14 ?: (operator condiional) de la dreapta la stnga
15 = *= /= %= += -=
&= ^= |= <<= >>=
de la dreapta la stnga
16 , (operator virgul) de la stnga la dreapta


Pentru a modifica ordinea de evaluare specificat n Tabelul nr. 6 se pot
utiliza parantezele ( ) care vor impune ordinea gruprii operanzilor i
operatorilor dorit de utilizator n evaluarea expresiilor.
n funcie de numrul de operanzi crora li se aplic, operatorii pot fi
clasificai n operatori unari (cu un operand), operatori binari (cu doi operanzi) i
ternari (cu trei operanzi).


Operatori aritmetici

operatorul unar - : se aplic unei expresii n vederea schimbrii
semnului acesteia;
operatorii binari +, -, *, / : se aplic la doi operanzi de tip numeric;
operatorul binar %: produce restul mpririi a dou date ntregi,
deci nu se poate aplica la operanzi float sau double.
Programare C++ - Lucrarea nr. 2

- 39 -

Observaie: mprirea ntregilor produce un ntreg prin trunchierea prii
fracionare a rezultatului.


int a=2, b=3, c;
c= 2*(a/b+1); // variabila c primete valoarea 2
c=a%b; // variabila c primete valoarea 2
c=b%a; // variabila c primete valoarea 1


Operatori relaionali i logici

Acetia sunt:
operator unar de negaie: ! ;
operatori relaionali (binari) : >, >=, <, <=;
operatori de egalitate (binari): = =, ! =;
operatori logici (binari): &&, ||.

if ( !(a<=0 || b<=0)
return (c && d);
else
return (c || d);


Operatori de incrementare i de decrementare

Operatorii de incrementare (++) i de decrementare (--) sunt operatori
unari i au ca efect adunarea cu 1, respectiv scderea, operandului su. Acetia
pot fi prefixai sau post fixai. n primul caz, modificarea valorii operandului se
face nainte de a-i folosi valoarea, n al doilea caz se folosete valoarea iniial a
operandului i apoi se efectueaz modificarea acesteia.

int i=5, j;
j = ++i // i=6, j=6;
j = i-- ; // i=5, j=6;

Aceti operatori se pot aplica numai variabilelor.

++5 ; // eroare, operatorul de incrementare se aplic unei constante
(i+j)-- ; // eroare, operatorul de decrementare se aplic unei expresii


Programare C++ - Lucrarea nr. 2

- 40 -

Operatori asupra biilor

Aceti operatori nu se pot aplica tipurilor float i double.

~ unar complement fa de 1
& binar I bit cu bit
| binar SAU bit cu bit
^ binar SAU EXCLUSIV bit cu bit
<< binar deplasare la stnga
>> binar deplasare la dreapta


int a=7, b=2, c;
~ a; // expresia returneaz valoarea -8
a&b; // expresia returneaz valoarea 2
a|b; // expresia returneaz valoarea 7
a^b; // expresia returneaz valoarea 5
a<<b; // expresia returneaz valoarea 28
a>>b; // expresia returneaz valoarea 1


Operatori de atribuire

Expresii de tipul:
e1 = (e1) operator (e2);

pot fi scrise ntr-o form condensat :

e1 operator= e2;

unde (operator= ) este operator de asignare n care operator poate fi unul dintre:

+ - * / % << >> & ^ |

i = i+2; echivalent cu i+=2;
x= x*(y+1); echivalent cu x*=y+1;
a=a>>b; echivalent cu a>>=b;



Programare C++ - Lucrarea nr. 2

- 41 -
Operator condiional

Operatorul condiional este operatorul ternar ? : . Se folosete n expresii
sub forma:
e1 ? e2 : e3;

Expresia e1 este evaluat prima. Dac valoarea acesteia este logic
adevrat (nonzero), se evalueaz expresia e2 i aceasta este valoarea returnat
de expresia condiional, n caz contrar, se evalueaz expresia e3, valoarea
acesteia fiind cea returnat.

z=(a>b) ? a : b; // z primete ca valoare maximul dintre a i b

Expresia anterioar este echivalent cu secvena:

if (a>b)
z=a;
else
z=b;


Alocarea dinamic de memorie folosind operatorii new i delete

C++ introduce o nou metod pentru alocarea dinamic a memoriei
adaptat programrii orientate ctre obiecte.
Alocarea memoriei se face cu operatorul unar new, folosind urmtoarea
sintax:

var_ptr = new tip_var; // 1
var_ptr = new tip_var(val_init) // 2
var_ptr = new tip_var[dim1][dim2][dimN] // 3

unde: - var_ptr = o variabil pointer de un tip oarecare;
- tip_var = tipul variabilei dinamice var_ptr;
- val_init = expresie cu a crei valoare se iniializeaz variabila dinamic;

Varianta 1 aloc spaiu de memorie corespunztor unei date de tip tip_var,
fr iniializare.
Varianta 2 aloc spaiu de memorie corespunztor unei date de tip tip_var,
cu iniializare.
Varianta 3 aloc spaiu de memorie corespunztor unei tablou cu elemente
Programare C++ - Lucrarea nr. 2

- 42 -
de tip tip_var de dimensiune dim1*dim2*...*dimN. Iniializarea tabloului nu
este posibil.
Dac alocarea de memorie a reuit, operatorul new returneaz adresa
zonei de memorie alocate. n caz contrar , returneaz valoarea NULL (=0) (n
cazul n care memoria este insuficient sau fragmentat).

int n=3, * p1, *p2, *p3, *p4;
p1=new int; // variabil ntreag neiniializat
p2=new int(15); // variabil ntreag iniializat cu valoarea 15
p3=new int[n]; // tablou unidimensional int de dimensiune n
p4=new int[2][n]; // tablou bidimensional int de dimensiune 2*n

n exemplul urmtor se fac alocri repetate de memorie pn cnd se
epuizeaz ntregul spaiu din memorie:

#include <iostream.h>
#include <stdlib.h>

void main()
{
double *var_ptr;
long dim;
cout<<\n Introduceti dimensiunea blocului de memorie:;
cin>>dim;
for(int i=1; ; i++) // n absena condiiei de ieire din ciclul for, ieirea se va face
// forat prin funcia exit() n momentul n care alocarea de
// memorie nu se mai poate face, deci variabila pointer var_ptr
// va primi valoarea 0 (NULL)
if (var_ptr=new double[dim])
cout<<\n Alocare bloc nr. <<i";
else
{
cout<<\nAlocare imposibila;
exit(1);
}
}


Eliminarea variabilei dinamice i eliberarea zonei de memorie se face cu
operatorul delete, folosind sintaxa:

delete var_ptr;

unde var_ptr conine adresa obinut n urma unei alocri cu operatorul new.
Programare C++ - Lucrarea nr. 2

- 43 -
Utilizarea altei valori este ilegal, putnd determina o comportare a programului
nedefinit.

int *p;
p=new int(10); // se aloc spaiul de memorie necesar unui int i se face
// iniializarea cu valoarea 10
delete p; // se elimin variabila p din memorie

int *p,*q;
p=new int(7); // se aloc spaiul de memorie necesar unui int i se face
// iniializarea cu valoarea 7
q=p; // variabila pointer q primete ca valoare adresa coninut n
// variabila p
delete q; // se elibereaz zona de memorie de la adresa coninut n
// variabila q
*p=17; // incorect, folosete o zona de memorie care a fost deja
// dealocat

int x=7;
int *y=&x;
delete y; // incorect, se cere dealocarea unei zone de memorie care nu a
// fost alocat cu operatorul new


Operatorul de rezoluie (::)

n C++ este definit operatorul de rezoluie (::), numit i operator de
acces (de domeniu, scop), care permite accesul la un identificator cu domeniul
fiier dintr-un bloc n care acesta nu este vizibil datorit unei alte declaraii.

char sir[]=Sir global; // se declar variabila global sir

void fct()
{
char *sir
sir=Sir local; // se declar variabila local sir
puts(::sir); // se afieaz irul global
puts(sir); // se afieaz irul local
}
....

#include<iostream.h>

int a=3; // se declar variabila global a cu iniializare
Programare C++ - Lucrarea nr. 2

- 44 -
void f(int);

void main()
{
int a=10; // se declar variabila local funciei main() cu iniializare
f(a); // se apeleaz funcia f() cu argumentul efectiv variabila local
// funciei main(), a
cout<<(::a); // se afieaz variabila global a
}

void f(int a) // definirea funciei cu parametrul formal a
{ a++; // se incrementeaz variabila local funciei f()
(::a)++; // se incrementeaz variabila global
}

Conversii de tip n expresii

Conversiile de tip se pot realiza implicit sau explicit.
Dac ntr-o expresie apar operanzi de tipuri numerice diferite, ei se
convertesc pentru fiecare operaie. n ordinea efecturii operaiilor se face n
prealabil o conversie, astfel nct ambii operanzi s fie de acelai tip.
Regula de baz const n conversia operandului de tip cu domeniu de
valori mai mic ctre tipul cu domeniul de valori mai mare al celuilalt operand.
Rezultatul fiecrei operaii este de tipul stabilit pentru operanzi.
Conversii de tip se fac i n cazul atribuirilor. Valoarea membrului drept
este convertit la tipul celui stng. n aceast situaie se pot efectua conversii de
la un tip cu reprezentare pe un numr mai mare de octei la un tip cu
reprezentare pe numr mai mic de octei, ceea ce poate provoca trunchieri (ex.:
float ->int), rotunjiri (ex: double->float) ale valorilor sau pierderea biilor de
ordin superior n exces (ex: long int->char).

int i=2;
float r=3.5; // valoarea constant 3.5, care are reprezentare double, este convertit la
// tipul float i apoi este atribuit variabilei r
(i+2.1)*r // 2.1 este de tip double, deci valoarea de tip int a variabilei i se
// convertete la tipul double, rezultatul returnat de operaia i+2.1 este
// de tip double, ca urmare i valoarea variabilei float r se convertete la
// double, rezultatul final al expresiei fiind i el de tip double

Conversia explicit de tip se poate utiliza n orice expresie utilizndu-se o
construcie numit typecast n construcii de forma:

Programare C++ - Lucrarea nr. 2

- 45 -
(nume_tip) expresie
sau
nume_tip (expresie)

int i=11;
printf(%d, i/2); // afieaz 5 ambii operanzi sunt de tip int, deci i
// valoarea returnat este de tip int
printf(%f, (float)i/2); // afieaz 5.5 se impune conversia operandului i la
// tipul float, deci i operandul 2 se va converti la acelai
// tip i n final tipul valorii returnate este tot float
printf(%f, i/2); // afiare eronat se ncearc afiarea unei valori de tip
// int n format float

Instruciuni

O instruciune este o expresie care se ncheie cu punct i virgul (;).
Instruciunile se pot scrie pe mai multe linii de program, spaiile
nesemnificative fiind ignorate.

int i;
i=
5;

Pe o linie de program se pot scrie mai multe instruciuni.

int i, j;
i=2; j+=i;


Instruciunea vid

Instruciunea vid are forma:
;

i este utilizat pentru a evita utilizarea unei etichete sau atunci cnd corpul unui
ciclu nu conine nici o instruciune.


Instruciunea compus (blocul de instruciuni)

Instruciunea compus grupeaz declaraii i instruciuni. Blocul de
Programare C++ - Lucrarea nr. 2

- 46 -
instruciuni este sintactic echivalent cu o instruciune. Forma general este:

{ lista_declaratii;
lista_instructiuni;}

Toate variabilele declarate n interiorul unei instruciuni compuse nu pot fi
accesate n afara acesteia, domeniul lor de existen fiind blocul de instruciuni.

#include <stdio.h>

void main()
{
int i; // se declar i
i=7;
{ // nceputul blocului
int i; // se redefinete i
int j; // se declar j
i=9;
j=2*i;
printf(\ni=%d, i); // se afieaz i=9
} // sfritul blocului
printf(\ni=%d, i); // se afieaz i=7
printf(\nj=%d, j); // eroare, instruciunea se afl n afara domeniului de
// existen a lui j
}

Instruciunile compuse se utilizeaz deseori n combinaie cu
instruciunile de ciclare i cele condiionale.


Instruciunea condiional if, if-else

Sintaxa instruciunii poate fi:

if(expresie) instruciune_1
sau
if(expresie) instruciune_1 else instruciune_2

Instruciunea if evalueaz expresia expresie. n cazul n care valoarea
acesteia este nenul, se execut instruciune_1 , iar dac este nul se execut
instruciunea imediat urmtoare instruciunii if, pentru prima form a sintaxei
sau instruciune_2 n cazul celei de a doua forme.

Programare C++ - Lucrarea nr. 2

- 47 -
if (a>b)
cout<<a>b\n;
else
if (a==b)
cout <<a=b\n;
else
cout<<a<b\n;


Instruciunea de ciclare while

Forma instruciunii este:

while (expresie) instruciune;

Att timp ct expresie este nenul, se execut instruciune. Evaluarea
expresiei are loc nainte de execuia instruciunii.

.......
i=n;
while (i)
{
cout<<\ni=<<i;
i--;
}


Instruciunea de ciclare do-while

Forma instruciunii este:

do instr while (expr);

Instruciunea instr se execut att timp ct expr este nenul. Evaluarea
expresiei expr se face dup executarea instruciunii instr.

.........
i=n;
do
{
cout<<\ni=<<i;
i--;
}
while (i);
Programare C++ - Lucrarea nr. 2

- 48 -
Instruciunea de ciclare for

Sintaxa general a instruciunii for este:

for( expr1; expr2; expr3)
instruciune;

expr1 se evalueaz o singur dat, la intrarea n bucla for;
expr2 se evalueaz naintea fiecrei iteraii i reprezint condiia de
ieire din ciclu;
expr3 se evalueaz la sfritul fiecrei iteraii, pentru actualizarea
parametrilor ciclului.

for(i=n; i; i--)
cout<<\ni=<<i;


Instruciunea switch

Instruciunea switch este o instruciune decizional, ea permind selecia,
n funcie de valoarea unei expresii, ntre mai multe opiuni posibile. Sintaxa
instruciunii este:

switch (expr)
{
case const1: lista_instructiuni;
<break;>
case const2: lista_instructiuni;
<break;>
..
<default> lista_instructiuni;
}
unde:
expr este o expresie ntreag (orice tip ntreg sau enumerare);
const1, const2,... sunt constante de selecie, de tip ntreg, cu valori distincte;
lista_instructiune este o secven de instruciuni;

Instruciunea break ntrerupe execuia instruciunii switch. Dac
lista_instruciuni nu este urmat de break, se continu execuia cu lista de
instruciuni ataat urmtoarei etichete.

Programare C++ - Lucrarea nr. 2

- 49 -
char cifra;
int i;
...
switch (cifra)
{
case 0: i=0; break
case 1: i=1; break
...
case F: i=15; break
}


Instruciunea break

Instruciunea are forma:
break;

i are ca efect terminarea execuiei unui ciclu de tip while, do-while, for sau a
instruciunii switch, controlul fiind transferat instruciunii imediat urmtoare.


Instruciunea continue

Instruciunea are forma:
continue;

i are ca efect trecerea la urmtoarea iteraie ntr-un ciclu while, do-while sau
for.


Instruciunea return

Formele admise sunt:
return;
sau
return (expr);
sau
return expr;

Efectul instruciunii este de a trece controlul la funcia care a apelat
funcia ce conine instruciunea return, fr transmiterea unei valori n prima
form i cu transmiterea valorii expr n celelalte.
Programare C++ - Lucrarea nr. 2

- 50 -
Instruciunea goto

Instruciunea goto are ca efect saltul la eticheta specificat. Sintaxa sa
este:

goto et;

Eticheta et este specificat folosind sintaxa:

et :


Observaie: Instruciunea goto ncalc principiile programrii structurate i
reduce claritatea programului, astfel nct este recomandat evitarea
ei.


Exerciii:

1. S se defineasc o funcie care inverseaz un ir de caractere.

2. S se scrie o funcie htoi() care convertete un ir de cifre hexazecimale n
valoarea sa ntreag echivalent. Cifrele sunt de la 0 la 9, iar literele de la
a la f i de la A la F.


3. Ce afieaz secvenele:

a. #include <iostream.h>

int a=2;

void main ()
{
a=3;
a++;
cout<<(::a);
}


b. #include<iostream.h>
int a=3;
int f(int);

void main()
{ int a=5; a++;
cout<<"\t"<<f(a);}

int f(int a)
{ a++; cout<<(::a);
return a; }

Programare C++ - Lucrarea nr. 2

- 51 -
4. Ce se ntmpl la compilarea secvenelor:

a. .........
int *p;
p=new int[7];
.........


b. .........
int *p,*q;
p=new int(7);
q=p;
delete q;
*p=17;
...........
c. ..........
int *p;
p=new int(10);
delete p;
.............

d. ........
int x=7;
int *y;
delete y;
*y=7;
.........



- 52 -
Lucrarea nr. 3


Funcii n C/C++

Funcii

n limbajul C++ (similar cu limbajul C standard) programul este o colecie
de module distincte numite funcii, structura general a programului fiind:

<directive preprocesor>
<declaraii globale>
funcii

Un program C++ conine obligatoriu o funcie numit main() i aceasta
este apelat la lansarea n execuie a programului.
Programul surs poate fi partiionat n mai multe fiiere grupate ntr-un
proiect (se utilizeaz meniul Project din mediul de programare utilizat). Fiecare
fiier conine declaraii globale i un set de funcii, dar numai unul conine
funcia main().
n C++, ca i n C, se utilizeaz declaraii i definiii de funcii.


Definiii de funcii

Sintaxa definiiei unei funcii este:

tip_rez nume_funcie (<lista_parametri>)
{
declaraii locale
secven de instruciuni
}
unde:
Programare C++ - Lucrarea nr. 3

- 53 -
- tip_rez este un tip oarecare de dat i reprezint tipul rezultatului returnat de
funcie. Dac nu este specificat, implicit tipul rezultatului returnat este int.
Pentru funciile care nu returneaz rezultat trebuie s se specifice tipul void.
- nume_functie este un identificator.
- lista_parametri reprezint enumerarea declaraiilor parametrilor sub forma:

tip_parametru nume_parametru, <tip_parametru nume_parametru>

Tipul parametrului poate fi orice tip valid de date.
Nu este admis definirea unei funcii n blocul altei funcii i nu sunt
permise salturi cu instruciunea goto n afara funciei.
Apelul funciei const din numele funciei urmat de lista de constante,
variabile sau expresii asociate parametrilor ncadrat ntre paranteze ().
Att la definirea, ct i la apelul funciilor, parantezele () urmeaz
ntotdeauna numele funciilor, chiar dac acestea nu au parametri.
Se folosete denumirea de parametri formali pentru identificatorii din
lista de argumente din definiia funciei i parametri efectivi constantele,
variabilele, expresiile din lista unui apel al funciei.
Parametrii formali reprezint variabile locale care au domeniu funcia i
timpul de via corespunde duratei de execuie a funciei, deci valorile lor se
memoreaz n stiv sau n registrele procesorului.


Declaraii de funcii. Prototipuri

Apelul unei funcii nu se poate face nainte de definiia ei. Sunt situaii n
care nu este posibil acest lucru (n cazul funciilor care se apeleaz unele pe
altele, sau al funciilor definite n alte fiiere surs).
Pentru a oferi compilatorului posibilitatea de a verifica corectitudinea
apelului unei funcii (numrul i tipurile parametrilor, tipul rezultatului,
eventualele conversii care apar), se pot folosi declaraii fr definire, numite
prototipuri. Sintaxa general a unui prototip este :

<tip> nume_functie (<lista_parametri>);

Lipsa lista_parametri este interpretat n C++ ca funcie fr parametri,
deci cuvntul void nu este necesar, spre deosebire de C standard care nu
consider funcia fr parametri, ci cu orice list de parametri i, ca urmare, nu
verific parametrii efectivi la apelare.
n declaraia funciei este suficient ca n lista_parametri s se specifice
Programare C++ - Lucrarea nr. 3

- 54 -
tipurile parametrilor, fr identificatorii lor. Dac ns se specific
identificatorii, acetia trebuie s fie identici cu cei folosii n antetul definiiei
funciei.
Prototipul funciei trebuie s fie plasat nainte de orice apel al funciei.


Transferul parametrilor
Transferul prin valoare. Conversii de tip

La apelul unei funcii, valoarea parametrilor efectivi este ncrcat n zona
de memorie corespunztoare parametrilor formali. Acest procedeu se numete
transfer prin valoare. Dac parametrul efectiv este o variabil, ea nu este
afectat de nici o operaie asupra parametrului formal, ceea ce poate constitui o
protecie util.
Transferul de valoare este nsoit de eventuale conversii de tip realizate de
compilator, implicite sau explicite dac se folosete operatorul cast.

Transferul prin referin

Pentru ca o funcie s poat modifica valoarea unei variabile folosit ca
parametru efectiv, trebuie folosit un parametru formal de tip pointer, iar la apelul
funciei s se foloseasc ca parametru explicit adresa variabilei.

#include <stdio.h>

void schimba(int *, int *); // lista de parametri este format din pointeri la int

void main()
{
int i, j;
scanf(%d%d, &i, &j);
schimba(&i, &j); // la apelul funciei, parametrii efectivi sunt adrese de variabile
printf(\ni=%d, j=%d, i, j);
}

void schimba(int *a, int *b)
{ int temp ;
temp=*a;
*a=*b;
*b=temp;
}
Programare C++ - Lucrarea nr. 3

- 55 -
Parametri tablou constituie o excepie de la transferul parametrilor prin
valoare, deoarece funcia primete ca parametru adresa tabloului. Acest lucru
este motivat de faptul c, n general, tablourile conin o cantitate mare de date al
cror transfer prin valoare ar avea ca efect o scdere a vitezei de execuie i
cretere a memoriei necesare prin copierea valorilor ntr-o variabil local. De
altfel, numele tabloului este echivalent cu adresa sa. n prototip i antetul
funciei, parametrul tablou se specific n lista de parametri sub forma :

tip nume_tablou[]
sau
tip *nume_tablou

Transferul prin variabile referin

Folosirea parametrilor formali variabile referin este similar folosirii
parametrilor formali pointeri, asigurnd posibilitatea de a modifica valorile
parametrilor efectivi.
Utilizarea referinelor prezint avantajul c procesul de transfer prin
referin este efectuat de compilator n mod transparent, scrierea funciei i
apelul ei fiind simplificate.
Dac tipul parametrului efectiv nu coincide cu cel al parametrului formal
referin, compilatorul efectueaz o conversie, ca n cazul transferului prin
valoare. Pentru realizarea conversiei se creeaz un obiect temporar de
dimensiunea tipului referin, n care se nscrie valoarea convertit a
parametrului efectiv, parametrul formal referin fiind asociat obiectului
temporar. Se pierd astfel avantajele folosirii referinelor ca parametri formali.

#include <stdio.h>

void schimba(int &, int &); // lista de parametri este format din referine de
variabile int

void main()
{
int i, j;
float p, q;
scanf(%d%d, &i, &j);
schimba(i, j); // apelul funciei include ca parametri efectivi variabilele
// i, j de tip int, deci nu este necesar conversia lor; pe
// parcursul execuiei funciei schimba() ele sunt
// redenumite, a, respectiv b, inversarea valorilor avnd
// efect asupra variabilelor din apel, i i j
Programare C++ - Lucrarea nr. 3

- 56 -
printf(\ni=%d, j=%d, i, j);
scanf(%f%f, &p, &q);
schimba(p, q); // apelul funciei include ca parametri efectivi variabile de tip
// float, deci va avea loc o conversie degradant, implicit,
// float->int, care are ca urmare crearea de variabile temporare;
// referinele care se creeaz sunt pentru aceste variabile
// temporare, i nu pentru p i q; inversarea valorilor are efect
// asupra variabilelor temporare, astfel c, la ieirea din funcie,
// se va constata faptul c valorile variabilelor p i q au rmas
// nemodificate
printf(\ni=%f, j=%f, p, q);
}

void schimba(int &a, int &b)
{
int temp ;
temp=a;
a=b;
b=temp;
}

Este util folosirea parametrilor formali referin i n situaia n care
parametrul are dimensiune mare i crearea n stiv a unei copii a valorii are ca
efect reducerea vitezei de execuie i creterea semnificativ a stivei. n aceast
situaie, dac nu se dorete modificarea parametrului efectiv, acesta poate fi
protejat prin folosirea modificatorului const la declararea parametrului formal
referin.


Rezultatul unei funcii. Instruciunea return

Instruciunea return determin ncheierea execuiei unei funcii i
revenirea n funcia apelant.
Valoarea expresiei reprezint rezultatul ntors de funcie, deci trebuie s
fie compatibil cu tipul indicat n prototip i definiie.
Dac tipul funciei este void, instruciunea return este necesar doar dac
se dorete revenirea din funcie nainte de execuia ntregii secvene de
instruciuni care alctuiete funcia.
Transferul rezultatului se poate face utiliznd toate cele trei metode: prin
valoare, pointer sau referin.
n cazul transferului prin pointer sau referin, trebuie s se evite ca
obiectul a crui adres se ntoarce s fie un obiect automatic, deoarece acesta
dispare odat cu terminarea execuiei funciei. Adresa de memorie returnat va
Programare C++ - Lucrarea nr. 3

- 57 -
corespunde unei zone de memorie care nu mai este alocat i pot apare efecte
neateptate.

Pointeri de funcii
Limbajul C++, ca i limbajul C, permite operarea cu variabile pointer care
conin adresa de nceput a codului executabil al unei funcii. Aceste variabile
permit:
transferul ca parametru al adresei funciei asociate;
apelul funciei prin intermediul pointerului.

Declaraia cu sintaxa:

tip_r(*p_fct)(<lista_tip_param>);

declar p_fct ca pointer cu tipul funcie cu rezultatul tip_r i parametri
lista_tip_param.


Observaie: n C standard, lista tipurilor parametrilor poate lipsi i nu se fac
verificri ale parametrilor, n timp ce n C++ lipsa listei
parametrilor indic o funcie fr parametri.


# include <iostream.h>

int fct(int, float); // prototip de funcie

void main()
{
int (*p_f)(int, float) // p_f, pointer de funcie
int i=5, j;
float r=3.5;
p_f=fct; // se atribuie adresa funciei fct pointerului p_f
j=p_f(i, r); // se apeleaz funcia fct prin pointerul p_f
cout<<j<<endl;
}

int fct(int a, float b)
{
cout<<a<<endl;
cout<<b<<endl;
return ( a + (int)b );
}
Programare C++ - Lucrarea nr. 3

- 58 -
Programul urmtor compar dou iruri de caractere (prin funcia test())
folosind dou criterii diferite de comparare exprimate prin funciile comp1() i
respectiv comp2().

#include<stdio.h>
#include<conio.h>

void test(char*, char *, int(*)(char*, char*)); // funcia test() are un parametru de tip
// pointer de funcie
int comp1(char *, char *);
int comp2(char *, char *);

void main()
{
char s1[80], s2[80];
int (*p)(char*, char*); // se declar pointerul p de funcii cu prototipul :
// int functie (char *, char *)
p=comp1; // variabilei pointer p i se atribuie ca valoare
// adresa funciei comp1()
puts("Introduceti primul sir");
gets(s1);
puts("Introduceti al doilea sir");
gets(s2);
test(s1, s2, p); // compar irurile folosind funcia comp1()
getch();
p=comp2; // variabilei pointer p i se atribuie ca valoare
// adresa funciei comp2()
test(s1, s2, p); // compar irurile folosind funcia comp2()
getch();
}

void test(char*a, char*b, int(*comp)(char*, char*))
{
if(!(comp)(a, b))
printf("\ns1 egal cu s2");
else
printf("\ns1 diferit de s2");
}

int comp(char *s1, char *s2) // funcia compar primele caractere ale irurilor
{ return(s1[0]-s2[0]); }

int comp1(char *s1, char *s2) // funcia compar lungimile irurilor
{
int i=0, j=0;
while( s1[i] ) i++;
while( s2[j] ) j++;
Programare C++ - Lucrarea nr. 3

- 59 -
return ( i - j );
}


Parametri cu valori implicite

C++ ofer posibilitatea declarrii funciilor cu valori implicite ale
parametrilor. La apelarea unor astfel de funcii, se poate omite specificarea
parametrilor efectivi pentru acei parametri formali care au declarate valori
implicite, transferndu-se automat valorile respective:

#include <iostream.h>

void f(int, int = 20); // prototip de funcie cu un parametru normal i un
// parametru implicit
void main()
{
cout<<Apel normal : f( 5, 10)<<endl;
f( 5, 10);
cout<<Apel cu un singur parametru: f( 5 )<<endl;
f( 5 );
}

void f(int a, int b)
{
cout<<a=<<a<<; b=<<b<<endl;
}

Programul afieaz:

Apel normal: f( 5, 10)
a=5; b=10
Apel cu un singur parametru: f( 5 )
a=5; b=20

Pentru parametrii crora nu li s-a asociat o valoare implicit, este
obligatoriu s se specifice parametri efectivi la apelul funciei.
La declararea i definirea funciilor cu parametri implicii trebuie s se
respecte urmtoarele reguli:
Valorile implicite se specific o singur dat, fie n prototip, fie n antetul
definiiei. De obicei acestea se declar n prototipul funciei, funciile putnd
fi definite extern programului.
n lista de parametri, cei cu valori implicite trebuie s fie plasai la sfritul
Programare C++ - Lucrarea nr. 3

- 60 -
listei. Aceast regul permite identificarea univoc a valorilor implicite. La
apelul funciei, lista de argumente efective va cuprinde obligatoriu valori
pentru parametri neimplicii.


# include <iostream.h>

void f1(int = 7, float, char = X); // eroare , se cere valoare implicit pentru
// parametrul float
void f2(int , double = 7.5); // prototip corect
void f3(int, long = 111, double = 3.5); // prototip corect

void main()
{
float v = 1.5;
f3( ); // apel incorect, lipsete parametru efectiv pentru parametrul int
// care nu are valoare implicit
f3( 1 ); // apel corect
f3( 2, v); // apel corect, se realizeaz conversia implicit, cu trunchiere,
// float ->long
f3( 3, 99); // apel corect
f3( 4, 99, 7.8); // apel corect
}

void f2(int i, double q = 7.5) // eroare, se repet declararea valorii parametrului
// implicit double
{
cout<<\ni=<<i;
cout<<\tq=<<q;
}

void f3(int i, long j, double r) // definiie corect
{
cout<<\ni=<<i;
cout<<\tj=<<j;
cout<<\tr=<<r;
}

Programul afieaz:

i=1 j=111 r=3.5
i=2 j=1 r=3.5
i=3 j=99 r=3.5
i=4 j=99 r=7.8


Programare C++ - Lucrarea nr. 3

- 61 -
Supradefinirea (suprancrcarea) funciilor

Limbajul C++ ofer posibilitatea de a atribui unui simbol mai multe
semnificaii care pot fi distinse n context. Astfel, se poate folosi supradefinirea
funciilor atunci cnd anumite activiti trebuie efectuate pentru liste de
parametri diferite, att ca tipuri ct i ca numr.
Selectarea funciei se face n urma comparrii tipurilor parametrilor
efectivi cu tipurile parametrilor formali din diferitele declaraii ale funciilor cu
acelai nume. Selecia se realizeaz n mai multe etape de cutare a
corespondenei optime, uneori implicnd conversii de tip.
n cazul supradefinirii unei funcii cu un singur parametru, paii urmai
pentru selectarea funciei sunt:
1. iniial se caut coincidena tipurilor parametrului efectiv cu cel formal;
2. n caz de eec al primei etape, se iniiaz o cutare nsoit de conversii
implicite nedegradante:
- char, unsigned char, short -> int
- unsigned short -> int sau unsigned int
- float ->double
3. n caz de eec al etapei a doua, se reia cutarea cu urmtoarele
conversii standard:
- tip numeric -> tip numeric (inclusiv conversii degradante cum ar fi
float->int)
- 0 -> tip numeric sau tip pointer oarecare
- pointer oarecare -> void *
- pointer spre clasa derivat -> pointer spre clasa de baz
4. se admite utilizarea unei singure conversii de tip definit de utilizator.

Cutarea se oprete n momentul n care se identific, n mod univoc, una
dintre funcii. Dac n una din etape se identific mai mult de o soluie posibil,
ambiguitatea este semnalat de compilator printr-un mesaj de eroare.

#include <iostream.h>

void g(int); // funcia 1
void g(double); // funcia 2

void main()
{
int i = 5;
float r = 2.5;
char c = a;
long l = 10000;
Programare C++ - Lucrarea nr. 3

- 62 -
g( i ); // apel funcia 1
g( r ); // apel funcia 2 cu conversie nedegradant float ->double
g( c ); // apel funcia 1 cu conversie nedegradant char ->int
g( l ); // eroare la compilare datorat ambiguitii aprute n etapa a 3-a
}

void g(int a)
{
cout<<\nApel functia 1 cu argumentul: <<a;
}

void g( double a )
{
cout<<\nApel functia 2 cu argumentul: <<a;
}

Dac funcia supradefinit are mai muli parametri, se aplic pentru
fiecare, succesiv setul de reguli descris, alegndu-se n final, dac este unic,
corespondena care este superioar fa de celelalte, pentru fiecare argument n
parte.

#include <iostream.h>

void g( int, float ); // funcia 1
void g( float, int ); // funcia 2

void main()
{
int i = 5, j = 20;
double r = 2.5;
char c = a;
g( i, r ); // apel funcia 1- pentru i corespondena este exact (etapa 1),
// iar pentru r este necesar conversie nedegradant (etapa 3)
g( r, c ); // apel funcia 2 cu conversie degradant double -> float pentru r
// (etapa 3) i o conversie nedegradant char ->int pentru i
// (etapa 2)
g( i, j ); // eroare la compilare datorat ambiguitii
}

void g(int a, float b)
{
cout<<\nApel functia 1 cu argumentele:
cout<<a=<<a<<\tb=<<b;
}

void g(float a, int b)
{
Programare C++ - Lucrarea nr. 3

- 63 -
cout<<\nApel functia 2 cu argumentele:
cout<<a=<<a<<\tb=<<b;
}


Observaie: n procedura de selecie a variantelor funciei nu intervine tipul
returnat, ci doar setul de parametri, astfel c redefinirea funciei cu
tipuri returnate diferite, dar cu acelai set de parametri va avea ca
efect eroare de compilare, ca urmare a ambiguitii create.


Funciile cu parametri cu valori implicite sunt tratate ca funcii
supradefinite cu numr cresctor de parametri.

#include <iostream.h>
#include<string.h>

int aduna(int , int ); // 1
double aduna(double , double ); // 2
char *aduna(char *, char *); // 3

void main()
{
int i = aduna(42, 17); // se apeleaz prima funcie
double d = aduna(42.5, 17.9); // se apeleaz funcia 2
char * s1 = "C++ ";
char * s2 = "is the best!";
char * s = aduna(s1,s2); // se apeleaz funcia 3
cout<<"\ni = "<<i;
cout<<"\nd = "<<d;
cout<<"\ns = "<<s;
}

int aduna(int a, int b)
{ return a+b; }

double aduna(double a, double b)
{ return a+b;}

char *aduna(char *a, char *b)
{return strcat( a, b );}


Programare C++ - Lucrarea nr. 3

- 64 -
Funcii inline

Directiva de preprocesare #define ofer posibilitatea utilizrii
macrodefiniiilor cu parametri. Pentru fiecare apel, prepocesorul insereaz n
textul programului, n locul apelului, textul macrodefiniiei n care se substituie
numele parametrilor formali cu cele ale parametrilor efectivi. Compilatorul preia
apoi textul, ignornd existena macrodefiniiilor.

#define fct( a, b ) a+b

void main()
{
int i = 2, j = 5, k ;
k = fct( i, j );
float r = 2.3, s = 5.2, t;
t = fct( r, s );
cout<<\nk=<<k;
cout<<\nt=<<t;
}

Avantajul folosirii macrodefiniiei fa de funcie este obinerea unui timp
de execuie mai scurt, deoarece codul este inserat efectiv n locul apelului,
eliminndu-se operaiile specifice apelului de funcii ( transfer de parametri prin
stiv, salvri, etc.).
Folosirea macrodefiniiilor implic ns o serie de dezavantaje:
dimensiunea codului generat crete deoarece se insereaz codul
corespunztor macrodefiniiei pentru fiecare apel;
macrodefiniiile nu pot fi compilate separat;
nu se fac verificri referitoare la tipul i numrul parametrilor;
pot apare probleme legate de transferul parametrilor, domeniul variabilelor,
etc.

# define fct( x ) x*x

void main()
{
int i = 5, j;
j=fct( i ); // corect, j = 5*5 = 25
j=fct( i+1 ); // incorect, j= 5 + 1 * 5 + 1 = 11
}

Eroarea poate fi evitat definind fct(x) ca n exemplul urmtor:

Programare C++ - Lucrarea nr. 3

- 65 -
# define fct( x ) ( x ) * ( x )

void main()
{
int i = 5, j;
j=fct( i ); // corect j = ( 5 ) * ( 5 ) = 25
j=fct( i+1 ); // corect j = ( 5+1 ) * ( 5+1 ) = 36
}

i n aceast situaie pot exista apeluri care s genereze erori:

j = fct( i++ ) ; // incorect, j = ( i++ ) * ( i++ ), i fiind dublu incrementat

C++ ofer posibilitatea declarrii funciilor inline care mbin
avantajele utilizrii macrodefiniiilor cu cele ale utilizrii funciilor.
Funciile inline pstreaz toate proprietile funciilor privind verificarea
validitii apelurilor, modul de transfer al parametrilor, domeniul declaraiilor
locale.
Spre deosebire de funciile ordinare, la fiecare apel compilatorul insereaz
codul obiect al funciei inline n codul programului, ceea ce are ca efect
creterea vitezei de execuie, n schimb crete dimensiunea codului, deci
funciile inline trebuie s fie foarte scurte. Nu se poate compila separat o funcie
inline i nu se pot folosi pointeri ctre asemenea funcii.
Din punct de vedere sintactic, funciile inline se definesc i se utilizeaz
similar celor ordinare, cu excepia adugrii specificatorului inline:

#include <iostream.h>

inline int max(int a, int b)
{
return (a>b) ? a : b;
}

void main()
{
int i, j;
cin>>i;
cin>>j;
cout<<max( i, j );
}

Funciile inline nu pot conine instruciuni repetitive.

inline int* f( int, int* x) // incorect, funcia conine instruciunea repetitiv for
Programare C++ - Lucrarea nr. 3

- 66 -
{
int i;
for(i=0; i<10; i++)
*x = 7+i;
return x+1;
}

inline int f(int i) // incorect, funcia conine instruciunea repetitiv while
{
if( i>0 )
while( i )
{
i--;
printf("%d", i);
}
return 1;
}

inline int f( int x ) // definiie corect
{ return x++ ; }


inline void f( int x ) // incorect, funcia conine instruciunea repetitiv do...while
{ do puts( "x" ); while( --x ); }


Inline este o cerere adresat compilatorului, care poate s o ignore, caz n
care genereaz o funcie ordinar (de exemplu dac n program se folosesc
pointeri ctre funcia respectiv).


Exerciii:

1. Se consider urmtoarele funcii redefinite i apeluri de funcii. Care
dintre acestea sunt corecte?

void f(int a, int b=10);
void f(int a);

f(20);
f(20, 30);


2. De ce nu este corect funcia de mai jos:

Programare C++ - Lucrarea nr. 3

- 67 -
inline int* f(int, int* x)
{ int i;
for(i=0; i<10; i++)
*x=7+i;
return x+1; }

3. Ce afieaz secvenele:

a. #include<iostream.h>

void f(int, int x=5);
void f(double, int y=5);

void main()
{ f(3,6); f(3.14,6); f(3); }
void f(int a, int x)
{ cout<<"\nMesaj si x="<<x; }
void f(double b, int y)
{ cout<<"\tEroare si y="<<y; }
b. #include<iostream.h>

void f(int);
void f(double);

void main()
{ f(3); f(3.14); }
void f(int x)
{ cout<<"Mesaj\t"<<x; }
void f(double y)
{ cout<<"\nEroare\t"<<y; }

4. Care din funciile de mai jos va fi apelat n funcia main()

int f(char){return 0;} // Funcia a
int f(char*){return 0;} // Funcia b
int f(int){return 0;} // Funcia c

void main(void) { int x=f("x"); }

5. Care din funciile de mai jos pot fi declarate 'inline':

a. int f(int i)
{ if(i>0)
while(i) { i--; printf("%d",i); }
return 1; }

b. int f(int x) { return x++;}

c. void f(int x)
{ do puts("x"); while(--x;); }

6. Redefinii funcia de criptare a unui ir prezentat n lucrarea nr. 1,
folosind parametri variabile referin.

r.

- 68 -
Lucrarea nr. 4


Clase i obiecte (partea I)


Limbajul C++ pune la dispoziia programatorului posibilitatea
programrii obiectuale (OOP-Object Oriented Programming). Domeniul
programrii orientate spre obiecte este deosebit de larg, unificnd dou
componente de baz: datele aplicaiei i codul necesar tratrii acestora. Se ofer
facilitatea definirii unor tipuri de date proprii i a operatorilor destinai
manipulrii lor, acestea comportndu-se asemntor datelor standard i a
operatorilor standard.
Avantajele OOP se pot descrie prin urmtoarele concepte:
- INCAPSULAREA prin care se obine contopirea datelor cu codul n cadrul
aa numitelor clase. Se asigur o bun modularizare i localizare a
eventualelor erori, precum i o bun protecie a datelor prin accesul controlat
ctre acestea.
- MOTENIREA care permite ca, pornind de la o clas definit, cu anumite
proprieti, care constituie clasa de baz, s se creeze seturi de clase
asemntoare care completeaz proprietile clasei de baz cu noi proprieti.
- POLIMORFISMUL ntr-o ierarhie de clase obinute prin motenire, o
metod poate avea forme diferite de la o clas la alta, utilizndu-se
supradefinirea acestora.

Clasa este un tip de date definit de utilizator care asociaz unei structuri
de date un set de funcii:

Clasa = Date + Operaii (metode)

Conceptele de domeniu i durat de via, variabile locale i globale,
variabile statice, automatice i dinamice se aplic i obiectelor.
C++ se distinge de limbajele POO pure prin faptul c permite controlul
accesului att la datele membre, ct i la funciile membre unei clase. n acest
scop se folosesc specificatorii de control al accesului : private, protected i
public.
Programare C++ - Lucrarea nr. 4

- 69 -
Efectul acestor specificatori asupra accesului unui membru este:
public : membrul poate fi accesat de orice funcie din domeniul de
declaraie a clasei;
private: membrul este accesibil numai funciilor membre i prietene
clasei;
protected: membrul este accesibil att funciilor membre i prietene
clasei, ct i funciilor membre i prietene claselor derivate din clasa
respectiv.

O funcie membr a unei clase are acces la toate datele membre ale
oricrui obiect din clasa respectiv, indiferent de specificatorul de acces.
n C++ se pot declara mai multe categorii de clase folosind cuvintele
cheie: struct, union, respectiv class. Variabilele de acest tip se numesc obiecte
struct, union, respectiv class.
Tipurile class sunt mai frecvent utilizate, ele corespunznd mai fidel
conceptului OOP.


Tipul class

Sintaxa general de declarare a unui tip de date class este similar cu a
tipului struct:

class <nume_clasa> <: lista_clase_baza> {<lista_membri>}
<lista_variabile>;

unde:
nume_clasa este un identificator care desemneaz numele tipului clas
declarat, care trebuie s fie unic n domeniul n care este valabil declaraia;
lista_clase_baza este lista claselor din care este derivat clasa declarat (dac
este cazul);
lista_membri reprezint secvena cu declaraii de datele membre i declaraii
sau definiii de funcii membre; datele membre pot fi de orice tip, mai puin
tipul clas declarat, dar se admit pointeri ctre acesta; folosirea
specificatorilor auto, extern, register nu este permis.
lista_variabile este lista variabilelor de tip nume_clasa.

La declararea unei clase este obligatoriu s existe cel puin una dintre
nume_clasa i lista_variabile. De regul se specific nume_clasa, fapt ce permite
declararea ulterioar de obiecte din tipul clas declarat.
La declararea obiectelor de tip clas se specific numele clasei i lista de
Programare C++ - Lucrarea nr. 4

- 70 -
variabile de acest tip.
Membrii unei clase au implicit atributul de acces private.
n cadrul declaraiei clasei pot s apar specificatorii de acces de oricte
ori i n orice ordine, toi membrii declarai dup un specificator avnd accesul
controlat de acesta.
Metodele asociate datelor (funciile membre), de regul, trebuie s fie
accesibile utilizatorului, deci se declar cu acces public, dar pot fi i situaii n
care s fie necesare funcii private, ele putnd fi apelate doar de alte funcii
membre ale clasei, sau de cele prietene clasei.
n declaraia clasei se pot include doar prototipurile funciilor membre,
definirea acestora putnd fi fcut oriunde n proiect, chiar i n alt fiier.
Funciile membre unei clase definite n declaraia acestei sunt implicit din
categoria inline.
Pentru definiiile de funcii aflate n afara declaraiei clasei este necesar s
se specifice numele clasei urmat de operatorul de rezoluie (::) alturat numelui
funciei. Operatorul indic compilatorului c funcia respectiv are acelai
domeniu cu declaraia clasei respective, fapt ce permite referirea direct a
membrilor clasei. n caz contrar, compilatorul consider c se definete o funcie
cu acelai nume, extern clasei respective.
Funciile membre unei clase pot fi supradefinite i pot avea parametri
implicii.
Pentru apelul funciilor membre publice i referirea datelor membre
publice ale unui obiect, se folosesc operatorii de selecie (.) i (->), ca n cazul
structurilor i uniunilor din C.
Un exemplu de clas l poate constitui clasa Punct care are ca date
membre doi membri de tip int, x i y, ce primesc ca valori, valorile
coordonatelor unui punct. De asemenea, clasa include ca funcii membre o
funcie de iniializare ce stabilete valorile coordonatelor iniiale ale punctului,
init(), dou funcii ce permit citirea valorilor coordonatelor, getx(), respectiv
gety(), funcia de modificare a coordonatelor punctului, move() i funcia de
afiare, afisare(), ce afieaz proprietile punctului.

#include<iostream.h>

class Punct{
private: // se specific accesul private la membrii clasei
int x, y; // date membre ale clasei
public: // se specific acces public la membrii clasei
void init(int initx=0, int inity=0) // funcie de iniializare, funcie membr cu
// parametri implicii
{x = initx; y = inity; }
int getx(){ return x; } // funcie inline, returneaz valoarea membrului x
Programare C++ - Lucrarea nr. 4

- 71 -
int gety(){ return y; } // funcie inline, returneaz valoarea membrului y
void move(int dx, int dy); // funcie membr cu parametri, definit n afara
// declaraiei clasei
void afisare() // funcie de afiare a valorilor membrilor, funcie inline
{ cout<<\nx=<<x<<\tz=<<y;}
};

void Punct::move(int dx, int dy) // definirea funciei move(), membr a clasei Punct
{
x+=dx;
y+=dy;
}

void main()
{
Punct Punct1; // se declar un obiect de tip Punct, Punct1
int x1, y1;

cout<<"\n Introduceti coordonata x= ";
cin>>x1;
cout<<" Introduceti coordonata y= ";
cin>>y1;
Punct1.init(x1, y1); // iniializarea obiectului Punct1 cu valorile x1,
// respectiv y1
cout<<"\n x este = "<<Punct1.getx(); // afiarea coordonatei x
cout<<"\n y este = "<< Punct1.gety(); // afiarea coordonatei y
Punct1.move(10, 20); // modificarea coordonatelor obiectului Punct1
cout<<"\n x este = "<<Punct1.getx(); // afiarea coordonatei x
cout<<"\n y este = "<< Punct1.gety(); // afiarea coordonatei y
Punct Punct2; // se declar un obiect de tip Punct, Punct2
Punct2.init(); // membrii x i y preiau valorile implicite ale
// parametrilor, deci x=y=0
Punct2.afisare(); // afiarea caracteristicilor obiectului Punct2
Punct *p_Punct3; // se declar un obiect pointer la Punct
p_Punct3 = &Punct2;
p_Punct3 -> move ( 5, 12); // apelul funciilor membre se face folosind
// operatorul de selecie ->
p_Punct3 -> afisare();
}

Obiectele declarate de tip Punct (Punct1, Punct2) sunt structuri de date
alctuite din doi membri int x i respectiv y, care pot fi controlai cu funciile
asociate care asigur atribuirea de valori, afiarea, modificarea acestora (init(),
afisare(), getx(), gety(), move()).
Accesul la membrii privai x i y nu se poate face din afara clasei, ci doar
prin funciile membre.
Programare C++ - Lucrarea nr. 4

- 72 -
Punct1.x = 15; // eroare, membrul Punct1.x este privat
Punct2.y = Punct1.x; // eroare Punct1.x, Punct2.y sunt membri privai

Pentru fiecare obiect al clasei se aloc spaiul de memorie necesar datelor
membre. Pentru funciile membre, n memorie exist codul ntr-un singur
exemplar, cu excepia funciilor inline pentru care se insereaz codul pentru
fiecare apel n parte.
Operatorul de atribuire ( = ) poate fi folosit pentru obiecte din acelai tip
class, determinnd copierea datelor membru cu membru, ca n cazul structurilor
din C.

Punct2=Punct1; //membrul x al obiectului Punct2 primete valoarea
// membrului x al obiectului Punct1 membrul y al
// obiectului Punct2 primete valoarea membrului y al
// obiectului Punct1
Punct2.afisare();

Se pot folosi operatorii new i delete pentru crearea/distrugerea de obiecte
de tip class.

Punct * p_Punct4=new Punct;
p_Punct4 -> init(5,7);
p_Punct4-> move(2, 2);
p_Punct4-> afisare();
delete p_Punct4;


Tipurile struct i union

Tipurile struct i union reprezint n C++ cazuri particulare de clase.
Sintaxa de declarare a tipurilor struct i union este similar celei utilizate
pentru class.
Tipul struct este similar tipului class, putnd avea att date membre, ct i
funcii membre, dar nu asigur ncapsularea implicit a datelor, implicit membrii
fiind cu acces public. Pot fi utilizai specificatorii de control al accesului.

//clas declarat cu struct
#include<iostream.h>

struct Punct{
private: // este necesar specificatorul de acces, deoarece accesul
// implicit este public
int x, y;
Programare C++ - Lucrarea nr. 4

- 73 -
public:
init(int initx=0, int inity=0)
{x=initx; y=inity;}
int getx()
{return x;}
int gety()
{return y;}
void move(int dx, int dy);
void afisare();
{cout<<\nx=<<x<<\tz=<<y;}
};


n C++, tipurile union, pe lng cmpurile de date, pot avea funcii
membre, dar membrii sunt ntotdeauna cu acces public, nefiind permis
utilizarea specificatorilor de acces. Tipurile union nu pot fi utilizate ca i clase
de baz pentru alte clase, nici nu pot fi derivate din alte clase.


Autoreferina. Cuvntul cheie this

n definiiile funciilor membre sunt necesare referiri la datele membre ale
clasei respective. Acest lucru se poate face fr a specifica un obiect anume. La
apelarea unei funcii membre, identitatea obiectului asupra cruia se acioneaz
este cunoscut datorit transferului unui parametru implicit care reprezint
adresa obiectului.
Dac n definiia unei funcii este necesar utilizarea adresei obiectului,
acest lucru se realizeaz cu cuvntul cheie this, asociat unui pointer ctre
obiectul pentru care s-a apelat funcia.
Clasa Punct definit anterior se poate completa cu funcia membr cu
prototipul :

void adresa();
definit astfel:

void Punct::adresa()
{
cout<<\n Adresa obiectului Punct este:
cout<< this;
}

Funcia main() se poate completa cu urmtoarele linii de program:
Programare C++ - Lucrarea nr. 4

- 74 -
Punct1.adresa();
Punct2.adresa();
p_Punct3->adresa();


Constructori i destructori

Unele obiecte necesit alocarea unor variabile dinamice la creare,
eventual atribuirea de valori adecvate datelor nainte de utilizare. Pe de alt
parte, eliminarea unui obiect de acest tip impune eliberarea zonei de memorie
alocat dinamic.
Pentru crearea, iniializarea, copierea i distrugerea obiectelor, n C++ se
folosesc funcii membre speciale, numite constructori i destructori.
Constructorul se apeleaz automat la crearea fiecrui obiect al clasei,
indiferent dac este static, automatic sau dinamic (creat cu operatorul new),
inclusiv pentru obiecte temporare.
Destructorul se apeleaz automat la eliminarea unui obiect, la ncheierea
timpului de via sau, n cazul obiectelor dinamice, cu operatorul delete.
Aceste funcii efectueaz operaiile prealabile utilizrii obiectelor create,
respectiv eliminrii lor. Alocarea i eliberarea memoriei necesare datelor
membre rmne n sarcina compilatorului.
Constructorul este apelat dup alocarea memoriei necesare datelor
membre ale obiectului (n faza final crerii obiectului).
Destructorul este apelat naintea eliberrii memoriei asociate datelor
membre ale obiectului (n faza iniial a eliminrii obiectului).
Constructorii i destructorii se deosebesc de celelalte funcii membre prin
cteva caracteristici specifice:
numele funciilor constructor sau destructor este identic cu numele clasei
creia i aparin; numele destructorului este precedat de caracterul tilde
(~);
la declararea i definirea funciilor constructor sau destructor nu se
specific nici un tip de rezultat (nici tipul void);
nu pot fi motenii, dar pot fi apelai de clasele derivate;
nu se pot utiliza pointeri ctre funciile constructor sau destructor;
constructorii pot avea parametri, inclusiv parametri implicii i se pot
supradefini; destructorul nu poate avea parametri i este unic pentru o
clas.

Constructorul fr parametri se numete constructor implicit.
Dac o clas nu are constructor i destructori definii, compilatorul
Programare C++ - Lucrarea nr. 4

- 75 -
genereaz automat un constructor implicit, respectiv un destructor implicit, care
sunt funcii publice.
Constructorii pot fi publici sau privai, dar, de regul, se declar cu acces
public. Dac se declar constructorul private, nu se pot crea obiecte de acel tip.
Acest lucru poate avea sens dac respectiva clas este destinat a folosi exclusiv
ca i clas de baz pentru o ierarhie de clase derivate.


Crearea, iniializarea i eliminarea obiectelor

Se consider iniial clasa Punct definit anterior. Pentru a asigura
iniializarea automat a obiectelor de tip Punct de la declarare, se pot defini
constructori care s preia operaiile efectuate de funcia init(). Clasa Punct poate
fi definit astfel:

class Punct{
private:
int x, y;
public:
Punct(int initx=0, int inity=0) // constructor cu parametri implicii
{
cout<<\nConstructor Punct\n;
x=initx; y=inity;}
~Punct() // destructor
{ cout<<\nDestructor Punct\n; }
int getx(){return x;}
int gety(){return y;}
void move(int dx, int dy);
void afisare()
{cout<<\nx=<<x<<\tz=<<y;}
};
void Punct::move(int dx, int dy)
{
x+=dx;
y+=dy;
}

Pentru vizualizarea apelului funciei constructor s-a inclus afiarea unui
mesaj. Clasa nu are nevoie de destructor, el ns a fost definit formal pentru a
permite vizualizarea momentului n care se elimin obiectele de tip Punct.
Se poate defini urmtorul program de testare:

Punct Punct1(10, 5); // se declar obiectul Punct1, global, pentru care se preiau
// valorile x=10 , y=5
Programare C++ - Lucrarea nr. 4

- 76 -
void main()
{
cout <<\nIncepe functia main();
Punct1.afisare();
cout << \Sfarsit functia main();
}

In urma execuiei programului, se afieaz:

Constructor Punct
Incepe functia main()
x=10 y=5
Sfarsit functia main()
Destructor Punct

Se observ c obiectul global Punct1 se creeaz nainte de a ncepe
execuia funciei main() i se elimin dup terminarea execuiei acesteia. n
momentul declarrii obiectului Punct1 se apeleaz constructorul cu parametrii
efectivi specificai.
O declaraie de forma:

Punct Punct2 ;

are ca efect apelul constructorului cu valorile implicite ale parametrilor, deci
membrii obiectului Punct2 vor prelua valorile x=0, y=0.

class Punct{
private:
int x, y;
public:
Punct() // constructor implicit
{
cout<<\nConstructor implicit\n;
x=0; y=0;
}
Punct(int initx, int inity) // constructor cu parametri
{
cout<<\nConstructor cu parametri\n;
x=initx; y=inity;
}
~Punct() // destructor
{ cout<<\nDestructor Punct\n; }
int getx(){ return x; }
int gety(){ return y; }
void move(int dx, int dy);
Programare C++ - Lucrarea nr. 4

- 77 -
void afisare()
{cout<<\nx=<<x<<\tz=<<y;}
};

Clasa Punct a fost definit cu supradefinirea constructorilor. In momentul
crerii unui obiect Punct fr specificarea parametrilor, se apeleaz constructorul
implicit, altfel se apeleaz constructorul cu parametri. Destructorul este unic.

void main()
{
Punct Punct1, Punct2(10, 20);
Punct1.afisare();
Punct2.afisare();
}

Programul afieaz:

Constructor implicit
Constructor cu parametri
x=0 y=0
x=10 y=20
Destructor Punct
Destructor Punct

Utilizarea claselor care au definite funcii constructor i destructor
garanteaz c obiectele create, indiferent c sunt statice, automatice sau
dinamice, sunt aduse ntr-o stare iniial adecvat utilizrii, cu evitarea utilizrii
unor valori reziduale, iar la eliminarea lor se efectueaz toate operaiile
prealabile necesare.


Constructor de copiere

La crearea unui obiect, acesta poate prelua valorile corespunztoare ale
unui obiect deja existent, prin apelul unui constructor special, numit constructor
de copiere.
Declaraia constructorului de copiere pentru o clas este un constructor cu
un parametru unic de tip referin la obiecte de tipul clasei ce se definete:

nume_clasa( nume_clasa &);

n absena definirii explicite a constructorului de copiere n cadrul clasei,
Programare C++ - Lucrarea nr. 4

- 78 -
compilatorul genereaz automat un constructor de copiere care iniializeaz
datele membre ale obiectului nou creat cu valorile corespunztoare ale
obiectului specificat.

Punct P1(10, 15);
Punct P2(P1); // se genereaz un constructor de copiere implicit care va face
// atribuirile P2.x=P1.x, P2.y=P1.y
P2.afisare();

n declaraia clasei Punct se poate aduga constructorul de copiere care va
avea prototipul:

Punct (Punct &);

i se definete:

Punct::Punct(Punct &P)
{
cout<< \n Constructor de copiere;
x=P.x;
y=P.y;
}

Se pot crea obiecte copie a unor obiecte existente folosind declaraii
similare exemplului anterior:

Punct P2(P1);

n cazul clasei Punct definit, nu este necesar definirea constructorului
de copiere, constructorul de copiere generat de compilator putnd s asigure
operaiile necesare crerii noului obiect. Exist ns situaii n care definirea
acestuia este absolut necesar. Aceast situaie apare, de regul, atunci cnd
clasa definit conine date membre pentru care s-a fcut alocare dinamic de
memorie. Prin copierea datelor membru cu membru se poate ajunge n situaia
de a referii aceeai zon de memorie prin membrii a dou obiecte diferite. La
eliminarea unuia dintre ele se face i dealocarea zonei respective de memorie, ea
putnd fi referit n continuare de obiectul nc existent. La eliminarea i a
acestui obiect se va ncerca eliberarea zonei de memorie deja dealocat.

Pentru ilustrarea celor spuse anterior se definete clasa tablou care are
ca date membre un tablou de elemente float, pentru toate obiectele tablou
numrul elementelor fiind acelai, N :
Programare C++ - Lucrarea nr. 4

- 79 -

#include <iostream.h>
#define N 10

class tablou
{
float tab[N];
public:
tablou();
void citire();
void afisare();
void modif(int, float);
};

tablou::tablou() // constructor implicit - iniializeaz elementele tabloului
{ // cu valoarea 0
for (int i=0; i<N; i++)
tab[i]=0;
}

void tablou::citire() // funcie membr prin care se citesc de la tastatur
{ // valorile elementelor din tab
for(int i=0; i<N; i++)
{ cout<<"\ntab["<<i<<"]=";
cin>>tab[i];
}
}

void tablou::afisare() // funcie de afiare a adresei obiectului i a valorilor
// elementelor din tab
{
cout<<"\nadresa="<<this;
for(int i=0; i<N; i++)
cout<<"\ntab["<<i<<"]="<<tab[i];
}

void tablou::modif(int i, float el) // funcie membr prin care se modific valoarea unui
// element al lui tab
{ tab[i]=el; }

void main()
{
tablou t1; // se declar obiectul de tip tablou t1
t1.citire();
tablou t2(t1); // se creeaz obiectul de tip tablou t2 prin copierea lui t1; se
// apeleaz constructorul de copiere generat de compilator
t2.modif(0, 2.5);
Programare C++ - Lucrarea nr. 4

- 80 -
t1.afisare();
t2.afisare();
}

Se reia declararea clasei tablou pentru a obine tablouri de dimensiuni
diferite. Este necesar alocarea dinamic a membrului tab.

#include <iostream.h>

class tablou
{ int nr_el; // dimensiunea tabloului tab
float *tab; // adresa tabloului tab
public:
tablou(); // constructor implicit
tablou(int); // constructor cu parametru
tablou(tablou &); // constructor de copiere
~tablou(); // destructor
void citire();
void afisare();
void modif(unsigned int, float);
};

tablou::tablou() // definire constructor implicit
{
cout<<"\nConstructor implicit";
nr_el=10;
tab=new float[nr_el];
for(int i=0; i<nr_el; i++)
tab[i]=0;
}

tablou::tablou(int n) // definire constructor cu parametru
{
cout<<"\nConstructor cu parametru ";
nr_el=n;
tab=new float[nr_el];
for(int i=0; i<nr_el; i++)
tab[i]=0;
}

tablou::tablou(tablou &t) // definire constructor de copiere
{
cout<<"\nConstructor de copiere";
nr_el=t.nr_el;
tab=new float[nr_el];
for(int i=0; i<nr_el; i++)
tab[i]=t.tab[i]; }
Programare C++ - Lucrarea nr. 4

- 81 -
tablou::~tablou() // definire destructor
{
cout<<"\nDestructor" ;
delete tab;
}

void tablou::citire()
{
for(int i=0; i<nr_el; i++) // definirea funciei de citire a valorilor
// elementelor tabloului
{ cout<<"\ntab["<<i<<"]=";
cin>>tab[i];
}
}

void tablou::afisare() // definirea funciei de afiare a datelor
{
cout<<"\nadresa="<<this;
for(int i=0; i<nr_el; i++)
cout<<"\ntab["<<i<<"]="<<tab[i];
}

void tablou::modif(unsigned int i, float el) // definirea funciei de modificare a valorii
{ // elementului de index i al tabloului
if (i>=nr_el)
cout<<"\nParametru ilegal";
else
tab[i]=el;
}

void main()
{
tablou t1(5); // se apeleaz constructorul cu parametru
t1.citire();
tablou t2(t1); // se apeleaz constructorul de copiere
t2.modif(0, 2.5);
t1.afisare();
t2.afisare();
tablou t3; // se apeleaz constructorul implicit
t3.citire();
t3.afisare();
}
Prin alocare dinamic de memorie pentru membrul tab, tablourile pot fi
create de dimensiuni diferite. Acest fapt atrage necesitatea definirii
destructorului care s elibereze zona respectiv de memorie.
De asemenea, n exemplul prezentat este necesar definirea
constructorului de copiere. n absena acestei definiii, constructorul de copiere
Programare C++ - Lucrarea nr. 4

- 82 -
creat implicit de compilator va atribui aceeai adres membrului tab al
obiectului t2, cu cea a coninutului obiectului t1, deci ele vor referi aceeai zon
de memorie. Modificarea valorii unui element pentru t1 va fi preluat i de t2 i
reciproc. n momentul distrugerii obiectelor t1 i t2 se va ncerca dealocarea
unei aceleiai zone de memorie.


Exerciii :

1. S se defineasc tipul de date:

class ceas
{ private :
long int secunde;
public :
ceas();
ceas(int o, int m, int s=0);
ceas( ceas &);
~ceas();
afisare(); };

care reprezint timpul scurs de la ora 0:0:0 a unei zile, exprimat n secunde.
Funciile membre vor conine afiarea de mesaje i afiarea autoreferinei
obiectelor, astfel nct s se poat urmri, la execuia programului, obiectul
asupra cruia se opereaz i funcia apelat. Funcia main() va conine
declaraii de obiecte de tip ceas cu apel al diferiilor constructori i atribuiri
ntre obiecte de tip ceas i afiarea proprietilor obiectelor ceas prin apelul
funciei afisare().

2. S se defineasc tipul de date:

class sir
{ private:
char * continut;
int dim;
public:
sir(int d = 80);
sir (sir &);
~sir();
void citire();
void afisare(); };

Funcia main() va conine declaraii de obiecte sir pentru care se citete
coninutul. Pentru fiecare obiect se va crea o copie ce va fi afiat.

- 83 -
Lucrarea nr. 5




Clase i obiecte (partea II)


Manevrarea dinamic a obiectelor

n mod similar utilizrii funciilor C malloc() i respectiv free(), se pot
crea i distruge obiecte dinamice utiliznd operatorii new i delete.
Operatorul new aloc memorie corespunztor tipului datei, fr a fi
necesar s se specifice dimensiunea zonei de memorie ca n cazul funciei
malloc(). n plus, n cazul tipurilor clas produce apelul unui constructor, ceea
ce nu se ntmpl n cazul funciei malloc().
Operatorul delete este analog funciei free(), dar, n plus fa de aceasta, n
momentul eliminrii obiectului din memorie va apela, dac este cazul, funcia
destructor.

De exemplu, pentru clasa tablou cu declaraia:

class tablou
{ int nr_el; // dimensiunea tabloului tab
float *tab; // adresa tabloului tab
public:
tablou(); // constructor implicit
tablou(int); // constructor cu parametru
~tablou(); // destructor
void citire();
void afisare();
};

se pot crea obiecte dinamice astfel:

tablou * p_t1 = new tablou; // se creeaz obiectul dinamic p_t1, cu apelarea
// constructorului implicit
p_t1 -> afisare();
p_t1-> citire();
p_t1-> afisare();
Programare C++ - Lucrarea nr. 5

- 84 -
delete p_t1; // de elimin obiectul p_t1, odat cu apelul
// destructorului
tablou * p_t2 = new tablou (3) // se creeaz obiectul dinamic p_t2, cu apelarea
// constructorului cu parametru
p_t2 -> afisare();
p_t 2-> citire();
p_t 2-> afisare();
delete p_t2; // se elimin obiectul p_t2, odat cu apelul destructorului



Transferul obiectelor ca parametri sau rezultat

n expresii, operanzii pot fi obiecte ale claselor definite, la fel rezultatul
returnat de ctre acestea. De asemenea, parametrii funciilor i rezultatul returnat
de ctre acestea pot fi obiecte ale unor clase.
n procesul evalurii expresiilor, ca i la preluarea valorilor pentru
parametri i returnarea rezultatelor, apar situaii de memorare temporar de
valori, crendu-se obiecte temporare. Obiectele temporare au durata de via
limitat la durata de execuie a blocului de instruciuni cruia i aparin, dar, spre
deosebire de variabilele automatice, ele au existena ascuns.
Un caz particular l constituie parametrii transferai prin valoare. Ei pot fi
asimilai cu variabilele automatice, dar spre deosebire de acestea timpul lor de
via este mai lung dect timpul de execuie al domeniului lor. Aceste obiectele
temporare sunt create nainte de nceperea execuiei funciei i sunt eliminate
dup ncheierea execuiei acesteia.
La transferul prin valoare a unui parametru sau al unui rezultat se creeaz
obiecte temporare prin apelul constructorului de copiere al clasei respective, sau
a constructorului de copiere generat de compilator, n lipsa definirii acestuia.
n exemplul urmtor se declar o clas punct n care n funciile
constructor i destructor se includ mesaje corespunztoare i afiarea adresei
obiectului pentru a putea urmri crearea i eliminarea obiectelor.

#include <iostream.h>

class punct
{
int x, y;
public:
punct(int=0, int=0 );
punct(punct&);
~punct();
void deplasare(int, int);
Programare C++ - Lucrarea nr. 5

- 85 -
void afisare();
};

punct::punct(int abs, int ord)
{
x=abs;
y=ord;
cout<<"\nConstructor "<<this;
afisare();
}

punct::punct(punct &p)
{
x=p.x;
y=p.y;
cout<<"\nConstructor copiere "<<this;
afisare();
}

punct::~punct()
{
cout<<"\nDestructor "<<this;
afisare();
}

void punct::deplasare(int dx, int dy)
{
x+=dx;
y+=dy;
}

void punct::afisare();
{
cout<<"\nx="<<x<<"\ty="<<y;
}

punct test(punct);

void main()
{
cout<<"\nApel main()";
punct p0, p1(1,1); // p0 se creeaz prin apelul constructorului cu parametri
// cu valorile implicite, p1 preia valorile x=1, y=1
p0=test(p1); // rezultatul funciei test(), p, este atribuit membru cu
// membru lui p0
p0.afisare();
cout<<"\nFinal main()";
}
Programare C++ - Lucrarea nr. 5

- 86 -
punct test(punct p) // parametrul formal p se creeaz apelnd constructorul
// de copiere
{
cout<<"\nApel test()";
p.deplasare(10,10);
return p; // rezultatul se returneaz printr-un obiect temporar creat
// prin apelul constructorului de copiere
}

Programul afieaz:

Apel main()
Constructor 0xfff2 // se creeaz p0
x=0 y=0
Constructor 0xffee // se creeaz p1
x=1 y=1
Constructor copiere 0xffe6 // se creeaz parametrul ca obiect temporar echivalent
// cu apelul p(p1)
x=1 y=1
Apel test()
Constructor copiere 0xffea // se creeaz obiectul temporar care transfer rezultatul
x=11 y=11
Destructor 0xffe6 // se elimin p
x=11 y=11
Destructor 0xffea // se elimin obiectul care transfer rezultatul
x=11 y=11
x=11 y=11
Final main()
Destructor 0xffee // se elimin p1
x=1 y=1
Destructor 0xfff2 // se elimin p0
x=11 y=11

Pentru obiecte de dimensiuni mari, se recomand transferul parametrilor
i rezultatului prin referin deoarece nu se mai creeaz obiecte temporare pentru
parametri, astfel nct se reduce ncrcarea stivei i crete viteza de execuie.
Funcia test() se poate defini astfel:

punct & test(punct &p) // parametrul formal p de definete ca referin u unui punct
{
cout<<"\nApel test()";
p.deplasare(10, 10);
return p; // rezultatul se returneaz printr-un obiect temporar creat prin
// apelul constructorului de copiere
}

Programare C++ - Lucrarea nr. 5

- 87 -
Programul afieaz:

Apel main()
Constructor 0xfff2 // se creeaz p0
x=0 y=0
Constructor 0xffee // se creeaz p1
x=1 y=1
Apel test()
x=11 y=11
Final main()
Destructor 0xffee // se elimin p1
x=11 y=11
Destructor 0xfff2 // se elimin p0
x=11 y=11

Se observ c nu se mai creeaz obiecte temporare pentru parametrul i
rezultatul funciei, n schimb modificrile care au loc asupra obiectului p din
funcia test() sunt de fapt modificri ale obiectului p1 a crui referin este.
Obiecte temporare nu se creeaz numai prin constructorul de copiere, ci
pot apare situaii n care se creeaz prin apelul unui constructor uzual:

punct p1(1, 1);
p1=punct(10, 10);

n aceast situaie, se creeaz un obiect temporar prin apelul
constructorului cu parametri punct(10, 10), dup care se face copierea membru
cu membru a valorilor membrilor obiectului temporar n obiectul p1.


Clase cu membri obiecte
Clasele pot avea membri de orice tip, mai puin tipul clas care se
definete. Deci membrii pot fi obiecte de diferite tipuri.
Se definete clasa pozitie care reprezint poziia unui punct pe ecran i
clasa text_poz care va conine un ir de caractere asociat unei poziii
specificat printr-un obiect pozitie:

#include <iostream.h>

class pozitie // declaraia clasei pozitie
{
int x, y;
public:
void init(int abs, int ord)
Programare C++ - Lucrarea nr. 5

- 88 -
{ x=abs; y=ord; }
void deplasare(int abs, int ord)
{ x+=abs; y+=ord; }
void afisare()
{ cout<<\nx=<<x<<\ty=<<y;}
};

class text_poz { //declaraia clasei text_poz
char *txt;
pozitie orig; // se declar membrul de tip pozitie ce va conine
// coordonatele de afiare a textului
public:
text_poz(int , int , char*); // constructor cu parametri
void depl_orig(int , int );
void afisare()
{ orig.afisare();
cout<<txt;
}
};

text_poz::text_poz(int abs, int ord, char*c=NULL)
{
txt=c;
cout<<"Constructor text_poz: "<<txt<<'\n';
orig.init(abs, ord);
};

void text_poz::depl_orig(int dx, int dy)
{
orig.deplasare(dx, dy);
};

void main()
{
text_poz txt(10, 10, "text");
txt.depl_orig(5, 5);
txt.afisare();
txt.orig.deplasare(5, 5); // eroare, orig este membru private
}

La crearea unui obiect text_poz, pentru crearea membrului orig, n absena
unui constructor declarat, se apeleaz constructorul generat de compilator clasei
punct. Dac exist un constructor declarat n clasa pozitie, la crearea membrului
orig se va apela constructorul respectiv. Dac acesta este unic i este declarat cu
parametri ordinari, trebuie transmise valorile corespunztoare acestor parametri,
deci un constructor generat implicit de compilator sau un constructor implicit
Programare C++ - Lucrarea nr. 5

- 89 -
declarat pentru clasa care conine membru obiect nu pot rezolva aceast situaie.
Deci este necesar, fie s se defineasc un constructor implicit pentru clasa
membrului, fie s se defineasc un constructor prin care s se defineasc
transferul parametrilor ctre constructorul clasei membrului nglobat.

#include <iostream.h>

class pozitie
{
int x, y;
public:
pozitie(); // constructor implicit
pozitie(int abs, int ord); // constructor cu parametri
void deplasare(int abs, int ord);
void afisare();
};

class text_poz {
char *txt;
pozitie orig;
public:
text_poz(); // constructor implicit la crearea unui obiect text_poz
// se va apela nti constructorul implicit pentru membrul
// pozitie i apoi cel al clasei text_poz
void citire_sir();
void afisare();
...
};

Transferul parametrilor prin constructorul clasei text_poz ctre
constructorul clasei pozitie se realizeaz prin specificarea parametrilor destinai
obiectului pozitie n antetul constructorului :

#include <iostream.h>

class pozitie
{
int x, y;
public:
pozitie(int abs, int ord) //constructor cu parametri
{ x=abs; y=ord;
cout<<"\nConstructor pozitie ";
afisare();
}
~pozitie() // destructor
{ cout<<"\nDestructor pozitie"; }
Programare C++ - Lucrarea nr. 5

- 90 -
void deplasare(int abs, int ord)
{
x+=abs;
y+=ord;
}
void afisare()
{
cout<<"\nx="<<x;
cout<<",y="<<y<<'\n';
}
};

class text_poz //clasa cu membri obiecte
{
char *txt;
pozitie orig; //poziia textului
public:
text_poz(int ,int , char*); // constructor cu parametri
~text_poz() //destructor
{ cout<<"\nDestructor text_poz";}
void depl_orig(int , int );
void afisare()
{ orig.afisare();
cout<<txt;
}
};

// se specific parametrii preluai de constructorul pozitie la crearea membrului orig
text_poz::text_poz(int abs, int ord, char*c=NULL) : orig(abs, ord)
{
txt=c;
cout<<"\nConstructor text_poz: ";
orig.afisare();
cout<<txt<<'\n';
};

void text_poz::depl_orig(int dx, int dy)
{
orig.deplasare(dx, dy);
};

void main()
{
int i;
cout<<"\nIncepe main()";
text_poz txt(10, 10, "Text exemplu");
txt.depl_orig(5, 5);
txt.afisare();
Programare C++ - Lucrarea nr. 5

- 91 -
cout<<"\nFinal main()";
}

La execuia programului se va afia:

Incepe main()
Constructor pozitie: x=10 y=10
Constructor text_poz: x=10 y=10
Text exemplu
x=15, y=15
Text exemplu
Final main()
Destructor text_poz
Destructor pozitie

Se observ c se apeleaz nti constructorul pozitie() care creeaz
membrul orig i apoi constructorul text_poz(). Ordinea eliminrii obiectelor
este invers, nti se distruge obiectul text_poz i apoi cel pozitie.

Tablouri de obiecte

Tablourile pot avea elemente de orice tip, inclusiv de tip clas.
La crearea unui tablou cu elemente de tip clas, se va apela constructorul
clasei tip element pentru fiecare element n parte. Problemele semnalate la
crearea obiectelor cu membri de tip clas vor aprea i n acest caz. Mai mult, n
cazul tablourilor nu exist posibilitatea specificrii valorilor corespunztoare
parametrilor, deci pentru tipul clas corespunztor elementelor tabloului este
obligatoriu s existe declarat constructor implicit sau constructor cu toi
parametrii implicii. Pentru fiecare element de tablou de tip clas se apeleaz
constructorul, la ncetarea domeniului de existen a tabloului, pentru fiecare
element de tablou se apeleaz destructorul clasei.

#include <iostream.h>

class pozitie
{
int x,y;
public:
pozitie(int abs, int ord) //constructor cu parametri
{ x=abs; y=ord;
cout<<"Constructor cu parametri - pozitie ";
afisare();
}
Programare C++ - Lucrarea nr. 5

- 92 -
pozitie() //constructor implicit
{
x=0; y=0;
cout<<"Constructor implicit - pozitie ";
afisare();
}

~pozitie()
{ cout<<\nDestructor pozitie; }

void deplasare(int abs, int ord)
{
x+=abs;
y+=ord;
}

void afisare()
{ cout<<"x="<<x;
cout<<",y="<<y<<'\n';
}
};

main()
{
int i;
cout<<"Incepe main()\n";
pozitie tab[10]; //apeleaz constructorul fr parametri de 10 ori
for (i=0; i<10; i++)
tab[i].deplasare(i, i);
for (i=0; i<10; i++)
tab[i].afisare();
cout<<"Final main()\n"; // la ieirea din main() nceteaz domeniul de existen al
// lui tab i se apeleaz de 10 ori destructorul clasei
// poziie
}

n mod similar se ntmpl n cazul crerii de tablouri prin alocare
dinamic de memorie:

pozitie *p_poz=new pozitie[5]; // tablou alocat dinamic se apeleaz constructorul fr
// parametri de 5 ori
for (i=0; i<5; i++)
{
p_poz[i].deplasare(i+2, i+2);
p_poz[i].afisare();
} ;

Programare C++ - Lucrarea nr. 5

- 93 -
delete [] p_poz; // eliberarea zonei de memorie prin apelul de 5 ori a
// funciei destructor

n exemplul anterior, o instruciune de forma:

delete p_poz;

are ca efect eliberarea spaiului corespunztor elementului p_poz[0], spaiul
corespunztor celorlalte elemente fiind n continuare ocupat. Deci sintaxa
folosit pentru dealocarea complet a unui tablou (nume_pointer) alocat dinamic
cu operatorul new este:
delete [ ] nume_pointer ;



Pointeri ctre membrii unei clase. Operatorii ( .* ) i ( ->* )

Limbajul C++ ofer posibilitatea de a opera cu pointeri ctre date i
funcii membre ale unei clase. Spre deosebire de pointerii ordinari, acetia au
asociat, pe lng tipul datei sau funciei, i tipul clas respectiv. Un pointer ctre
un membru al unei clase nu se asociaz obiectelor clasei respective, ci clasei, el
coninnd ca informaie nu adresa membrului ci deplasarea lui n cadrul clasei.
Sintaxa declaraiei unui pointer la membrul clasei nume_clas este:

tip nume_clasa::*pointer_membru;

Pentru atribuirea valorii pointerului se folosete acelai operator, &:

pointer_membru=&nume_clasa::membru;

Operatorii destinai accesului la un membru prin pointeri sunt:

- operatorul .* dac se specific un obiect al clasei
- operatorul ->* dac se specific un pointer de obiect

Sintaxa folosit pentru referirea membrului este:

obiect.*pointer_membru
sau
pointer_obiect->*pointer membru
Programare C++ - Lucrarea nr. 5

- 94 -

#include <iostream.h>

class pozitie
{
public:
int x, y;
pozitie(int abs, int ord)
{ x=abs; y=ord;
cout<<"Constructor cu parametri-pozitie ";
afisare();
}
~pozitie()
{ cout<<"\nDestructor pozitie";}
void deplasare(int abs, int ord)
{ x+=abs; y+=ord; }
void afisare()
{ cout<<"\nx="<<x<<\ty="<<y; }
};

void main()
{
pozitie p(1, 1), *p_poz; // se declar un obiect pozitie i un pointer la pozitie
int pozitie::*p_x, pozitie::*p_y; // se declar pointeri la membri de tip ntreg ai
// clasei pozitie
void (pozitie::*p_func)(int, int); // se declar pointer la o funcie membr clasei
// pozitie cu prototip void functie(int, int)
p_poz=&p; // se atribuie pentru pointerul p_poz adresa obiectului p
p_x=&pozitie::x; // se atribuie pointerului p_x adresa membrului x al
// obiectului p
p_y=&pozitie::y; // se atribuie pointerului p_y adresa membrului y al
// obiectului p
p_func=&pozitie::deplasare; // se asociaz pointerului la funcie p_func funciei
// membr clasei pozitie, deplasare()
(p.*p_func)(5, 5); // se apeleaz funcia membr deplasare() pentru
// obiectul p prin pointerul la funcie p_func
cout<<"\nx="<<p.*p_x; // se afieaz membrul x al obiectului p prin pointerul
// p_x
cout<<"\ny="<<p.*p_y; // se afieaz membrul y al obiectului p prin pointerul
// p_y
(p_poz->*p_func)(2, 2); // se apeleaz funcia membr deplasare() pentru
// obiectul pointer la pozitie p_poz prin pointerul la
// funcie p_func
cout<<"\nx="<<p_poz->*p_x; // se afieaz membrul x al obiectului p_poz prin
// pointerul p_x
cout<<"\ny="<<p_poz->*p_y; // se afieaz membrul y al obiectului p_poz prin
} // pointerul p_y
Programare C++ - Lucrarea nr. 5

- 95 -
n cazul pointerilor la funcii membre nu este obligatorie utilizarea
operatorului &, deci declaraiile:

p_func=&pozitie::deplasare;
i
p_func=pozitie::deplasare;
sunt echivalente.


Membri statici ai unei clase

Membrii unei clase, date sau funcii, pot fi declarai statici.
Datele membre nestatice ale unei clase sunt distincte pentru fiecare obiect
al clasei n parte, n timp ce datele statice sunt unice pentru toate obiectele clasei
respective, ele fiind create, iniializate, accesate independent de obiectele clasei.
Funciile membre statice efectueaz operaii asupra ntregii clase, ne fiind
asociate unor obiecte individuale.
Referirea membrilor statici se poate face astfel:
- numele clasei mpreun cu operatorul de rezoluie i numele
membrului static, chiar dac nu exist obiecte ale clasei respective
declarate:
nume_clasa :: nume_membru

- numele unui obiect al clasei mpreun cu operatorul de selecie i
numele membrului, asemenea membrilor nestatici:

nume_obiect.nume_membru

Alocarea memoriei i iniializarea membrilor statici se fac separat de
celelalte date, fiind obligatorie existena unei definiii, eventual cu iniializare,
extern clasei. Iniializarea implicit se face cu valoarea 0.

#include <iostream.h>

class pozitie
{
int x, y;
static int st; // declarare membru static, care se va folosi ca un contor pentru
// obiectele din clasa poziie create; definirea se face n
// exteriorul clasei
public:
Programare C++ - Lucrarea nr. 5

- 96 -
pozitie(int abs, int ord)
{ x=abs; y=ord;
st++; // la crearea unui obiect pozitie, contorul st se
// incrementeaz
cout<<"\nS-a creat obiectul pozitie nr: "<<st<<'\t';
afisare();
}
~pozitie()
{ cout<<"\nDestructor pozitie";
cout<<"\nSe distruge obiectul nr. "<<st;
st--; // odat cu distrugerea unui obiect pozitie, contorul st se
// decrementeaz
}
static void nr_obj() // funcie static ce permite accesul la membrul static
// privat st
{cout<<"\nNr. obiecte: "<<st;}

void deplasare(int abs, int ord)
{ x+=abs;
y+=ord; }

void afisare()
{ cout<<"\nx="<<x<<"\ty="<<y; }
};

int pozitie::st; // definirea membrului static st; implicit este iniializat
// cu valoarea 0 definirea cu iniializare este :
// int pozitie::st=0
void main()
{
pozitie::nr_obj(); // apel funcie static prin numele clasei
pozitie::st; // eroare, membrul st este private
pozitie p1(1,1), p2(2,2), *p3;
p1.nr_obj(); // apel funcie static prin numele unui obiect poziie
p3=new pozitie(3,3);
pozitie::nr_obj(); // apel funcie static prin numele clasei
delete p3;
}

Funciile statice nu primesc implicit adresa unui obiect, apelul pentru
toate obiectele clasei fiind identic. Din aceast cauz, n funciile statice nu se
poate folosi cuvntul this, iar membrii nestatici pot fi referii doar prin numele
obiectelor. Din funciile statice se pot referi direct doar membrii statici.

static int return_st()
{
cout<<adresa obiectului<<this; // eroare, nu se poate folosi autoreferina this
Programare C++ - Lucrarea nr. 5

- 97 -
cout<<valoare x:<< x; // eroare, nu se pot referi membri nestatici in mod direct
return st;
}


Funcii i clase prietene unei clase

Accesul la membrii private ai unei clase se poate acorda, n afara
funciilor membre i unor funcii nemembre, dac sunt declarate cu
specificatorul friend.
Funciile declarate prietene unei clase pot fi funcii independente sau
funcii membre ale altei clase. Funciile prietene sunt externe clasei, deci apelul
lor nu se face asociat unui obiect al clasei.

class cls
{

friend void func();
}

void main()
{
cls obj;
obj.func(); // eroare, funcia func() este extern clasei
func(); // apel corect al funciei func()

}

Funciile prietene au acces la toi membrii clasei, ele opernd asupra
obiectelor care se transfer ca parametrii ai funciei. Referirea unui membru se
face prin numele obiectului, operatorul de selecie i numele membrului, dat
sau funcie.
Se pot ntlni urmtoarele situaii:
1. o funcie independent este prieten unei clase ;
2. o funcie membr a unei clase este prieten altei clase;
3. o funcie este prieten mai multor clase ;
4. o clas este prieten altei clase (toate funciile membre ale unei clase sunt
prietene celeilalte clase).

// Situaia 1.

class pozitie
{ int x, y;
Programare C++ - Lucrarea nr. 5

- 98 -
public:
pozitie(int abs, int ord)
{ x=abs; y=ord; }
void deplasare(int dx, int dy)
{ x+=dx; y+=dy; }
friend void compar(pozitie &, pozitie &); // funcia compar() se declar prieten
// clasei pozitie
};

void compar(pozitie &p1, pozitie &p2) // funcia compar() este extern clasei
// poziie, deci nu poate fi definit dect
// n afara clasei pozitie
{
if ((p1.x= =p2.x)&&(p1.y= =p2.y)) // funcia prieten compar() are acces la
// membrii private ai clasei pozitie
cout<<\n Pozitii identice;
else
cout<<\n Pozitii diferite;
}

void main()
{
pozitie p1(1, 1); p2(3, 3);
compar(p1, p2); // apel al funciei compar()
p1.deplasare(2, 2);
compar(p1, p2); // apel al funciei compar()
}

// Situaia 2

#include <iostream.h>
class A; // declaraie incomplet a clasei A
class B // declaraia clasei B
{
int b;
public:
B(int n)
{b=n;}
void func(A &); // declararea funciei membre a lui B, avnd
// parametru de tip A
};

class A
{
int a;
public:
A(int n)
{a=n;}
Programare C++ - Lucrarea nr. 5

- 99 -
friend void B::func(A &); // se declar funcia membr clasei B func()
} ; // prieten clasei A

void B::func( A &ob_a) // definirea funciei func() membr a clasei B
{
cout<<'\n'<<ob_a.a; // funcia fiind prieten clasei A are acces la
// membrul private a al clasei A
cout<<'\n'<<b;
}

void main()
{
A ob1(10);
B ob2(20);
ob2.func(ob1); // apelul funciei membre clasei B
}

Declaraia friend din clasa A trebuie s gseasc clasa B creia i aparine
funcia func() declarat. n declaraie trebuie s se specifice i numele clasei
creia i aparine funcia prieten.

// Situaia 3

#include <iostream.h>
class A; // declaraie incomplet a clasei
class B; // declaraie incomplet a clasei

class A
{
int a;
public:
A(int n)
{a=n;}
friend void func(A &, B &); // funcia func() este declarat prieten clasei A
} ;

class B
{
int b;
public:
B(int n)
{b=n;}
friend void func(A &, B &); // funcia func() este declarat prieten clasei B
};

void func( A &ob_a, B &ob_b) //definirea funciei func(), extern claselor A i B
{
Programare C++ - Lucrarea nr. 5

- 100 -
cout<<'\n'<<ob_a.a;
cout<<'\n'<<ob_b.b;
}

void main()
{
A ob1(10);
B ob2(20);
func(ob1, ob2); // apelul funciei func() cu parametri de tip A i
// respectiv B
}

Funcia func() se definete n afara claselor A i B, ea fiind o funcie
independent.


// Situaia 4

#include <iostream.h>
class A; // declaraie incomplet a clasei A
class B // declaraia clasei B
{
int b;
public:
B(int n)
{b=n;}
void func(A &);
};

class A
{
int a;
public:
A(int n)
{a=n;}
friend class B; // clasa B se declar prieten clasei A
} ;

void B::func( A &ob_a)
{
cout<<'\n'<<ob_a.a;
cout<<'\n'<<b;
}

void main()
{ A ob1(10);
B ob2(20);
ob2.func(ob1); } // apelul funciei func() membr a clasei B
Programare C++ - Lucrarea nr. 5

- 101 -
Declaraia friend din clasa A trebuie s gseasc clasa B declarat.
Declaraia incomplet a clasei A este necesar pentru a se putea specifica
parametrii de tip A n funciile membre ale clasei B.
Utilizarea funciilor prietene ncalc principiul ncapsulrii, dndu-se
acces i altor funcii dect celor membre la membrii private ai unei clase. Pot
apare probleme i atunci cnd compilarea funciei prietene se face independent
de compilarea funciilor membre, prin omiterea accidental a acesteia. De
asemenea, dac funcia este membr altei clase, apar interdependene ntre clase.
n ciuda acestor inconveniente, funciile prietene ofer anumite faciliti,
ele oferind o alt soluie de acces controlat ctre membrii unor clase.


Exerciii:

1. Se definete clasa:

class dreptunghi
{ int x1, y1, x2, y2;
static int cnt; // membru static ce reprezint un contor pentru obiectele
// clasei create
void normalizare(); // valorile coordonatelor sunt ncadrate in intervalul
// [1, 80], cele orizontale i [1, 25] cele verticale
public:
dreptunghi(int=1, int=1, int=80, int=25); // coordonatele sunt normalizate
~dreptunghi();
void afisare(); // afiarea se face n mod text, prin marcarea laturilor
// dreptunghiului cu un caracter oarecare afiat n mod repetat
static int nr_dreptunghiuri(); // afieaz numrul obiectelor dreptunghi existente la un
// moment
void deplasare(int, int); // realizeaz translatarea dreptunghiului; coordonatele
// sunt normalizate
};

S se defineasc o funcie prieten clasei dreptunghi, aria(), care s
returneze aria corespunztoare dreptunghiului.
n funcia main() se declar obiecte de tip dreptunghi. Se afieaz
proprietile obiectelor, ariile corespunztoare, numrul de instanieri.

2. S se defineasc un tip de dat class data_calendaristic, un tip de dat
class persoana pentru preluarea datelor unei persoane (pentru data naterii se
va folosi un membru de tip data_calendaristic). In funcia main() se va
declara un tablou de tip persoana ce va reprezenta o grup de studeni. Se
citesc date de la tastatur i se realizeaz ordonarea alfabetic a studenilor.

- 102 -
Lucrarea nr. 6





Supradefinirea operatorilor

Funcia operator

Limbajul C++ permite programatorului definirea diverselor operaii cu
obiecte ale claselor, folosind simbolurile operatorilor standard.
Operatorii standard sunt deja supradefinii, ei putnd intra n expresii ai
cror operanzi sunt de diferite tipuri fundamentale, operaia adecvat fiind
selectat n mod similar oricror funcii supradefinite.
Un tip clas poate conine definirea unui set de operatori specifici asociai,
prin supradefinirea operatorilor existeni, utiliznd funcii cu numele:

operator simbol_operator

unde:
- operator este cuvnt cheie dedicat supradefinirii operatorilor;
- simbol_operator poate fi simbolul oricrui operator, mai puin operatorii:
( . ), (.*), ( :: ) i ( ? : ).

Pentru definirea funciilor operator se pot folosi dou variante de
realizare:
- definirea funciilor operator cu funcii membre clasei ;
- definirea funciilor operator cu funcii prietene clasei.

Prin supradefinirea operatorilor nu se pot modifica:
- pluralitatea operatorilor (operatorii unari nu pot fi supradefinii ca
operatori binari sau invers);
- precedena i asociativitatea operatorilor.

Operatorii care pot fi supradefinii n C++ sunt prezentai n Tabelul nr. 7.

Programare C++ - Lucrarea nr. 6

- 103 -
Tabelul nr. 7 Operatorii care pot fi supradefinii
Clasa de
prioritate
Tip Operatori Asociativitate
1 Binar () [] -> de la stnga la dreapta
2 Unar ! ~ + - ++ -- &
* (tip) new delete
de la dreapta la stnga
3 Binar ->* de la stnga la dreapta
4 Binar * / % de la stnga la dreapta
5 Binar + - de la stnga la dreapta
6 Binar << >> de la stnga la dreapta
7 Binar < <= > >= de la stnga la dreapta
8 Binar = = != de la stnga la dreapta
9 Binar & de la stnga la dreapta
10 Binar ^ de la stnga la dreapta
11 Binar | de la stnga la dreapta
12 Binar && de la stnga la dreapta
13 Binar || de la stnga la dreapta
14 Binar = *= /= %= += -=
&= ^= |= <<= >>=
de la dreapta la stnga
15 Binar , (operator virgul) de la stnga la dreapta

Funcia operator trebuie s aib cel puin un parametru, implicit sau
explicit de tipul clas cruia i este asociat operatorul. Pentru tipurile standard
operatorii i pstreaz definirea.
Operatorii =, [], (), -> pot fi definii doar cu funcii membre nestatice
ale clasei.
Programatorul are deplin libertate n modul n care definete noua
operaie, dar n general pentru a da o bun lizibilitate programului, se recomand
ca noua operaie s fie asemntoare semnificaiei originale.


Supradefinirea operatorilor folosind funcii membre clasei

Se consider clasa complex definit pentru reprezentarea numerelor
complexe.

class complex
{
double re, im;
Programare C++ - Lucrarea nr. 6

- 104 -
public:
complex(double r=0, double i=0)
{ re=r; im=i; }
void afisare()
{ cout<<\nre=<<re<<\tim=<<im; }
}

Se dorete definirea unui operator binar + care s realizeze nsumarea a
doi operanzi de tip complex, rezultatul returnat fiind un obiect complex cu
partea real sum a prilor reale a celor doi operanzi, iar partea imaginar sum
a prilor imaginare a celor doi operanzi.
O funcie membr primete implicit adresa obiectului pentru care este
apelat, acesta constituind unul dintre parametrii, deci funcia operator + va
trebui s aib un singur parametru explicit. Prototipul funciei va fi:

complex operator+( complex );

Expresia x+y , unde x i y sunt obiecte de tip complex, este echivalent cu un
apel de forma:
x.operator+(y)

class complex
{
double re, im;
public:
complex(double r=0, double i=0)
{ re=r; im=i; }
void afisare()
{ cout<<\nre=<<re<<\tim=<<im; }
complex operator+(complex);
};

complex complex::operator+(complex c)
{
complex aux;
aux.re=re+c.re;
aux.im=im+c.im;
return aux;
}

void main()
{
complex c1(1, 2), c2(3, 4), c3, c4;
c1.afisare();
c2.afisare();
Programare C++ - Lucrarea nr. 6

- 105 -
c3=c1+c2;
c3.afisare();
c4=c1+c2+c3;
c4.afisare();
}

Programul va afia:

re=1 im=2
re=3 im=4
re=4 im=6
re=8 im=12

Expresia c3=c1+c2 este echivalent cu apelul:

c3=c1.operator+(c2);

Pentru evaluarea expresiei c4=c1+c2+c3, compilatorul creeaz un obiect
temporar de tip complex care preia un rezultat parial, astfel c expresia este
echivalent cu secvena:

temp=c1.operator+(c2);
c3=temp.operator+(c3);

Se observ c, n cazul definirii funciei operator ca funcie membr a
clasei, primul operand este obligatoriu de tipul clasei respective.

Supradefinirea operatorilor folosind funcii prietene clasei

n cazul definirii funciei operator + din exemplul anterior cu ajutorul unei
funcii prietene, funcia fiind extern clasei, este necesar s preia doi parametri
de tip complex, prototipul funciei fiind:

complex operator+(complex, complex);

Expresia x+y , unde x i y sunt dou obiecte de tip complex, este
echivalent cu un apel al funciei operator+ de forma:

operator+(x, y);

Programare C++ - Lucrarea nr. 6

- 106 -
class complex
{
double re, im;
public:
complex(double r=0, double i=0)
{ re=r; im=i; }
void afisare()
{ cout<<\nre=<<re<<\tim=<<im; }
friend complex operator+(complex, complex); // funcia operator+() este declarat
// funcie prieten clasei complex
};

complex operator+(complex a, complex b) // funcia operator+() este funcie extern
// clasei complex
{
complex c;
c.re=a.re+b.re;
c.im=a.im+b.im;
return c;
}

void main()
{
complex c1(1, 2), c2(3, 4), c3, c4;
c1.afisare();
c2.afisare();
c3=c1+c2;
c3.afisare();
c4=c1+c2+c3;
c4.afisare();
}

Programul va afia:

re=1 im=2
re=3 im=4
re=4 im=6
re=8 im=12

Expresia c3=c1+c2 este echivalent cu apelul:

c3=operator+(c1, c2);

La evaluarea expresiei c4=c1+c2+c3 se ine seama de asociativitatea
operatorului + de la stnga la dreapta, deci c1+c2+c3=(c1+c2)+c3.
Apelul funciei operator+ se face astfel:
Programare C++ - Lucrarea nr. 6

- 107 -

c3=operator+(operator+(c1, c2), c3);


Supradefinirea operatorilor unari

Operatorii unari pot fi supradefinii att cu funcii membre clasei, ct i cu
funcii prietene clasei. Expresia op x, unde op este simbolul operatorului
supradefinit, este echivalent cu:

x.operator op(); // pentru definire cu funcie membr
sau
operator op(x) // pentru definire cu funcie prieten


#include <iostream.h>

class pozitie {
int x, y; // coordonatele ce descriu poziia
public:
pozitie (int abs=0, int ord=0) //constructor cu parametri implicii
{
x=abs;
y=ord;
}
void afisare()
{
cout<<"x="<<x;
cout<<", y="<<y<<'\n';
}
pozitie operator ++() // supradefinire operator ++
{
x++; y++;
cout<<"Op++";
return pozitie(x, y);
}
pozitie operator --() // supradefinire operator --
{
x--; y--;
cout<<"Op--";
return pozitie(x, y);
}
};

Programare C++ - Lucrarea nr. 6

- 108 -
void main()
{
pozitie p(1, 1), r;
r=p++; // expresie echivalent cu r=p.operator++();
r.afisare();
r=--p; // expresie echivalent cu r=p.operator(); - diferena
// dintre pre i post fixarea operatorului nu mai este
// valabil
r.afisare();
r=++p;
r.afisare();
r=p--;
r.afisare();
}


Supradefinirea operatorului de atribuire (=)

n lipsa supradefinirii operatorului de atribuire pentru operanzi clas,
compilatorul ofer o semnificaie predefinit a acestuia, atribuirea fcndu-se
membru cu membru, n mod asemntor crerii constructorului de copiere
implicit. Probleme similare utilizrii constructorului de copiere apar i la
utilizarea operatorului de atribuire.
Operatorul de atribuire poate fi supradefinit doar prin funcii membre
clasei respective, nu i prin funcii prietene.
Se consider clasa complex definit anterior.

complex c1(5, 6), c2;
c2=c1;
c2.afisare();

Expresia c2=c1 are ca efect atribuirea valorilor membru cu membru, deci
este echivalent cu atribuirile:
c2.re=c1.re;
c2.im=c1.im;

Vom analiza n continuare o clas vector destinat crerii de tablouri de
componente de tip double, avnd dimensiuni diferite.

#include <iostream.h>

class vector
{ int grad; // preia ca valoare dimensiunea tabloului
Programare C++ - Lucrarea nr. 6

- 109 -
double* comp; // preia ca valoare adresa la care se creeaz tabloul de
// componente
public:
vector(int gr=10); // constructor cu parametru implicit
~vector(); // destructor
void afisare(); }; // funcie de afiare

vector::vector(int gr)
{ cout<<"\nConstructor vector\n";
grad=gr;
comp=new double[grad];
cout<<\nVectorul are <<grad<< componente, introduceti valorile:
for(int i=0; i<grad; i++)
cin>>comp[i];
}

vector::~vector()
{ delete comp; }

void vector::afisare()
{
cout<<"\n\nAdresa obiectului este: this="<<this;
cout<<\nVectorul are << grad << componente;
cout<<"\nAdresa membrului comp este: "<<&comp;
cout<<"\tsi contine valoarea: "<<comp;
cout<<"\nComponentele vectorului sunt:\n";
for(int i=0; i<grad; i++)
cout<<comp[i]<<" ";
}

void main(){
vector v1(5), v2(3);
v1.afisare();
v2.afisare();
v2=v1;
v2.afisare();
}

Execuia programului are ca rezultat:

Constructor vector
Vectorul are 5 componente, introduceti valorile:
1.5 // valorile sunt introduse de la tastatur
2.3
5.1
6
9.2
Programare C++ - Lucrarea nr. 6

- 110 -
Constructor vector
Vectorul are 3 componente, introduceti valorile:
7.1 // valorile sunt introduse de la tastatur
8.22
9.5

Adresa obiectului este: this=0xfff2
Vectorul are 5 componente
Adresa membrului comp este : 0xfff4 si contine valoarea 0x130a
Componentele vectorului sunt:
1,5 2.3 5.1 6 9.2
Adresa obiectului este: this=0xffee
Vectorul are 3 componente
Adresa membrului comp este : 0xfff0 si contine valoarea 0x1336
Componentele vectorului sunt:
7.1 8.22 9.5

Adresa obiectului este: this=0xffee
Vectorul are 5 componente
Adresa membrului comp este : 0xfff0 si contine valoarea 0x1330a
Componentele vectorului sunt:
1.5 2.3 5.1 6 9.2

Se observ c efectul expresiei v2=v1 este atribuirea valorilor membru cu
membru, deci expresia este echivalent cu:

v2.grad=v1.grad;
v2.comp=v1.comp;

Ca urmare, zona de memorie alocat dinamic pentru membrul v1.comp prin
constructorul obiectului v1, va fi referit n continuare i prin v2.comp.
Dealocarea memoriei prin apelul destructorului pentru cele dou obiecte, v1, v2,
se va adresa aceleiai zone de memorie, n schimb, zona alocat iniial de
vectorul v2 va rmne n continuare alocat.
Avnd n vedere aceste aspecte, rezult necesitatea supradefinirii
operatorului de atribuire.

#include <iostream.h>

class vector
{ int grad;
double* comp;
public:
vector(int gr=10);
~vector();
Programare C++ - Lucrarea nr. 6

- 111 -
vector(vector&); // constructor de copiere
void afisare();
void operator=(vector &); // supradefinire operator de atribuire
};

vector::vector(int gr)
{ cout<<"\nConstructor vector\n";
grad=gr;
comp=new double[grad];
cout<<"\nVectorul are "<<grad <<"componente, introduceti valorile:\n";
for(int i=0; i<grad; i++)
cin>>comp[i];
}

vector::~vector()
{ cout<<"\nDestructor obiect cu adresa: "<<this;
delete comp;
}

vector::vector(vector &v1)
{
grad=v1.grad;
comp=new double[grad];
for(int i=0; i<grad; i++)
comp[i]=v1.comp[i];
}
void vector::afisare()
{
cout<<"\n\nAdresa obiectului este: this="<<this;
cout<<"\nVectorul are "<<grad<<"componente";
cout<<"\nAdresa membrului comp este: "<<&comp;
cout<<"\tsi contine valoarea: "<<comp;
cout<<"\nComponentele vectorului sunt:\n";
for(int i=0; i<grad; i++)
cout<<comp[i]<<" ";
}

void vector::operator=(vector &v)
{ if (this!=&v) // este necesar verificarea apariiei unei situaii ob=ob
// (ob este de tip vector)
{ delete comp; // dealocarea memoriei corespunztoare componentelor
// iniiale
grad=v.grad;
comp=new double[grad]; //se face o nou alocare pentru un numr de componente
// ce poate fi diferit de cel iniial
for(int i=0; i<grad; i++)
comp[i]=v.comp[i]; }
}
Programare C++ - Lucrarea nr. 6

- 112 -
void main()
{
vector v1(5), v2(3);
v1.afisare();
v2.afisare();
v2=v1;
v2.afisare();
}

Execuia programului are un rezultat asemntor programului anterior, dar
se poate observa c, n urma atribuirii, membrii comp ai celor doi vectori
v1.comp i v2.comp nu mai pointeaz ctre aceeai zon de memorie, ci
v2.comp a fost realocat, de data aceasta rezervndu-se memorie pentru 5
componente n care s-au copiat valorile corespunztoare vectorului v1.
Operaiile care se efectueaz n funcia operator=() sunt:
- eliberarea tabloului de componente al vectorului v2;
- alocarea unui nou tablou de componente de dimensiune egal cu a
vectorului v1;
- copierea elementelor tabloului de componente v1 n tabloul de
componente v2.

Execuia programului are ca efect afiarea mesajelor:

Constructor vector
Vectorul are 5 componente, introduceti valorile:
1.5 // valorile sunt introduse de la tastatur
2.3
5.1
6
9.2

Constructor vector
Vectorul are 3 componente, introduceti valorile:
7.1 // valorile sunt introduse de la tastatur
8.22
9.5

Adresa obiectului este: this=0xfff2
Vectorul are 5 componente
Adresa membrului comp este : 0xfff4 si contine valoarea 0x132a
Componentele vectorului sunt:
1.5 2.3 5.1 6 9.2

Adresa obiectului este: this=0xffee
Vectorul are 3 componente
Programare C++ - Lucrarea nr. 6

- 113 -
Adresa membrului comp este : 0xfff0 si contine valoarea 0x1356
Componentele vectorului sunt:
7.1 8.22 9.5

Adresa obiectului este: this=0xffee
Vectorul are 5 componente
Adresa membrului comp este : 0xfff0 si contine valoarea 0x1356
Componentele vectorului sunt:
1,5 2.3 5.1 6 9.2

Atribuirea v2=v1 este echivalent cu apelul funciei operator=() sub
forma:
v2.operator=(v1)

n definirea funciei operator=() s-a specificat ca parametru o referin de
vector pentru a crete viteza de execuie i a reduce necesarul de memorie, lucru
ce poate deveni important atunci cnd obiectele transferate prin parametru au
dimensiuni semnificative.

n cazul n care se include o expresie de forma a=b=c, rezultatul este un
mesaj de eroare, funcia operator=() nereturnnd nici o valoare. Pentru a putea
folosi atribuiri multiple, funcia trebuie s returneze tipul clas respectiv, sau
referina acelui tip. Prototipul funciei operator=() va fi:

vector& operator=(vector&);

i funcia va fi definit astfel:

vector & vector::operator=(vector &v)
{
if (this!=&v)
{ delete comp;
grad=v.grad;
comp=new double[grad];
for(int i=0;i<grad;i++)
comp[i]=v.comp[i];
}
return *this;
}

Expresia v1=v2=v3 este echivalent cu:

v1.operator=(v2.operator=(v3));

Programare C++ - Lucrarea nr. 6

- 114 -
Asociativitatea operatorului de atribuire este de la dreapta la stnga.
Rezultatul returnat este chiar obiectul implicit al funciei.


Supradefinirea operatorului []

Operatorul de indexare [ ] este un operator binar. El are forma general:

expresie1[expresie2]

Funcia operator[] se definete ca funcie membr a unei clase (ea nu
poate fi definit ca funcie prieten).
Dac pentru o clas A s-a definit funcia operator[](), expresia a[n], unde
a este obiect al clasei A, este echivalent cu :

a.operator[](n)

Clasa vector definit anterior se poate completa cu funcia operator[]()
avnd prototipul:
double& operator[](int);

care, va fi definit astfel:

double& vector::operator[ ](int n)
{ return comp[n];}

Pentru ca operaia s poat fi folosit la nscrierea valorilor pentru
elementele respective, este necesar ca rezultatul returnat s fie referina
elementului cu numrul n.
Funcia main() se poate completa cu secvena:

v2[0]=99.9
for (int i=0; i<5; i++)
cout<<"\ncomp["<<i<<"]="<<v2[i];

Dac elementele componente ale vectorului v2 aveau valorile 1.5, 2.3,
5.1, 6, 9.2, la execuia programului, corespunztor acestei secvene se va afia:

comp[0]= 99.9 // comp[0] i-a modificat valoarea prin atribuirea v2[0]=99.9
comp[1]= 2.3
comp[2]= 5.1
Programare C++ - Lucrarea nr. 6

- 115 -
comp[3]= 6
comp[4]= 9.2

n exemplul urmtor se declar clasa persoana care preia datele unei
persoane (nume i vrst) i o clas exemplu_index care are ca membru un
tablou de dimensiune N cu elemente de tip persoana (lista). Se definesc trei
criterii de localizare a unui element al listei (dup nume, dup vrst i dup
index) supradefinind funcia operator[]().

#include <iostream.h>
#include <string.h>

#define N 10

class persoana
{
char nume[20];
double varsta;
friend exemplu_index; // clasa exemplu_index este declarat prieten pentru ca
// funciile membre acesteia s aib acces la membrii
// clasei persoana
public:
persoana() // constructor implicit
{ *nume='\0';
varsta=0;
}
void date_persoana(char * p, double v) // funcie membr prin care se transmit
// valori pentru membrii obiectului
{ strncpy(nume, p, 19);
nume[19]='\0';
varsta=v;
}
void afisare()
{ if (this!=NULL)
cout<<"\nNume: "<<nume<<" varsta: "<<varsta<<" ani";
else
cout<<"\nElement inexistent in lista"; }
};

class exemplu_index
{
persoana lista[N]; // membrul lista este un tablou de N elemente de
// tip persoana
public:
void citire_date(void); // citirea datelor de la tastatur pentru cele N
// persoane din lista
Programare C++ - Lucrarea nr. 6

- 116 -
persoana * operator[](char *); // funcie operator[]() care localizeaz elementul
// dup nume
persoana * operator[](double); // funcie operator[]() care localizeaz elementul
// dup vrsta
persoana * operator[](int); // funcie operator[]() care localizeaz elementul
// dup index
};

void exemplu_index::citire_date()
{
char n[20];
double d;
for(int i=0; i<N; i++)
{ cout<<"\nTastati numele si varsta: ";
cin>>n>>d;
lista[i].date_persoana(n, d);
}
}

persoana * exemplu_index::operator[](char * string )
{
for (int i=0; i<N; i++)
if (strcmp(lista[i].nume, string)==0)
return &lista[i];
return NULL;
}

persoana * exemplu_index::operator[](double varsta)
{
for (int i=0; i<N; i++)
if (lista[i].varsta==varsta)
return &lista[i];
return NULL;
}

persoana * exemplu_index::operator[](int i )
{
if (i<N)
return &lista[i];
return NULL;
}

void main(void)
{
exemplu_index lista_pers; // se declar un obiect exemplu_index
lista_pers.citire_date(); // se citesc datele persoanelor din lista
lista_pers["Adrian"]->afisare(); // se caut n lista elementul cu numele=Adrian
lista_pers[(double)22]->afisare(); // se caut n lista elementul cu varsta=22;
Programare C++ - Lucrarea nr. 6

- 117 -
// conversia este necesar pentru a impune apelul
// funciei cu cutare dup vrst, altfel, valoarea
// parametrului fiind de tip int, se apeleaz funcia ce
// face localizarea dup index
lista_pers[2]->afisare();
}
n funcia de afiare s-a introdus verificarea adresei obiectului persoana.
Funciile operator[]() returneaz adresa obiectelor persoana dac localizarea
elementului s-a putut face, n caz contrar returneaz valoarea 0 (NULL).


Supradefinirea operatorilor new i delete

Operatorii new i delete sunt supradefinii astfel nct se admite orice tip
de dat, inclusiv tipul clas. Ei pot fi suprancrcai pentru clasele definite,
oferind operaii specializate de alocare, respectiv eliberare dinamic a memoriei.
Funciile operator new i delete pot fi definite ca funcii membre ale
clasei, nu i ca funcii prietene acesteia. Acestea sunt funcii implicit statice,
chiar dac nu sunt declarate static explicit.
Funcia operator new trebuie s aib un parametru de tipul size_t care
precizeaz dimensiunea n octei a obiectului alocat i s returneze un pointer
void (void*) care s conin adresa zonei de memorie alocat obiectului:

void * operator new(size_t);

Tipul size_t este declarat n stdlib.h ( alloc.h, stddef.h, mem.h, etc.).
Parametrul size_t, chiar dac este specificat de programator, calculul
dimensiunii obiectului revine compilatorului. Din acest motiv, la apelul funciei
nu este obligatoriu s se specifice argumentul, deci nu se modific sintaxa de
utilizare.
Operatorul new supradefinit pstreaz proprietatea de a apela
constructorul clasei dup alocarea dinamic a memoriei necesare obiectului.
Funcia operator delete trebuie s primeasc un parametru pointer de
tipul clasei asociate sau void care, la apel, conine adresa obiectului care se
elimin. Funcia nu returneaz rezultat. Funcia operator delete poate s aib un
al doilea parametru, opional, de tip size_t.

void operator delete (void * )

Operatorul delete supradefinit pstreaz proprietatea de a apela
destructorul clasei nainte de a elibera zona de memorie a obiectului dinamic.
Programare C++ - Lucrarea nr. 6

- 118 -
Dac sunt supradefinii operatorii new i delete, definiiile standard pot fi
utilizate n continuare folosind operatorul de rezoluie (::).

#include <iostream.h>
#include <stddef.h>

class vector
{ int grad;
double* comp;
static int cnt; // membru static, utilizat ca i contor pentru
// obiectele vector dinamice
public:
vector(int gr=10); // constructor cu parametru implicit
~vector(); // destructor
void afisare();
void * operator new(size_t dim); // prototip funcie operator new()
void operator delete(void *); // prototip funcie operator delete()
};

int vector::cnt=0;

vector::vector(int gr) // definire constructor cu parametru implicit
{ cout<<"\nConstructor vector\n";
grad=gr;
comp=new double[grad];
cout<<"\nVectorul are "<<grad <<"componente, introduceti valorile:\n";
for(int i=0; i<gr; i++)
cin>>comp[i];
}

vector::~vector() // definire destructor
{
cout<<"\nDestructor obiect cu adresa: "<<this;
delete comp;
}

void * vector::operator new(size_t dim) // supradefinirea funciei operator new()
{
cnt++; // la crearea unui obiect dinamic de tip
// vector, contorul se incrementeaz
cout<<"\ndim="<<dim<<"\nnew: cnt="<< cnt;
return new char[dim]; // valoarea returnat este adresa memoriei
// alocate prin operatorul new predefinit
}

void vector::operator delete(void * p) // supradefinirea funcie operator delete()
{
Programare C++ - Lucrarea nr. 6

- 119 -
cout<<"\ndelete: cnt= "<<cnt;
cnt--; // la distrugerea unui obiect dinamic vector,
// contorul se decrementeaz
delete p;
}

void vector::afisare()
{
cout<<"\nVectorul are "<<grad<<"componente";
cout<<"\nComponentele vectorului sunt:\n";
for(int i=0;i<grad;i++)
cout<<comp[i]<<" ";
}

void main()
{
vector *pv1, *pv2, *pv3; // se declar pointeri la tipul vector
pv1=new vector(5); // se aloc dinamic memorie pentru un obiect vector;
// operator new apeleaz constructorul cu parametru
// implicit, valoarea parametrului fiind 5
pv2=new vector(*pv1); // se aloc dinamic memorie pentru un obiect vector,
// copie a celui de la adresa depus n pv1, ca urmare se
// apeleaz constructorul de copiere
pv3=::new vector(2); // alocarea de memorie se face cu operatorul new
// predefinit
pv1->afisare();
pv2->afisare();
pv3->afisare();
delete pv1; // se elimin obiectul pv1, apelndu-se operatorul delete
// definit de utilizator
delete pv2; // se elimin obiectul pv1, apelndu-se operatorul delete
// definit de utilizator

::delete pv3; // se elimin obiectul pv1, apelndu-se operatorul delete
// predefinit
}


Operatorii new i delete pot fi supradefinii folosind funcii independente,
cu prototip similar celor anterioare. n aceast situaie, operatorii new i delete
vor fi apelai pentru toate tipurile de date, inclusiv cele standard. Operatorii
predefinii nu mai pot fi folosii, deci programatorul este cel care trebuie s
controleze gestionarea alocrii dinamice a memoriei .



Programare C++ - Lucrarea nr. 6

- 120 -
Exerciii:

1. Se definete clasa de matrici ptratice:

class matrice
{ int dim;
double * m ;
public:
matrice();
~matrice();
void citeste();
void afiseaza();
};

S se completeze clasa cu operatorul unar - (opusul matricei) i cu operatorii
binari + (suma), * (produsul) definii prin funcii membre sau prietene i
operatorul de atribuire. S se defineasc o funcie prieten care s returneze
transpusa matricii.


2. Fie clasa : Q={ p/q | p,q Z, q0} definit mai jos:

class rational
{ int p, q;
void simplifica(); // aduce membrii p i q n form ireductibil
public:
rational(int a=0, b=1); // se verifica b0 i se aduc p i q n form
// ireductibil
~rational();
void afiseaza();
};

Sa se completeze clasa cu operatorii unari i binari corespunztori operaiilor
specifice numerelor raionale, folosind att funcii membre, ct i funcii
prietene clasei, membrii p i q fiind adui n form ireductibil dup fiecare
modificare a lor.




- 121 -
Lucrarea nr. 7




Conversii de tip definite de utilizator

Procedee de definire a conversiilor

n timpul execuiei unui program, compilatorul efectueaz conversii
implicite, n conformitate cu regulile deja cunoscute, n urmtoarele situaii:
- la evaluarea unei expresii, pentru fiecare operator, dac operanzii sunt
de tipuri diferite, se efectueaz conversia ctre operandul de tip cu
domeniu de valori mai mare a celui cu domeniu de valori mai mic;
- la atribuire, se face conversia valorii atribuite ctre tipul operandului
care primete valoarea;
- la transferul parametrilor, se face conversia parametrilor efectivi ctre
tipul de dat specificat n prototipul funciei.

n limbajul C++, se pot defini conversii explicite de tip folosind
operatorul cast.
Pentru clase se pot defini reguli de conversie care se adaug conversiilor
standard i sunt apelate de compilator n toate situaiile specificate mai sus.
Acestea sunt utilizate cu unele restricii:
- ntr-un lan de conversii, compilatorul admite o singur conversie
definit de utilizator;
- conversiile definite de utilizator se folosesc doar dac nu exist alte
soluii (de exemplu, la atribuire, nti se verific existena
supradefinirii operatorului de atribuire i numai n absena acesteia se
va aplica conversia definit de utilizator).

Exist dou modaliti de definire a conversiilor. O prim variant const
n suprancrcarea operatorului unar cast folosind o funcie membr a clasei.
De exemplu, pentru tipul complex se pot defini conversii la tipul double i
la tipul poziie:

complex::operator double(),
Programare C++ - Lucrarea nr. 7

- 122 -
respectiv
complex::operator pozitie()

A doua variant const n definirea constructorului cu un singur parametru
de tipul din care se face conversia. De exemplu:

complex(double)
complex(pozitie)

Observaii: - supradefinirea operatorului cast nu poate realiza conversii dintr-
un tip fundamental ntr-un tip clas;
- supradefinirea constructorului nu permite conversia unui tip clas
ntr-un tip fundamental.


Supradefinirea operatorului cast folosind funcii membre clasei

Operatorul cast este un operator unar. Supradefinirea operatorului cast se
poate face numai cu funcii membre clasei din care se face conversia. n
declaraia i definiia funciei nu trebuie specificat tipul rezultatului, acesta fiind
specificat, implicit, prin numele operatorului. Funcia nu are nici parametru, ea
fiind funcie membr a clasei primete adresa obiectului, ca orice funcie
membr.
Se definete clasa complex care va conine funcia membr operator
float() care va face conversia unui obiect complex n dat de tip float, valoarea
returnat fiind partea real (membrul re) a numrului complex.

#include <iostream.h>

class complex
{
float re, im;
public:
complex(float r=0, float i=0)
{
cout<<"\nConstructor: ";
re=r; im=i;
afisare();
}
complex(complex &c)
{
cout<<"\nConstructor de copiere: ";
Programare C++ - Lucrarea nr. 7

- 123 -
re=c.re; im=c.im;
afisare();
}
operator float()
{
cout<<"\nApel float-valoare returnata:"<<re;
return re;
}

void afisare()
{ cout<<"re="<<re<<"\tim="<<im; }
};

void fct(float r)
{
cout<<"\nApel functie: valoare float="<<r;
}

void main()
{
complex c1(2, 2), c2(5, 5);
float r1, r2;

// 1.Conversie la atribuire
cout<<\n1.1 conversie explicita la atribuire;
r1=(float)c1;
cout<<"\tr1="<<r1;

cout<<\n1.2. conversie implicita la atribuire;
r2=c2;
cout<<"\tr2="<<r2;

// 2. Conversii la transfer de parametrii
cout<<\n2.1 apel al functie fara conversie a parametrului;
fct(r1);

cout<<\n2.2 apel al functie cu conversie explicita a parametrului ;
fct((float)c1);

cout<<\n2.3 apel al functiei cu conversie implicita a parametrului;
fct(c2);

// 3. Conversii la evaluarea expresiilor
cout<<\n3.1 evaluarea expresiilor - conversie explicita a operandului;
r1=(float)c1+r1;
cout<<"\tr1="<<r1;

cout<<\n3.2.a evaluarea expresiilor - conversie implicita a unui operand;
Programare C++ - Lucrarea nr. 7

- 124 -
r1=c1+r1;
cout<<"\tr1="<<r1;

cout<<\n3.2.b evaluarea expresiilor - conversie implicita a ambilor operanzi;
r1=c1+c2;
cout<<"\tr1="<<r1;

cout<<\n3.2.c evaluarea expresiilor - conversie implicita complex->float\
i double->float ;
r1=c1+2.5;
cout<<"\tr1="<<r1;
}

In cazul atribuirii, exemplele 1.1 i 1.2, se observ c, indiferent c se
realizeaz o conversie implicit sau explicit, se apeleaz operatorul de
conversie pentru membrul drept.
In cazul apelurilor funciei fct(), se observ c n situaiile care presupun
conversii de tip, nti se efectueaz conversia de la complex ctre float, valoarea
rezultat fiind preluat de parametru i apoi se apeleaz funcia. Astfel se
explic faptul c nu se apeleaz constructorul de copiere pentru preluarea valorii
de ctre parametrul funciei.
In cazul expresiei c1+r1, compilatorul verific iniial existena
supradefinirii operatorului + pentru operanzi, unul complex, cellalt float. n
lipsa acestuia, se caut definirea unei conversii care s permit operaia ntre doi
operanzi de acelai tip.
Pentru expresia c1+c2, n mod similar situaiei anterioare, se va face
conversia ambilor operanzi ctre tipul obiectului care preia valoarea, deci
amndoi operanzii complex se convertesc la tipul float.
n expresia c1+2.5, constanta 2.5 este memorat n format double, deci c1
va cunoate conversia complex->float->double. n aceast succesiune, doar o
conversie este definit de utilizator, deci ea va fi acceptat de compilator.
Dac n timpul efecturii conversiilor compilatorul sesizeaz existena
mai multor variante de conversii, ambiguitatea va fi semnalat cu mesaj de
eroare.


Supradefinirea operatorului cast folosind funcii prietene clasei

Operator cast poate fi utilizat i pentru definirea unor conversii a unui tip
clas n alt tip clas. n aceast situaie, funciile operator cast folosite sunt
funcii prietene acesteia.
Se definesc clasele complex i poziie, definindu-se totodat operatorul de
Programare C++ - Lucrarea nr. 7

- 125 -
conversie complex -> pozitie.

#include <iostream.h>

class complex; //declaraie incomplet a clasei complex
class pozitie
{
int x, y ;
public:
pozitie(int abs=0, int ord=0) //constructor pozitie cu parametri implicii
{ x=abs;
y=ord;
cout<<"\nConstructor pozitie ";
afisare();
}
void afisare()
{ cout<<"x="<<x;
cout<<", y="<<y;
}
operator complex(); // declarare conversie pozitie->complex
};

class complex {
float re, im;
public:
complex(float r=0, float i=0) //constructor complex
{ re=r; im=i; }
void afisare()
{ cout<<"(re="<<re;
cout<<", im="<<im<<")";
}
friend complex operator +(complex, complex); // supradefinire operator +() binar pentru
// clasa complex
friend pozitie::operator complex(); // declararea funciei operator complex() membr
// a clasei pozitie, prieten clasei complex
};

pozitie::operator complex()
{ cout<<"\nOperator pozitie->complex:";
complex c;
c.re=x;
c.im=y;
cout<<"("<<x<<","<<y<<")->";
c.afisare();
return c;
}

Programare C++ - Lucrarea nr. 7

- 126 -
complex operator +(complex a, complex b)
{ cout<<"\nOperator + complex:";
a.afisare();
cout<<" + ";
b.afisare();
cout<<'\n';
return complex(a.re+b.re, a.im+b.im);
}

void main()
{
complex c1(1.1, 1.1), r;
pozitie p1(10, 10), p2(20, 20);
cout<<'\n';

// conversie complex=complex+pozitie
r=c1+p2;
cout<<"r : ";
r.afisare();
cout<<"\n\n";

//conversie complex=pozitie+pozitie
r=p1+p2;
cout<<"r : ";
r.afisare();
cout<<"\n\n";
}

Execuia programului are ca rezultat afiarea:

Constructor pozitie: x=10, y=10
Constructor pozitie: x=20, y=20

Operator pozitie->complex: (20, 20)->re=(20, im=20)
Operator + complex : (re=1.1, im=1.1) + (re=20, im=20)
r: (re=21.1, im=21.1)

Operator pozitie->complex: (20, 20)->(re=10, im=20)
Operator pozitie->complex: (10, 10)->(re=10, im=10)
Operator + complex : (re=10, im=10) + (re=20, im=20)
r: (re=30, im=30)

Pentru ca n declararea clasei poziie s poat fi inclus operator complex(),
a fost necesar nti declararea incomplet a clasei complex.
Funcia operator complex() trebuie s aib acces la membrii clasei
complex, deci ea a trebuit declarat prieten acesteia.
Programare C++ - Lucrarea nr. 7

- 127 -
Conversii de tip folosind constructori

Conversia unei date de tip standard n dat de tip clas
De exemplu, pentru clasa complex definit anterior, datorit faptului c a
fost definit un constructor cu parametri implicii, se poate obine conversia float
->complex prin specificarea unui singur parametru la crearea obiectului
complex.
Se consider exemplul urmtor:

#include <iostream.h>

class complex
{
float re, im;
public:
complex(float r=0, float i=0) //constructor cu parametri implicii
{
cout<<"\nConstructor : " ;
re=r; im=i;
afisare();
}
void afisare()
{ cout<<"re="<<re;
cout<<" , im="<<im;
}
};

void f(complex c)
{
cout<<"\nApel functie : ";
c.afisare();
}

void main()
{
complex c1(1, 1), c2(2, 2);
float r1=10, r2=20;

// conversie explicita float->complex
c1=complex(r1);
cout<<endl;
c1.afisare();

// conversie implicita float->complex la atribuire
c2=r2;
Programare C++ - Lucrarea nr. 7

- 128 -
cout<<endl;
c2.afisare();

// conversie implicita float->complex la transfer de parametri
f(r1);

// conversie implicit int->float->complex la atribuire
int i=7;
c2=i;
cout<<endl;
c2.afisare();
}

Pe ecran vor aprea mesajele:

Constructor: re=1, im=1
Constructor: re=2, im=2
Constructor: re=10, im=10
re=10, im=0
Constructor: re=20, im=0
re=20, im=0
Constructor: re=10, im=0
Apel functie: re=10, im=0
Constructor: re=7, im=0
re=7, im=0

Se observ c n toate situaiile s-a creat un obiect temporar de tip
complex care preia pentru partea real valoarea float, iar membrul im preia
valoarea implicit 0, apoi se face copierea, membru cu membru, n obiectul
destinaie.
Constructorul poate fi folosit i pentru alte conversii de tip, de exemplu
int -> complex, valoarea int fiind nti convertit la tipul float i n final are loc
conversia float -> complex.

Conversia unui tip clas n alt tip clas
Se poate defini conversia unui tip clas n alt tip clas, incluznd n
declaraia clasei ctre care se face conversia, un constructor cu un parametru
dinspre care se face conversia.
n exemplul urmtor se va defini conversia tipului pozitie n tipul
complex.

#include <iostream.h>
class pozitie;
class complex
Programare C++ - Lucrarea nr. 7

- 129 -
{
float re, im;
public:
complex(float r=0, float i=0)
{ re=r; im=i; }
complex(pozitie); // constructor cu parametru pozitie care permite
// conversia pozitie->complex
void afisare()
{ cout<<"\nComplex: re="<<re;
cout<<" , im="<<im;
}
complex operator+(complex c)
{
complex aux;
aux.re=re+c.re;
aux.im=im+c.im;
return aux;
}
};

class pozitie
{
int x, y;
public:
pozitie(int abs=0, int ord=0)
{x=abs; y=ord;}
void afisare()
{cout<<"\nPozitie: x="<<x<<"\ty="<<y;}
friend complex::complex(pozitie); // constructorul clasei complex se declar
// funcie prieten
};

complex::complex(pozitie p)
{
re=p.x; im=p.y;
}

void main()
{
complex c1, c2;
pozitie p1(5, 5), p2(7, 7);

// conversie pozitie->complex explicit
c1=complex(p1);
cout<<endl;
p1.afisare();
c1.afisare();

Programare C++ - Lucrarea nr. 7

- 130 -
// conversie pozitie->complex implicit la atribuire
c2=p2;
cout<<endl;
p2.afisare();
c2.afisare();

// conversii implicite la evaluarea expresiilor
c1=p1+p2; // conversii pozitie->complex
cout<<endl;
c1.afisare();

c1=p1+5.5; // pentru p1 se face conversie pozitie->complex
// pentru 5.5 au loc conversiile double->float->complex
cout<<endl;
c1.afisare();
}

Pentru a putea declara constructorul folosit pentru conversia pozitie
->complex, este necesar declararea incomplet a clasei pozitie nainte de
declararea clasei complex. Acest constructor trebuie declarat funcie prieten
clasei pozitie pentru a avea acces la membrii acesteia.
La evaluarea expresiei p1+p2, se observ extinderea, prin conversie, a
operatorului+ supradefinit n clasa complex. n lipsa supradefinirii operatorului+
n clasa pozitie, compilatorul va efectua conversia operanzilor p1 i p2 la tipul
complex, rezultatul fiind, de asemenea, de tip complex. Avnd n vedere tipul
rezultatului, rezult c acesta nu poate fi atribuit unui obiect de tip pozitie, deci o
expresie de forma p1=p1+p2 va genera un mesaj de eroare.
n cazul expresiei p1+5.5, n lipsa supradefinirii operatorului+ pentru un
operand pozitie i cellalt double, se va apela, de asemenea, operatorul +
supradefinit pentru clasa complex. Operandul p1 este convertit la tipul complex
prin constructorul cu parametru pozitie, iar constanta de tip double 5.5 este
convertit la tipul float i apoi, prin constructorul cu parametri implicii, este
convertit la tipul complex.

Observaii:- Operatorul cast este un operator unar. El se poate defini cu o funcie
membr a clasei, rezultatul returnat fiind obligatoriu de tipul
operatorului;
- Supradefinirea operatorului cast nu se poate folosi pentru a realiza
conversii dintr-un tip fundamental ntr-un tip clas;
- Constructorul unei clase nu permite conversia unui obiect de tip
clas ntr-un tip fundamental, deoarece funcia constructor
returneaz implicit un obiect de tipul clasei creia i aparine;
- Conversii de la un tip clas la alt tip clas se pot realiza att prin
Programare C++ - Lucrarea nr. 7

- 131 -
supradefinirea operatorului cast, ct i prin funcii constructor. n
ambele situaii este important ordinea plasrii declaraiilor claselor.
Nu este permis definirea unui tip de conversie prin ambele variante,
acest lucru conducnd la o ambiguitate semnalat prin mesaj de
eroare de compilator.
- n situaia n care un operator este supradefinit ntr-o clas, la
evaluarea expresiilor care conin operatori de tipuri diferite clasei,
compilatorul va extinde funcionarea respectivului operator pentru
operanzi de tipuri pentru care nu exist supradefinit operatorul, dac
conversiile necesare sunt definite i lanul de conversii nu necesit
mai mult de una definit de utilizator.


Exerciii:

1. S se defineasc clasa cerc ce descrie un cerc prin raz i coordonatele
centrului. S se defineasc conversii de la tipuri de date fundamentale la
tipul cerc, de la tipul cerc la tipuri fundamentale. S se defineasc
conversii de la i ctre clasa pozitie ce conine coordonatele unui punct.

2. S se defineasc clasa vector_pozitie ce descrie un vector de poziie prin
modul i unghiul fa de abscis. S se supradefineasc operatorul binar +
care s compun doi vectori. Avnd definit clasa cerc de la exerciiul nr.
1, s se defineasc conversiile vector_pozitie->cerc (raza cercului preia
valoarea modulului, coordonatele centrului fiind (0,0)) i cerc
->vector_pozitie (modulul vectorului preia valoarea razei, unghiul fiind
0). S se evidenieze extinderea funcionrii operatorului + supradefinit
asupra obiectelor de tip cerc.







- 132 -
Lucrarea nr. 8



Motenirea. Clase derivate


Prin procedeul de derivare se urmrete obinerea unor clase noi, numite
clase derivate, care motenesc proprietile unei clase deja definite, numit
clas de baz.
Clasele derivate conin toi membrii clasei de baz, la care se adaug noi
membrii, date i funcii membre.
Clasa de baz nu este afectat de derivare, ea putnd fi compilat anterior
eventualelor derivri.
Dintr-o clas de baz se poate deriva o clas care, la rndul su, s
serveasc drept clas de baz pentru derivarea altora. Prin aceast succesiune se
obine o ierarhie de clase. Pornind de la clase simple i generale, fiecare nivel
al ierarhiei adaug noi proprieti, obinndu-se clase cu un grad sporit de
complexitate, devenind din ce n ce mai specializate.
Se pot defini clase derivate care au la baz mai multe clase, nglobnd
proprietile tuturor claselor de baz, procedeu ce poart denumirea de
motenire multipl.


Declararea clasei derivate

Dup cum s-a vzut n lucrrile de laborator anterioare, sintaxa general
de declarare a unui tip de date class este de forma:

class <nume_clasa> <: lista_clase_baza> {<lista_membri>}
<lista_variabile>;

lista_clase_baza cuprinde unul (n cazul unei derivri simple) sau mai
multe (n cazul unei derivri multiple) nume de clase, nsoite de cte un
specificator de acces. Specificatorul de acces controleaz drepturile de acces la
membrii clasei de baz. Deci lista claselor de baz are sintaxa urmtoare:
Programare C++ - Lucrarea nr. 8

- 133 -

specificator_acces clasa_baza_1, specificator acces clasa_baza_2,

Specificatorii de acces ce se pot utiliza sunt public i private. Valoarea
implicit este private.
Atributele de acces la membrii motenii de clasa derivat, n funcie de
specificatorul de acces folosit, sunt cele prezentate n Tabelul nr. 8.

Tabelul nr. 8 Atributele de acces la membrii motenii
Atributul din
clasa de baz
Modificatorul
de acces
Accesul motenit
de clasa derivat
Accesul din
exterior
private inaccesibil inaccesibil
protected private inaccesibil
public

private
private inaccesibil
private inaccesibil inaccesibil
protected protected inaccesibil
public

public
public accesibil


Pentru a avea acces la membrii clasei de baz din clasa derivat, este
necesar ca acetia s fi fost declarai protected sau public.
Pentru a pstra dreptul de acces la membrii clasei de baz, este necesar s
se foloseasc acces public.
La stabilirea specificatorilor de acces se au n vedere perspectivele de
dezvoltare a ierarhiei de clase, fr a se nclca principiul ncapsulrii datelor.
Motenirea este asemntoare cu procesul de includere a obiectelor n
obiecte (procedeu ce poart denumirea de compunere), dar exist cteva
elemente caracteristice motenirii:
- codul poate fi comun mai multor clase;
- clasele pot fi extinse, fr a recompila clasele originare;
- funciile ce utilizeaz obiecte din clasa de baz pot utiliza i obiecte
din clasele derivate din aceast clas.

#include <iostream.h>

class punct
{
float x, y;
public :
punct(float a=0, float b=0)
{x=a; y=b;}
Programare C++ - Lucrarea nr. 8

- 134 -
void modific_punct(float dx, float dy)
{x+=dx; y+=dy;}
void afisare()
{cout<<"\nPunct: x="<<x<<"\ty="<<y;}
};

class cerc
{
punct centru;
float raza;
public:
cerc(float r=0)
{raza=r;}
void afisare()
{centru.afisare(); // afiarea valorilor corespunztoare centrului, x i
// respectiv y, se face prin funcia membr clasei punt;
// nu este permis acces direct deoarece membrii x i y
// sunt private
cout<<"\tRaza="<<raza; }
void modific_cerc(float dx, float dy, float dr)
{centru.modific_punct(dx, dy); // modificarea valorilor corespunztoare
// centrului se face prin funcia membr clasei
// punt; nu este permis acces direct deoarece
// membrii x i y sunt private
raza+=dr; }
};

void main()
{
cerc c1;
c1.modific_cerc(1.1, 2.2, 3.3);
c1.afisare();
}

Clasa cerc include un membru de tip punct, centru. Accesul la membrii
privai ai clasei punct se face doar prin funciile membre cu acces public ale
clasei punct. Pentru a avea acces direct la toi membrii, ar trebui declarai toi cu
acces public, ceea ce nu mai permite controlul strict asupra membrilor clasei
punct, deci este nclcat principiul ncapsulrii.

n exemplul urmtor se va deriva clasa cerc din clasa punct.

#include <iostream.h>

class punct
{
Programare C++ - Lucrarea nr. 8

- 135 -
protected: // se folosete specificatorul de acces protected pentru a permite
// accesul la datele membre ale clasei punct din clasele derivate,
// dar asigurndu-se n continuare protecia fa de funciile
// externe acestora
float x, y;
public :
punct(float a=0, float b=0)
{x=a; y=b;}
void modific_punct(float dx, float dy)
{x+=dx; y+=dy;}
};

class cerc : public punct // n declaraie se specific clasa de baz punct cu acces public
{
float raza;
public:
cerc(float r=0)
{raza=r;}
void afisare()
{cout<<"\nCentru cerc: x="<<x<<"\ty="<<y; // accesul la membrii clasei punct se face
// direct datorit specificatorului
// protected din clasa punct i a
// specificatorului public ataat clasei de
// baz
cout<<"\tRaza="<<raza;
}
void modific_cerc(float dx, float dy, float dr)
{ x+=dx; // accesul la membrii clasei punct se face direct
y+=dy;
raza+=dr;}
};

void main()
{
cerc c1;
c1.modific_cerc(1.1, 2.2, 3.3);
c1.afisare();
c1.modific_punct(2.5, 4.6); // accesul la funciile membre clasei punct prin
// obiecte de tip cerc este permis
c1.afisare();
}

Clasa derivat preia toate proprietile clasei de baz (membrii x, y,
funciile punct() i modific_punct()), la acestea adugnd membrul raza, funcia
cerc(), funcia modific_cerc() i funcia afisare().
Este permis atribuirea obiectelor derivate unor obiecte ale clasei de baz,
deci n funcia main() a exemplului anterior se poate include secvena:
Programare C++ - Lucrarea nr. 8

- 136 -

punct p1; // se creeaz un obiect punct cu x=0 i y=0
p1=c1; // p1.x preia valoarea c1.x, iar p1.y preia valoarea c1.y

Prin operaia de atribuire se preiau numai membrii clasei de baz. Regula
de compatibilitate se poate extinde i la pointeri i referine de obiecte.

punct * p_p;
cerc * p_c;

p_p=p_c;


Constructori i destructori pentru clasa derivat

La crearea unui obiect se apeleaz ntotdeauna constructorul clasei
respective, iar declaraia obiectului trebuie s specifice valorile iniiale cerute de
constructor. Distrugerea obiectului este precedat de apelul funciei destructor a
clasei respective.
Aceste reguli sunt valabile i n cazul claselor derivate. Pentru crearea
unui obiect al unei clase derivate, se creeaz iniial un obiect al clasei de baz
prin apelul constructorului acesteia, apoi se adaug elementele specifice clasei
derivate prin apelul constructorului clasei derivate. Declaraia obiectului derivat
trebuie s conin valorile de iniializare, att pentru elementele specifice, ct i
pentru obiectul clasei de baz. Deci la definirea constructorului clasei derivate,
este necesar s se specifice care sunt parametrii ce sunt preluai de constructorul
clasei de baz. Aceast specificare se ataeaz la antetul funciei constructor a
clasei derivate.

#include <iostream.h>

class punct
{
protected:
float x, y;
public :
punct(float a, float b)
{
x=a; y=b;
cout<<"\nConstructor punct: this="<<this;
cout<<"\tx="<<x<<"\ty="<<y;
}

Programare C++ - Lucrarea nr. 8

- 137 -
~punct()
{
cout<<"\nDestructor punct: this="<<this;
cout<<"\tx="<<x<<"\ty="<<y<<endl;
}
};

class cerc:public punct // se declar clasa cerc derivat din clasa punct
{
float raza;
public:
cerc(float a, float b, float r):punct(a, b) // n antetul constructorului se specific efectiv
// care sunt parametrii transferai constructorului
// punct
{
raza=r;
cout<<"\nConstructor cerc: this="<<this;
cout<<"\traza="<<r<<endl;
}
~cerc()
{
cout<<"\nDestructor cerc: this="<<this;
cout<<"\traza="<<raza;
}
};

void main()
{
cerc c1(1.1, 2.2, 10), c2(5, 10, 15);
}

n urma execuiei programului, se va afia:

Constructor punct: this=0xffea x=1.1 y=2.2
Constructor cerc: this=0xffea raza=10

Constructor punct: this=0xffde x=5 y=10
Constructor cerc: this=0xffde raza=15

Destructor cerc: this=0xffde raza=15
Destructor punct: this=0xffde x=5 y=10

Destructor cerc: this=0xffea raza=10
Destructor punct: this=0xffea x=1.1 y=2.2

La definirea constructorului clasei cerc, s-a specificat care dintre
parametrii transmii sunt preluai de constructorul clasei punct.
Programare C++ - Lucrarea nr. 8

- 138 -
S-a inclus afiarea de mesaje, astfel nct s se poat urmri ordinea n
care se fac apelurile funciilor.
Se observ c, la crearea unui obiect cerc, se apeleaz nti constructorul
punct i apoi constructorul cerc. De asemenea, se observ c adresa obiectului
este unic, ea fiind asociat tuturor elementelor care alctuiesc un obiect cerc,
deci ambii constructori, att punct(), ct i cerc() vor afia aceeai valoare.
La eliminarea obiectelor cerc, apelul destructorilor se face n ordine
invers, deci nti se apeleaz destructorul ~cerc() i apoi ~punct().

n situaia n care clasele de baz au definit constructor implicit sau
constructor cu parametri implicii, nu se impune specificarea parametrilor care
se transfer ctre obiectul clasei de baz. Astfel se explic corectitudinea
exemplului prezentat n paragraful anterior.


Constructorul de copiere pentru o clas derivat

n ceea ce privete utilizarea constructorului de copiere pentru o clas
derivat, se pot distinge mai multe situaii.
Dac ambele clase, att clasa derivat ct i clasa de baz, nu au definit
constructor de copiere, se apeleaz constructorul implicit creat de compilator.
Copierea se face membru cu membru.

#include <iostream.h>

class punct
{
protected:
float x, y;
public :
punct(float a, float b)
{
x=a; y=b;
cout<<"\nConstructor punct:";
cout<<"\tx="<<x<<"\ty="<<y;
}

~punct()
{
cout<<"\nDestructor punct: ";
cout<<"\tx="<<x<<"\ty="<<y<<endl;
}
};
Programare C++ - Lucrarea nr. 8

- 139 -

class cerc:public punct
{
float raza;
public:
cerc(float a, float b, float r):punct(a, b)
{
raza=r;
cout<<"\nConstructor cerc: ";
cout<<"\traza="<<r<<endl;
}
~cerc()
{
cout<<"\nDestructor cerc: ";
cout<<"\traza="<<raza;
}
};

void main()
{
cerc c1(1.1, 2.2, 10);
cerc c2(c1);
}

Programul afieaz:

Constructor punct: x=1.1 y=2.2
Constructor cerc: raza=10

Destructor cerc: raza=10
Destructor punct: x=1.1 y=2.2

Destructor cerc: raza=10
Destructor punct: x=1.1 y=2.2

Crearea obiectului c2 s-a fcut prin apelul constructorului de copiere
generat de compilator, ca urmare nu se afieaz nici un mesaj corespunztor
apelului constructorului, n schimb se afieaz mesajele corespunztoare
eliminrii ambelor obiecte, c1 i c2.

n cazul n care clasa de baz are constructorul de copiere definit, dar
clasa derivat nu, pentru clasa derivat compilatorul creeaz un constructor
implicit care apeleaz constructorul de copiere al clasei de baz.
Pentru clasa punct din exemplul anterior se include definirea
constructorului de copiere:
Programare C++ - Lucrarea nr. 8

- 140 -

punct(punct &p)
{
x=p.x;
y=p.y;
cout<<"\nConstructor de copiere punct:";
cout<<"\tx="<<x<<"\ty="<<y;
}

La execuia programului, se va afia:

Constructor punct: x=1.1 y=2.2
Constructor cerc: raza=10

Constructor de copiere punct: x=1.1 y=2.2

Destructor cerc: raza=10
Destructor punct: x=1.1 y=2.2

Destructor cerc: raza=10
Destructor punct: x=1.1 y=2.2

Se observ afiarea mesajului corespunztor apelului constructorului de
copiere pentru clasa punct. Acesta a fost apelat de constructorul de copiere
generat de compilator pentru clasa cerc.

n cazul n care se definete constructor de copiere pentru clasa derivat,
acestuia i revine n totalitate sarcina transferrii valorilor corespunztoare
membrilor ce aparin clasei de baz. Deci, pentru clasa cerc se include
constructorul de copiere cu prototipul:

cerc(cerc &);

iar antetul este de forma:

cerc::cerc(cerc &c) : punct(c.x, c.y)

cerc::cerc(cerc &c) : punct(c.x, c.y)
{
raza=c.raza;
cout<<\nConstructor de copiere cerc:
cout<<\traza=<<raza;
}

Programare C++ - Lucrarea nr. 8

- 141 -
La execuia programului, se afieaz:

Constructor punct: x=1.1 y=2.2
Constructor cerc: raza=10

Constructor punct: x=1.1 y=2.2
Constructor de copiere cerc: raza=10

Destructor cerc: raza=10
Destructor punct: x=1.1 y=2.2

Destructor cerc: raza=10
Destructor punct: x=1.1 y=2.2

Se poate observa c, dei este definit constructor de copiere pentru clasa
de baz, pentru crearea obiectului punct se apeleaz constructorul cu parametri,
nu cel de copiere.
Lista ce specific parametrii ce se transmit ctre obiectul clasei de baz
poate lipsi n situaia n care pentru clasa de baz este definit constructor implicit
sau constructor cu parametri implicii.


Redefinirea funciilor membre

Clasa derivat are acces la toi membrii cu acces protected sau public ai
clasei de baz. Astfel, dac se definete pentru clasa punct o funcie membr de
afiare:

punct::afisare()
{
cout<<\nPunct: x=<<x<< , y=<<y;
}

i se definete funcia main():

void main()
{
cerc c(1.1, 2.2, 10);
c.afisare();
}

rezultatul execuiei acestei secvene este afiarea:

Programare C++ - Lucrarea nr. 8

- 142 -
Constructor punct: x=1.1 y=2.2
Constructor cerc: raza=10

Punct: x=1.1 y=2.2

Destructor cerc: raza=10
Destructor punct: x=1.1 y=2.2

Funcia de afiare membr a clasei punct este motenit de clasa cerc, dar,
avnd n vedere faptul c o funcie de afiare pentru clasa cerc ar trebui s ofere
mai mult informaie, este necesar definirea unei noi funcii de afiare, membr
a clasei cerc.
Este permis supradefinirea funciilor membre clasei de baz cu funcii
membre ale clasei derivate.
Clasa cerc se completeaz cu funcia membr: void afisare().

void cerc::afisare()
{
cout<<\nCerc:;
cout<<\ncentru: x=<<x<<\ty=<<y;
cout<<\nraza: <<raza;
}

Apelul funciei :

c.afisare();

are ca efect afiarea:

Cerc:
centru: x=1.1 y=2.2
raza= 10

Noile definiii din clasa derivat nu substituie definiiile din clasa de baz,
ci se adaug acestora.


Compatibilitatea ntre o clas derivat i clasa de baz. Conversii
de tip

Deoarece clasa derivat motenete proprietile clasei de baz, ntre tipul
clas derivat i tipul clas de baz se admite o anumit compatibilitate.
Programare C++ - Lucrarea nr. 8

- 143 -
Compatibilitatea este valabil numai pentru clase derivate cu acces public la
clasa de baz i numai n sensul de la clasa derivat spre cea de baz, nu i
invers.
Compatibilitatea se manifest sub forma unor conversii implicite de tip:
- dintr-un obiect derivat ntr-un obiect de baz;
- dintr-un pointer sau referin la un obiect din clasa derivat ntr-un
pointer sau referin la un obiect al clasei de baz.

De exemplu, constructorul de copiere al clasei cerc se poate rescrie sub
forma:

.
cerc::cerc(cerc & c): punct( c )
{
raza=c.raza;
afisare();
}
...
void main()
{
cerc c1(1.1, 2.2, 10);
cerc c2(c1);
}

Rezultatul execuiei programului este:

Constructor punct: x=1.1 y=2.2
Constructor cerc: raza=10

Constructor de copiere punct: x=1.1 y=2.2
Constructor de copiere cerc: raza=10

Destructor cerc: raza=10
Destructor punct: x=1.1 y=2.2

Destructor cerc: raza=10
Destructor punct: x=1.1 y=2.2

Prin definiia folosit pentru constructorul de copiere se determin apelul
constructorului de copiere al clasei punct n urma conversiei implicite referin
cerc -> referin punct.
Avnd n vedre acceptarea conversiilor implicite, este corect secvena:

void test(punct &r_p)
Programare C++ - Lucrarea nr. 8

- 144 -
{ r_p.afisare(); }

void main()
{
punct p(2.5, 7.1), *p_p;
cerc c(4.5, 3.2, 15), *p_c;
p=c; // conversie cerc->punct la atribuire
p.afisare();
punct p1(c); // conversie cerc->punct la apelul constructorului de copiere
p1.afisare();
test(c); // conversie referin cerc->referin punct la transferul parametrului
// prin referin
p_p=&c; // conversie pointer cerc -> pointer punct la atribuire
p_p->afisare();
}

Atribuirea p=c se face n urma conversiei implicite a obiectului de tip cerc
c, n tipul punct.
O atribuire de forma c=p nu este permis, ea fiind semnalat cu mesaj de
eroare.
Situaii similare apar la transferul parametrilor la apelul funciilor prin
valoare, referin (vezi exemplul) sau pointer la clas.
Nu sunt permise conversii inverse. De exemplu atribuirile:

p_c=&p; // conversie pointer punct->pointer cerc
c=p; // conversie punct->cerc
cerc &r_c=p; // conversie referina punct->referinta cerc

nu sunt permise, ele conducnd la erori la compilarea programului.
Dac ntr-o aplicaie, o conversie de la clasa de baz la clasa derivat este
necesar, ea se poate defini prin supradefinirea operatorului de atribuire sau a
operatorului cast.


Supradefinirea operatorilor n clasele derivate

Funciile operator membre ale clasei de baz sunt motenite de clasele
derivate i pot fi supradefinite n clasele derivate, ca toate celelalte funcii
membre.
O excepie o constituie operatorul de atribuire, care se supune
urmtoarelor reguli:
- Dac operatorul este supradefinit n clasa derivat, funcia operator=()
Programare C++ - Lucrarea nr. 8

- 145 -
preia sarcina efecturii atribuirilor pentru toi membrii, i pentru cei ai
clasei de baz, chiar dac este supradefinit operatorul n clasa de baz;
- Dac nu este supradefinit funcia operator=() n clasa derivat, dar
este definit n cea de baz, atribuirea pentru membrii clasei de baz se
face apelndu-se funcia operator=(), pentru ceilali membri fcndu-se
atribuirea membru cu membru;
- Dac nu este supradefinit operatorul de atribuire n nici una din clase,
atribuirea se face membru cu membru.

n exemplul urmtor se declar clasa segment care definete un segment
de dreapt prin coordonatele extremitilor sale i clasa derivat din aceasta,
dreptunghi care preia segmentul de dreapt ca i diagonal a sa.

#include <iostream.h>

class segment
{
protected:
int x1, y1, x2, y2;
public:
segment(int a1=0, int b1=0, int a2=0, int b2=0)
{ cout<<"\nConstructor segment";
if (a1<a2)
{ x1=a1; x2=a2; }
else
{ x1=a2; x2=a1; }
if (b1<b2)
{ y1=b1; y2=b2; }
else
{ y1=b2; y2=b1; }
}
segment operator=(segment s)
{ cout<<"\nApel operator=()pentru segment";
x1=s.x1, y1=s.y1; x2=s.x2; y2=s.y2;
return *this;
}
segment operator+(segment s)
{ cout<<"\nApel operator+() pentru segment";
segment aux;
aux.x1=x1+s.x1;
aux.y1=y1+s.y1;
aux.x2=x2+s.x2;
aux.y2=y2+s.y2;
return aux;
}
Programare C++ - Lucrarea nr. 8

- 146 -
void afisare()
{
cout<<"\nx1="<<x1<<" y1="<<y1<<" x2="<<x2<<" y2="<<y2;
}
};

class dreptunghi: public segment
{
public:
dreptunghi(segment s):segment(s)
{ cout<<"\nConstructor - conversie segment->dreptunghi"; }
dreptunghi(int a1=0, int b1=0, int a2=0, int b2=0):segment(a1,b1,a2,b2)
{ cout<<"\nConstructor dreptunghi";}
long aria()
{ return (x2-x1)*(y2-y1);}
void afisare()
{ segment::afisare(); // se apeleaz funcia afisare() a clasei de baz
cout<<"\naria="<<aria();
}
};

void main()
{
dreptunghi d1(1, 1, 3, 3), d2(5, 5, 8, 8), d3;
d3=d1;
d1.afisare();
d3=d1+d2;
d3.afisare();
}

n urma execuiei programului se afieaz:

Constructor segment // pentru fiecare din cele 3 obiecte dreptunghi se
Constructor dreptunghi // apeleaz constructorul segment i apoi constructorul
Constructor segment // dreptunghi
Constructor dreptunghi
Constructor segment
Constructor dreptunghi
Apel operator=() pentru segment // Pentru atribuirea d3=d1, n absena supradefinirii
// operator=() pentru clasa dreptunghi, compilatorul
// apeleaz operator=() definit pentru segment
x1=1 y1=1 x2=3 y2=3
aria=4
Apel operator+() pentru segment // Extinderea funcionrii operator+() definit
// pentru segment asupra obiectelor dreptunghi
Constructor segment // Rezultatul returnat de operator+() e preluat de un
// obiect temporar segment
Programare C++ - Lucrarea nr. 8

- 147 -
Constructor conversie segment->dreptunghi // Conversie necesar atribuirii
Apel operator=() pentru segment // Apel similar celui anterior al funciei
// operator=()
x1=6 y1=6 x2=11 y2=11
aria=25

Expresia d3=d1+d2 se evalueaz n urmtoarele etape:
- se apeleaz funcia operator+() definit pentru clasa segment, prin
extinderea funcionrii ei asupra clasei dreptunghi;
- n timpul execuiei funciei operator+() se creeaz obiectul de tip
segment local funciei, aux, care va fi returnat de ctre aceasta;
- se face conversia segment->dreptunghi definit prin constructor (este
necesar definirea conversiei deoarece este conversie clas_de_baza
->clasa_derivata, conversie ce nu poate fi implicit), generndu-se un
obiect temporar dreptunghi;
- compilatorul realizeaz atribuirea prin apelul implicit al funciei
operator=() definit pentru clasa segment.



Exerciiu:

S se defineasc clasa dreptunghi pentru referirea unei zone din ecran prin
poziia colurilor stnga-sus i dreapta-jos, cu specificarea culorii de afiare.
Prelund aceast clas drept clas de baz, s se defineasc clasa derivat
dreptunghi_cu_chenar prin care se specific caracterul folosit pentru trasarea
chenarului i culoarea acestuia. Afiarea se face n modul text.



- 148 -
Lucrarea nr. 9




Crearea unei ierarhii de clase

Clasele derivate pot fi utilizate, la rndul lor drept clase de baz pentru a
obine noi clase derivate, crendu-se astfel ierarhii de clase. Se pornete de la o
clas relativ simpl. La fiecare derivare se adaug noi proprieti obinndu-se
clase tot mai complexe. Fiecare clas derivat motenete proprietile tuturor
claselor utilizate n mod succesiv drept clase de baz.
n exemplul urmtor se creeaz o ierarhie clasa poz -> clasa punct -> clasa
caracter. Clasa caracter motenete proprietile celorlalte dou.

#include <iostream.h>
#include <conio.h>

class poz // declaraia clasei poz
{
protected:
int x, y;
public:
poz(int abs=0, int ord=0)
{ cout<<"\nconstructor poz: ";
x=abs; y=ord;
cout<<"x="<<x<<"\ty="<<y ;
}
~poz()
{ cout<<"\ndestructor poz:";
cout<<"x="<<x<<"\ty="<<y ;
}
void af()
{cout<<"\npoz: x="<<x<<" y="<<y;}
void depl(int dx, int dy)
{x+=dx; y+=dy; }
};

class punct:public poz // declaraia clasei punct, derivat din clasa poz
{
protected:
Programare C++ - Lucrarea nr. 9

- 149 -
int vizibil; //vizibil=0-punct invizibil
int culoare;
public:
punct(int abs, int ord, int cul) : poz(abs, ord)
{ cout<<"\nconstructor punct: ";
vizibil=1;
culoare=cul;
cout<<"vizibil="<<vizibil<<"\tculoare="<<cul;
}
~punct()
{ cout<<"\ndestructor punct";
cout<<"vizibil="<<vizibil<<"\tculoare="<<culoare;
}
void cul(int c)
{ culoare=c; }
void viz(int v)
{ vizibil=v; }
void afisare()
{ if (vizibil)
{ textcolor(culoare);
gotoxy(x, y);
cprintf("*");
}
}
};

class caracter:public punct // declaraia clasei caracter, derivat din clasa
{ // punct; ea va moteni att membrii clasei punct,
char c; // ct i ai clasei poz
public:
caracter(int abs, int ord, int cul, char ch) : punct(abs, ord, cul)
{ cout<<"\nconstructor caracter: ";
c=ch;
cout<<"caracter="<<c;
}
~caracter()
{ cout<<"\ndestructor caracter: ";
cout<<"caracter="<<c;
}
void afisare()
{ if (vizibil)
{ textcolor(culoare);
gotoxy(x, y);
cprintf("%c", c);
}
}
};

Programare C++ - Lucrarea nr. 9

- 150 -
void main()
{
clrscr();
poz p1(5, 5);
p1.af();
getch();
punct pct1(7, 7, 7);
pct1.af();
pct1.afisare();
pct1.cul(15);
pct1.depl(1, 1);
pct1.afisare();
getch();
caracter c1(12, 12, 15, '#');
c1.afisare();
getch();
}

La execuia programului se afieaz:

constructor poz: x=5 y=5 // se creeaz obiectul p1
poz: x=5 y=5
constructor poz: x=7 y=7 // se creeaz obiectul pct1, apelndu-se
// nti constructorul poz() i apoi punct ()
constructor punct: vizibil=1 culoare=7
poz: x=7 y=7
*
*
constructor poz: x=12 y=12 // se creeaz obiectul c1, apelndu-se
// nti constructorul poz(), apoi punct (),
constructor punct: vizibil=1 culoare=15 // apoi caracter()
constructor caracter: caracter = #
#
destructor caracter: caracter = # // se distruge obiectul c1, apelndu-se
destructor punct: vizibil=1 culoare=15 // nti destructorul ~poz(), apoi
destructor poz: x=12 y=12 // ~punct (), apoi ~caracter()

destructor punct: vizibil=1 culoare=15 // se distruge obiectul pct1, apelndu-se
destructor poz: x=8 y=8 // nti destructorul ~poz(), apoi ~punct ()

destructor poz: x=5 y=5 // se distruge obiectul p1, apelndu-se
// destructorul ~poz()

Clasa caracter este derivat din clasa punct, care la rndul su este
derivat din clasa poz. Ea motenete membrii x i y de la clasa poz, vizibil i
culoare de la clasa punct i adaug membrul caracter. Funcia af() a clasei poz
Programare C++ - Lucrarea nr. 9

- 151 -
este motenit, putnd fi apelat pentru un obiect caracter, n schimb funcia
afisare() definit n clasa punct este supradefinit n clasa caracter, pentru un
obiect caracter fiind apelat funcia definit n clasa caracter.



Motenirea multipl

Conceptul de motenire permite crearea de clase noi care motenesc
proprietile mai multor clase de baz. Motenirea multipl aduce mai mult
flexibilitate n construirea claselor, rezultatul fiind obinerea unor structuri de
clase complexe.
Sintaxa folosit pentru declararea unei clase derivate D este:

class D: specif_acces clasa_baza1, specif_acces clasa_baza2, {}

n lista claselor de baz, pentru fiecare clas de baz, se include
specificatorul de acces.
Principiile prezentate la derivarea simpl i la crearea ierarhiilor simple de
clase sunt valabile i n cazul derivrii multiple.
Prin clasa strpoz se vor crea obiecte ce conin un ir de caractere mpreun
cu poziia de afiare. Aceast clas se obine prin derivarea din clasele pozitie i
string, accesul ctre acestea fiind public.

#include <iostream.h>
#include <string.h>
#include <conio.h>

class pozitie // declarare clas pozitie
{
protected :
int x, y;
public:
pozitie(int=0, int=0);
pozitie(pozitie&);
~pozitie();
void afisare();
void deplasare(int,int);
};

pozitie::pozitie(int abs, int ord)
{
Programare C++ - Lucrarea nr. 9

- 152 -
x=abs; y=ord;
cout<<"\nConstructor - pozitie";
afisare();
}

pozitie::pozitie(pozitie &p)
{
x=p.x; y=p.y;
cout<<"\nConstructor copiere-pozitie";
afisare();
}

pozitie::~pozitie()
{
cout<<"\nDestructor - pozitie";
afisare();
}

void pozitie::afisare()
{
cout<<"\npozitie: x="<<x<<" y="<<y;
}

void pozitie::deplasare(int dx, int dy)
{
x+=dx; y+=dy;
}

class string // declarare clas string
{
protected:
int ncar; // lungimea sirului
char *str;
public:
string(int n=0)
{
ncar=n;str=new char[ncar+1];
str[0]='\0';
cout<<"Constructor 1- string" ;
afisare();
}
string(char * s)
{
ncar=strlen(s); str=new char[ncar+1];
strcpy(str,s);
cout<<"\nConstructor 2 - string";
afisare();
}
Programare C++ - Lucrarea nr. 9

- 153 -
string(string & s)
{
ncar=s.ncar;
str=new char[ncar+1];
strcpy(str, s.str);
cout<<"\nConstructor de copiere-string";
afisare();
}
~string()
{ cout<<"\nDestructor- string";
afisare();
delete str;
}
void afisare()
{
cout<<"\nstring:"<<str<<"\n";
}
string operator+(string &s)
{
string temp(ncar+s.ncar);
strcat(temp.str, str);
strcat(temp.str, s.str);
return temp;
}
};

class strpoz : public pozitie, public string // declararea clasei strpoz, derivat din
// pozitie i string cu acces public ctre
{ // acestea
char culoare;
public:
// n antetul constructorului se specific parametrii preluai de constructorii claselor de baz
strpoz(int abs, int ord, int n=0, char c='A') : pozitie(abs, ord), string(n)
{
culoare=c;
cout<<"\nConstructor 1 - strpoz";
afisare();
}
strpoz(int abs, int ord, char *s, char c='A'):pozitie(abs, ord), string(s)
{
culoare=c;
cout<<"\nConstructor 2 - strpoz";
afisare();
}
strpoz(strpoz &sp) : pozitie(sp), string(sp) // parametrul preluat de constructorii
// claselor de baz este chiar sp pentru
// care se face conversia implicit ctre
// clasa de baz respectiv
Programare C++ - Lucrarea nr. 9

- 154 -
{ culoare=sp.culoare;
cout<<"\nConstructor copiere - strpoz";
afisare();
}
~strpoz()
{
cout<<"\nDestructor -strpoz";
afisare();
}
void coloreaza(char c)
{ culoare=c; }
void afisare()
{
cout<<"\nstrpoz:"<<str;
cout<<"\nx="<<x<<" y="<<y;
cout<<"\nculoare:"<<culoare<<"\n";
}
strpoz operator+(strpoz &sp)
{
strpoz temp(x+sp.x, y+sp.y, ncar+sp.ncar);
strcat(temp.str, str);
strcat(temp.str, sp.str);
return temp;
}
};

void main()
{
strpoz sp1(5, 5, "TEXT");
strpoz sp2(sp1);
string s1("aaa"), s2("bbb");
string s3=s1+s2;
s3.afisare();
strpoz sp3=sp1+sp2;
sp3.afisare();
}


Programul afieaz:

Constructor pozitie // se creeaz obiectul sp1, apelndu-se, n ordine
pozitie: x=5 y=5 // constructorii pozitie(), string(), strpoz()
Constructor 2 - string
string : TEXT
Constructor 2 strpoz
strpoz: TEXT
x=5 y=5
Programare C++ - Lucrarea nr. 9

- 155 -
culoare: A

Constructor copiere pozitie // se creeaz obiectul sp2, copie a obiectului
pozitie: x=5 y=5 // sp1, apelndu-se, n ordine constructorii de copiere
Constructor copiere - string
string : TEXT
Constructor copiere strpoz
strpoz: TEXT
x=5 y=5
culoare: A

Constructor 2 string // se creeaz obiectul string s1
string: aaa

Constructor 2 string // se creeaz obiectul string s2
string: bbb

Constructor 1 string // se creeaz obiectul temp local funciei operator+()
string:

Constructor de copiere string // se creeaz obiectul s3, copie a obiectului returnat de
string: aaabbb // functia operator+()

Destructor string // se elimin obiectul temp la ieirea din funcia
string: aaabbb // operator+()

string: aaabbb // apel al funciei afisare() pentru obiectul string s3

Constructor copiere pozitie // se creeaz obiectul temp de tip strpoz , apelndu-se, n
pozitie: x=10 y=10 // ordine constructori pentru clasele pozitie,string, strpoz
Constructor 1 string
string:
Constructor 1 strpoz
strpoz:
x=10 y=10
culoare=A

Constructor copiere pozitie // se creeaz obiectul sp3, prin copierea obiectului
Pozitie: x=10 y=10 // returnat de funcia operator+()
Constructor copiere - string
string : TEXTTEXT
Constructor copiere - strpoz
strpoz : TEXTTEXT
x=10 y=10
culoare: A

Destructor - strpoz // se elimin obiectul temp la ieirea din funcia
strpoz : TEXTTEXT // operator+(), apelndu-se destructorii n ordinea:
Programare C++ - Lucrarea nr. 9

- 156 -
x=10 y=10 //~strpoz(), ~string(), ~pozitie()
culoare: A
Destructor string
string: TEXTTEXT
Destructor pozitie
pozitie: x=10 y=10

strpoz:TEXTTEXT // se afieaz obiectul sp3
x=10 y=10
culoare:A

Destructor - strpoz // se elimin obiectul sp3 la ieirea din funcia main(),
strpoz : TEXTTEXT // apelndu-se destructorii n ordinea: ~strpoz(),
x=10 y=10 // ~string(), ~pozitie()
culoare: A
Destructor string
string: TEXTTEXT
Destructor pozitie
pozitie: x=10 y=10

Destructor string // se elimin obiectul s3 la ieirea din funcia main(),
string:aaabbb // apelndu-se destructorul ~string()
Destructor string // se elimin obiectul s2 la ieirea din funcia main(),
apelndu-se
string:bbb // destructorul ~string()

Destructor string // se elimin obiectul s1 la ieirea din funcia main(),
string:aaa // apelndu-se destructorul ~string()

Destructor strpoz // se elimin obiectul sp2 la ieirea din funcia main(),
strpoz:TEXT // apelndu-se destructorii n ordinea: ~strpoz(),
x=5 y=5 // ~string(), ~pozitie()
culoare=A
Destructor string
string:TEXT
Destructor pozitie
pozitie: x=5 x=5

Destructor strpoz // se elimin obiectul sp2 la ieirea din funcia main(),
strpoz:TEXT // apelndu-se destructorii n ordinea: ~strpoz(),
x=5 y=5 // ~string(), ~pozitie()
culoare=A
Destructor string
string:TEXT
Destructor pozitie
pozitie: x=5 x=5

Programare C++ - Lucrarea nr. 9

- 157 -
n declaraia clasei strpoz se specific derivarea cu acces public din
clasele pozitie i string.
Definiia fiecrui constructor al clasei strpoz specific transferul datelor
ctre constructorii claselor de baz.
Constructorii i destructorii claselor de baz sunt apelai automat la
crearea, respectiv distrugerea obiectelor derivate. Ordinea apelrii
constructorilor este dat de ordinea din lista claselor de baz din definiia clasei
derivate, constructorul clasei derivate fiind apelat ultimul. Destructorii sunt
apelai n ordine invers.
Clasa derivat conine toi membrii claselor de baz, dar i poate accesa
doar pe cei declarai cu acces public sau protected.
Clasele derivate pot supradefini funcii existente n clasele de baz.
Membrii i funciile omonime pot fi accesate folosind operatorul de rezoluie.
De exemplu, pentru clasa strpoz se poate defini funcia membr de afiare astfel:

void strpoz::afisare()
{
cout<<\nStrpoz:
pozitie::afisare();
string::afisare();
cout<<\culoare:<<c<<endl;
}

iar n funcia main() se poate include linia de program:

sp3.pozitie::afisare();

Exerciiu:

S se defineasc o clas derivat produs care descrie un produs prin
denumire, cod, pre. Se vor folosi o clas de baz string pentru membrul
denumire i o clas care definete codul prin trei grupe de caractere. Se vor
realiza cele dou variante de derivare, i anume o ierarhie simpl de clase i,
respectiv, derivarea multipl.

- 158 -
Lucrarea nr. 10






Clase virtuale

n situaia unei derivri multiple, n lista claselor de baz se pot regsi
clase, derivate din aceeai clas de baz. Problema care apare este faptul c noua
clas obinut va conine membri duplicai. Acetia pot fi referii folosindu-se
operatorul de rezoluie ::.
De exemplu, se consider situaia:

class Baza { protected x ;};
class B1: public Baza {};
class B2 : public Baza {};
class D: public B1, public B2 {};

Clasa D conine doi membri x. Cel care provine din clasa B1 se poate
referi sub forma:
Baza::B1::x

iar cel care provine din clasa B2 sub forma:

Baza::B2::x

De obicei aceast duplicare nu este necesar. Pentru a prelua membrul
neduplicat, se declar clasa Baza virtual n declaraiile claselor B1 i B2.

class Baza { protected x ;};
class B1: public virtual Baza {};
class B2 : public virtual Baza {};
class D: public B1, public B2 {};

Rezultatul utilizrii cuvntului cheie virtual este faptul c n clasa
derivat D se va include un singur membru x. Declararea clasei Baza virtual nu
Programare C++ - Lucrarea nr. 9

- 159 -
are efect asupra clase Baza, ci numai asupra claselor derivate din aceasta.
La definirea constructorului clasei derivate este necesar s se precizeze
nemijlocit transferul de informaie pentru constructorul clasei Baza n vederea
crerii copiei unice a obiectului Baza. n aceast situaie, definirea
constructorului va avea sintaxa:

D(...): B1(...), B2(...), Baza(...) {}

ntr-o ierarhie de clase, constructorul clasei virtuale este ntotdeauna
apelat primul.
n exemplul urmtor se definete clasa poz ce descrie poziia unui punct
prin coordonatele sale, care va fi folosit ca i clas de baz pentru clasele cerc
i, respectiv ptrat. n final se declar clasa fig care reprezint o figur format
dintr-un cerc i un ptrat cu centru comun.

# include <iostream.h>

class poz // se declar clasa poz
{
protected:
int x, y;
public:
poz(int abs=0, int ord=0) // constructor cu parametri impliciti
{
cout<<"\nConstructor poz";
x=abs;
y=ord;
}
void afisare()
{
cout<<"\nx="<<x<<" y="<<y;
}
};

class cerc: public poz // se declar clasa cerc derivat din clasa de baz
{
protected:
float raza;
public:
cerc(int abs, int ord, float r): poz(abs, ord) // la definirea constructorului se specific
// parametrii preluai de constructorul
{ // clasei de baz
cout<<"\nConstructor cerc";
raza=r;
}
Programare C++ - Lucrarea nr. 9

- 160 -
void afisare()
{
cout<<"\nx="<<x<<" y="<<y;
cout<<"\nraza="<<raza;
}
};

class patrat: public poz // se declar clasa patrat derivat din clasa
// de baz poz
{
protected:
float latura;
public:
patrat(int abs, int ord, int l) : poz(abs, ord) // la definirea constructorului se specific
// parametrii preluai de constructorul
{ // clasei de baz
cout<<"\nConstructor patrat";
latura=l;
}
void afisare()
{
cout<<"\nx="<<x<<" y="<<y;
cout<<"\nlatura="<<latura;
}
};

class fig : public cerc, public patrat // se declar clasa fig derivat din cerc i
{ // patrat
public:
// la definirea constructorului se specific parametrii preluai de constructorii claselor de baz
fig(int abs, int ord, float dim) : cerc(abs, ord, dim/2.): patrat(abs, ord, dim)
{
cout<<"\nConstructor fig";
}
void afisare()
{
cout<<"\nx="<<cerc::x<<" y="<<cerc::y;
cout<<"\nx="<<patrat::x<<" y="<<patrat::y;
cout<<"\nraza="<<raza<<" latura="<<latura;
}
};

void main()
{
fig f(5, 10, 25);
f.afisare();
}
Programare C++ - Lucrarea nr. 9

- 161 -
La execuia programului se afieaz:

Constructor poz
Constructor cerc
Constructor poz
Constructor patrat
Constructor fig
x=5 y=10
x=5 y=10
raza=12.5 latura=25

Se observ c, la crearea obiectului f se creeaz nti un obiect cerc,
pentru care se apeleaz iniial constructorul poz() i apoi constructorul cerc(),
apoi un obiect patrat pentru care se apeleaz nti constructorul poz() i apoi
constructorul patrat(), i, n final se apeleaz constructorul fig. Pentru crearea
unui obiect fig se apeleaz de dou ori constructorul poz(), deci vor exista cte
doi membrii x i y. Pentru a fi distini n funciile membre clasei fig, aceti
membri trebuie referii prin numele clasei creia aparin. O definire a funciei de
afiare sub forma:

void fig::afisare()
{
cout<<"\nx="<<x<<" y="<<y;
.}

genereaz un mesaj de eroare, deoarece compilatorul nu tie la care membru x,
respectiv y, se face referire.
Dac, n momentul declarrii claselor cerc i patrat, clasa de baz este
nsoit de specificaia virtual, la crearea unui obiect fig constructorul poz se
apeleaz o singur dat, membrii x i y fiind unici. Declaraiile claselor cerc i
patrat vor fi:

class cerc : virtual public poz
{};
class patrat : virtual public poz
{};

Funcia afisare() a clasei fig poate fi definit astfel:

void fig::afisare()
{
cout<<"\nx="<<x<<" y="<<y;
cout<<"\nraza="<<raza<<" latura="<<latura;
}
Programare C++ - Lucrarea nr. 9

- 162 -

fr a genera erori.
La execuia programului se va afia:

Constructor poz
Constructor cerc
Constructor patrat
Constructor fig
x=5 y=10
raza=12.5 latura=25

Se observ c se apeleaz o singur dat constructorul clasei poz(), acesta
fiind primul constructor apelat.


Funcii virtuale

Aa cum s-a artat n lucrarea anterioar, ntre o clas derivat i clasa de
baz se admite o anumit compatibilitate n sensul conversiei de la clasa derivat
spre cea de baz, nu i invers. Conversiile implicite sunt acceptate att pentru
obiecte de tip clas derivat, ct i pentru referine sau pointeri ai acestora.
Presupunem c avem o clas de baz, B i clasele derivate D1, D2, D3,
, care redefinesc o metod M. Se pune problema modului n care compilatorul
identific corect care metod va fi apelat. n situaia utilizrii operatorului de
scop (un apel de forma B::M()) sau apelului cu ajutorul obiectului asupra cruia
se aplic metoda ( de exemplu o exprimare de forma: D1 d; d.M();), decizia este
simpl i este luat n faza de compilare. n acest caz este vorba despre legtura
static (n terminologia englez early binding). Exist ns situaii n care un
pointer la clasa de baz poate primi pe parcursul execuiei programului ca
valoare adrese de obiecte de tip clas derivat sau clas de baz, ceea ce
presupune luarea unei decizii n privina metodei care se apeleaz chiar n timpul
execuiei programului. Acest mod de lucru se numete legtur dinamic (late
binding). Funciile membre pentru care se realizeaz legtura dinamic se
numesc funcii virtuale i se declar cu ajutorul cuvntului cheie virtual.
Redefinirea cu acelai prototip n ntreaga ierarhie de clase a unei funcii
declarat virtual n clasa de baz, se supune legturii dinamice. Funciile
virtuale au urmtoarele proprieti:
- sunt funcii membre nestatice ale unei clase;
- redefinirea funciei virtuale se face cu respectarea prototipului;
- redefinirea funciei virtuale n clasele derivate nu este obligatorie;
- redefinirea unei funcii virtuale cu schimbarea prototipului, are ca efect
Programare C++ - Lucrarea nr. 9

- 163 -
supradefinirea funciei; funciilor supradefinite nu li se mai aplic
legtura dinamic;
- constructorii nu pot fi funcii virtuale, n schimb destructorii da;
- funciile inline nu pot fi virtuale.

Se declar ierarhia simpl de clase poz->punct->carcter (vezi lucrarea nr.
8).

#include <iostream.h>

class poz // declarare clas poz
{
protected: // se permite accesul claselor derivate ctre datele
// membre
int x, y;
public:
poz(int =0, int=0);
virtual void afisare(); // funcia afisare() se declar virtual
void deplasare(int, int);
};

class punct:public poz // declararea clasei derivate punct
{
protected: // se permite accesul claselor derivate ctre datele
// membre
int vizibil;
int culoare;
public:
punct(int=0, int=0, int=1);
void afisare(); // se redefinete funcia virtual afisare()
void deplasare(int, int);
};

class caracter:public punct // declararea clasei derivate caracter
{
char c;
public:
caracter(int, int, int, char);
void afisare(); // se redefinete funcia virtual afisare()
void deplasare(int, int);
};

poz::poz(int abs, int ord)
{
cout<<"\nConstructor pozitie:"<<this;
x=abs; y=ord;
Programare C++ - Lucrarea nr. 9

- 164 -
cout<<" x="<<x<<" y="<<y;
}
void poz::afisare()
{
cout<<"\nPozitie:"<<this;
cout<<" x="<<x<<" y="<<y<<endl;
}
void poz::deplasare(int dx, int dy)
{
x+=dx; y+=dy;
cout<<"\nDeplasare poz:";
afisare();
}

punct::punct(int abs, int ord, int cul):poz(abs, ord)
{
cout<<"\nConstructor punct:"<<this;
culoare=cul; vizibil=0;
cout<<" x="<<x<<" y="<<y<<" culoare="<<culoare<<" vizibil="<<vizibil;
}
void punct::afisare()
{
cout<<"\nPunct:"<<this;
cout<<" x="<<x<<" y="<<y<<" culoare="<<culoare<<" vizibil="<<vizibil<<endl;
}
void punct::deplasare(int dx, int dy)
{
x+=dx; y+=dy;
cout<<"\nDeplasare punct:";
afisare();
}

caracter::caracter(int abs, int ord, int cul, char ch):punct(abs, ord, cul)
{
cout<<"\nConstructor caracter:"<<this;
c=ch;
cout<<" x="<<x<<" y="<<y<<" culoare="<<culoare<<" vizibil="<<vizibil;
cout<<" caracter=:"<<c;
}
void caracter::afisare()
{
cout<<"\nCaracter:"<<this;
cout<<" x="<<x<<" y="<<y<<" culoare="<<culoare<<" vizibil="<<vizibil;
cout<<" caracter="<<c<<endl;
}
void caracter::deplasare(int dx, int dy)
{
x+=dx; y+=dy;
Programare C++ - Lucrarea nr. 9

- 165 -
cout<<"\nDeplasare caracter:";
afisare();
}

void main()
{
// se declar obiecte de tip clas
poz p1(1, 1);
punct p2(2, 2, 1);
caracter c1(3, 3, 3, '*');

// se declar pointeri la clase cu iniializare, ntre tipul pointerilor i tipul obiectului existnd
// coresponden
poz* p_poz=&p1;
punct * p_punct=&p2;
caracter * p_car=&c1;

// apeluri ale funciilor membre
p_poz->afisare();
p_poz->deplasare(2, 2);
p_punct->afisare();
p_punct->deplasare(3, 3);
p_car->afisare();
p_car->deplasare(5, 5);

p_poz=p_punct; // atribuire admis, prin conversia implicit punct*->poz*;
p_poz->afisare();
p_poz->deplasare(3, 3);

p_poz=p_car; // atribuire admis, prin conversia implicit caracter*->poz*;
p_poz->afisare();
p_poz->deplasare(3, 3);
}

Programul afieaz:

Constructor pozitie: 0xfff0 x=1 y=1
Constructor pozitie: 0xffe6 x=2 y=2
Constructor punct: 0xffe6 x=2 y=2 culoare=1 vizibil=0
Constructor pozitie: 0xffd8 x=3 y=3
Constructor punct: 0xffd8 x=3 y=3 culoare=1 vizibil=0
Constructor caracter:0xffd8 x=3 y=3 culoare=1 vizibil=0 caracter=*

Pozitie: 0xfff0 x=1 y=1

Deplasare poz:
Pozitie: 0xfff0 x=1 y=1
Programare C++ - Lucrarea nr. 9

- 166 -

Punct: 0xffe6 x=2 y=2 culoare=1 vizibil=0

Deplasare punct:
Punct: 0xffe6 x=3 y=3 culoare=1 vizibil=0

Caracter:0xffd8 x=3 y=3 culoare=1 vizibil=0 caracter=*

Deplasare caracter:
Caracter:0xffd8 x=8 y=8 culoare=1 vizibil=0 caracter=*

// Se poate observa c, dei p_poz este definit ca pointer la poz, el conine adresa unui obiect
// punct, deci, datorit faptului c funcia afisare() este virtual, se va apela definiia funciei
// corespunztoare clasei punct
Punct: 0xffe6 x=5 y=5 culoare=1 vizibil=0

// Funcia deplasare() nu este virtual, deci se va apela definiia funciei corespunztoare
// clasei poz, dar funcia afisare() apelat din interiorul acesteia este corect apelat
Deplasare poz:
Punct: 0xffe6 x=8 y=8 culoare=1 vizibil=0

// Se poate observa c, dei p_poz este definit ca pointer la poz, el conine adresa unui obiect
// caracter, deci, datorit faptului c funcia afisare() este virtual, se va apela definiia funciei
// corespunztoare clasei caracter
Caracter:0xffd8 x=8 y=8 culoare=3 vizibil=0 caracter=*

// Funcia deplasare() nu este virtual, deci se va apela definiia funciei corespunztoare
// clasei poz, dar funcia afisare() apelat din interiorul acesteia este corect apelat
Deplasare poz:
Caracter:0xffd8 x=11 y=11 culoare=3 vizibil=0 caracter=*


Din exemplul prezentat se observ c apelul funciei afisare() se face
corespunztor obiectului declarat, chiar dac pointerul este de tipul clasei de
baz, datorit faptului c funcia a fost declarat virtual. Funcia deplasare(), ne
fiind virtual, va fi apelat corespunztor tipului pointerului.
Dac se nlocuiete declaraia funciei deplasare() a clasei poz cu
declaraia:
virtual void deplasare(int, int);

se va observa n urma execuiei programului c i apelul acesteia se face
corespunztor tipului obiectului i nu tipului pointerului. n aceast situaie,
corespunztor secvenei

p_poz=p_punct;
Programare C++ - Lucrarea nr. 9

- 167 -
p_poz->afisare();
p_poz->deplasare(3, 3);

p_poz=p_car;
p_poz->afisare();
p_poz->deplasare(3, 3);

din funcia main(), se va afia:

Punct: 0xffe6 x=5 y=5 culoare=1 vizibil=0

Deplasare punct:
Punct: 0xffe6 x=8 y=8 culoare=1 vizibil=0

Caracter:0xffd8 x=8 y=8 culoare=3 vizibil=0 caracter=*

Deplasare caracter:
Caracter:0xffd8 x=11 y=11 culoare=3 vizibil=0 caracter=*

n clasele derivate, dac pentru declararea unei funcii declarat virtual n
clasa de baz se folosete un prototip diferit, funcia va fi supradefinit i se
pierde legtura dinamic. Acest lucru este acceptat de compilator, dar se afieaz
un mesaj de atenionare a faptului c noua declaraie ascunde existena funciei
virtuale.
De exemplu, n clasa caracter se va nlocui declaraia funciei deplasare cu
declaraia:
void deplasare();

cu definiia:

void caracter::deplasare()
{
x+=1; y+=1;
cout<<"\nDeplasare caracter:";
afisare();
}

n acest caz, apelul funciei se va face astfel:

p_car->deplasare(); // funcia este fr parametri


Pentru secvena:

Programare C++ - Lucrarea nr. 9

- 168 -
p_poz=p_car;
p_poz->afisare();
p_poz->deplasare(3, 3);

se va afia:

Caracter:0xffd8 x=8 y=8 culoare=3 vizibil=0 caracter=*

Deplasare punct:
Caracter:0xffd8 x=11 y=11 culoare=3 vizibil=0 caracter=*

Se poate observa c funcia selectat deplasare() apelat, datorit existenei
parametrilor din apel, este funcia motenit de la clasa punct.
Apelul :

p_poz->deplasare();

va fi sancionat cu mesaj de eroare, se cer parametri pentru funcia deplasare(),
deoarece pointerul la poz ncearc s acceseze funcia virtual.
Nu este obligatorie redefinirea funciilor virtuale n clasele derivate, astfel
c, dac eliminm declaraia funciei deplasare() din clasa caracter, se va apela
redefinirea din ultima clas ierarhic motenit, deci apelul:

p_poz->deplasare(3, 3);

va avea ca efect afiarea:

Deplasare punct:
Caracter:0xffd8 x=11 y=11 culoare=3 vizibil=0 caracter=*

Avnd n vedere c folosirea funciilor virtuale presupune un consum de
memorie suplimentar i operaii suplimentare care au ca urmare creterea
timpului de execuie, se recomand evitarea utilizrii nejustificate a funciilor
virtuale.


Exerciiu:

S se redefineasc clasele strpoz, pozitie, string declarate n lucrarea nr.
9, utiliznd funcii virtuale. S se urmreasc modul de selectare a acestora cnd
sunt apelate prin pointeri de tipuri diferite la clasele definite.

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