Documente Academic
Documente Profesional
Documente Cultură
Indrumar C++.Unlocked
Indrumar C++.Unlocked
ndrumar
C++
-3-
Cuprins
Lucrarea nr. 1
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 _asm cdecl default enum _fastcall huge _loadds _pascal return _ss typedef while Cuvinte cheie ale limbajului C++ asm auto break case char class const continue delete do double _ds _es _export extern _far float for friend goto if inline int _interrupt long _near near new pascal private protected public _saveregs _seg short signed static struct switch template union unsigned virtual void _cdecl _cs else far _huge interrupt operator register sizeof this volatile
-7-
char = signed char unsigned char int = signed int = short int = signed short int unsigned int = unsigned short int long int = signed long int unsigned long int
Tabelul nr. 3. Tipuri de date n virgul mobil Domeniu de valori float +/-(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 Tip Spaiu de memorie ocupat 32 bii
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); sizeof (unsigned char) sizeof (long double) // expresia returneaz valoarea 4 // expresia returneaz valoarea 1 // 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 23u 32768 77UL // tip int // tip unsigned int // tip long int // tip unsigned long int
Programare C++ - Lucrarea nr. 1 Constantele octale sunt valori avnd prima cifr 0. Cifrele 8 i 9 sunt ilegale.
-067 067u 020000000 055ul 089 // tip int // tip unsigned int // tip long int // tip unsigned long // 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 0X8000 0xffu 0x10000 0xFFul // tip int // tip unsigned int // tip unsigned int // tip long int // 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 11.22E5 -.33e-2 .5 1. 1.f 0.L // valoare 2,1 (tip double) // valoare 11,22 x 105 (tip double) // valoare 0,33 x 10-2 (tip double) // valoare 0,5 (tip double) // valoare 1 (tip double) // valoare 1 (tip float) // 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 \a \b \f \n \r \t \v \\ \ \ \? \o \xH Caracter alarm (bell) BS FF LF CR TAB VT \ ? orice caracter orice caracter Descriere sun alarma backspace formfeed linefeed carrige return tab orizontal tab vertical backslash apostrof ghilimele semnul ntrebrii ir de cifre octale ir de cifre hexazecimale
// se afieaz caracterul ? // se afieaz caracterul ? , 63 fiind valoarea // corespunztoare n codul ASCII // se afieaz caracterul ? // 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 \t\t // reprezentarea n memorie ca ntreg de valoare 25185 (0x62 0x61) // 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
Pentru scrierea irurilor lungi se poate utiliza simbolul (\) care semnaleaz continuarea irului pe rndul urmtor:
Exemplu de sir \ scris pe doua randuri
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; unsigned int n; // declararea variabilei r de tip pointer la float, // 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
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; float r=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
// se declar variabila automatic cu numele c creia i se rezerv un // octet n segmentul de stiv, variabila fiind o variabil local // 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
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.
void main (void) { for (int i=5; i<8; i++) {} for {i=0; i<5; i++) {} }
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; double s; ... }
// declararea unei variabile automatice cu declarare explicit // 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; return (a++); } void main(void) { int n;
Programul afieaz:
Prima atribuire : n= 3 A doua atribuire : n= 4 // la primul apel al funciei, variabila a are valoarea // iniial 2, ea fiind apoi incrementat // 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; void main(void) { scanf(%d, &m); printf(\nm= %#x, m); // se declar legtur extern pentru variabila 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; a++; a+=5; // declaraia variabilei a const // eroare, a este const // 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; int * p2=&var3; const int * p3=&var3; var3++;
// declaraie cu iniializare // eroare, nu se poate converti const int * la int * // corect, tipul de date corespund // eroare, se ncearc modificarea unui obiect 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; octet var; // tipul unsigned char este echivalent cu octet // variabila var se declar de tip octet
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;
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; int * p=&n; int &r=n; n=20; *p=25; r=30; // se declar n de tip ntreg // se declar pointerul p cu iniializare adresa lui n // se definete r referina lui n // n primete valoarea 20 // n primete valoarea 25 // 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 ; int &r=20 ; const int i = 20; int &r = i ; // eroare, se declar o referin fr iniializare // corect, se declar o referin la o constant
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
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]; float *p; p=mat; p=(float*)mat; mat = = &mat[0][0]; mat+1 = = &mat[1][0]; *(mat+9) = = mat[9][0]; // mat reprezint un tablou de pointeri float // p este un pointer float // eroare, tipurile pointerilor difer // corect, s-a folosit o conversie explicit de tip // expresie adevrat // expresie adevrat // expresie adevrat
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 { unsigned int zi; unsigned int luna; unsigned int an; }; struct persoana { char nume[15]; char prenume[15]; data data_n; } pers1, pers2; // se declar tipul de date data, ca structur cu 3 membri de tip // unsigned int
// se declar tipul de date persoana ca structur cu 2 // membri ir de caractere i un membru de tip data
// 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; puts(\nIntroduceti numele:); // se declar un obiect pointer la persoana
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; p2=p1; // p1 se declar cu iniializare // 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>
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; unsigned int luna:4; unsigned int an:11; } DATE;
// se declar membrul zi cu reprezentare pe 5 bii // se declar membrul luna cu reprezentare pe 4 bii // se declar membrul an cu reprezentare pe 11 bii
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
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; unsigned int b1:1; unsigned int b2:1; unsigned int b3:1; unsigned int b4:1; unsigned int b5:1; unsigned int b6:1; unsigned int b7:1; // se declar o structur care ocup un octet de memorie, // cu acces separat, prin membrii si, la fiecare bit n // parte
};
// se declar o uniune ce ocup un octet de memorie care poate // fi accesat ca i char prin membrul val, sau ca octet prin // membrul bit
Programul afieaz:
0x16 se reprezinta in binar 00010110
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';
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 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; cout<<sir<<\n; cout<<*sir<<\n; cout<<&sir<<\n; cout<<(void*)sir<<\n; cin>>*sir; cout<<sir<<\n; cin>>sir; cout<<sir<<\n; char * p_sir=abc; cout<<p_sir<<\n; cout<<*p_sir<<\n; cout<<&p_sir<<\n; cout<<(void*)p_sir<<\n;
// se declar un ir de caractere cu iniializare // se afieaz coninutul sirului de caractere sir // se afieaz primul caracter din irul de caractere sir // se afieaz adresa la care se afl variabila pointer sir // se afieaz adresa la care se afl variabila pointer sir // se citete un alt caracter pentru prima poziie a irului // se afieaz coninutul irului de caractere sir // se citete o nou valoare pentru irul de caractere // se afieaz coninutul irului de caractere sir // declar un pointer la tipul char ce se iniializeaz cu // adresa irului constant abc // se afieaz coninutul irului de caractere ctre care // pointeaz p_sir // se afieaz primul caracter din irul constant de // caractere // se afieaz adresa la care se afl variabila pointer // p_sir // se afieaz adresa coninut de variabila pointer p_sir,
Programul afieaz:
Sir de caractere S 0xffe0 0xffe0 s sir de caractere Alt sir de caractere Alt sir de caractere abc a 0xfff4 0x00be
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);}
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++; ..............
Lucrarea nr. 2
&& | | %= += ^= 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. - 37 -
Tabelul nr. 6 Prioritatea i ordinea de evaluare a operatorilor Clasa de prioritate 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Operatori () [] - :: . ! ~ + - ++ -- & * (tip) sizeof new delete .* ->* * / % + << >> < <= > >= = = != & ^ | && || ?: (operator condiional) = *= /= %= += -= &= ^= |= <<= >>= , (operator virgul) Asociativitate de la stnga la dreapta de la dreapta la stnga de la stnga la dreapta de la stnga la dreapta de la stnga la dreapta de la stnga la dreapta de la stnga la dreapta de la stnga la dreapta de la stnga la dreapta de la stnga la dreapta de la stnga la dreapta de la stnga la dreapta de la stnga la dreapta de la dreapta la stnga de la dreapta la stnga 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. - 38 -
Observaie: mprirea ntregilor produce un ntreg prin trunchierea prii fracionare a rezultatului.
int a=2, b=3, c; c= 2*(a/b+1); c=a%b; c=b%a;
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 j = i-- ; // i=6, j=6; // i=5, j=6;
- 39 -
Operatori asupra biilor Aceti operatori nu se pot aplica tipurilor float i double. ~ & | ^ << >>
int a=7, b=2, c; ~ a; a&b; a|b; a^b; a<<b; a>>b;
complement fa de 1 I bit cu bit SAU bit cu bit SAU EXCLUSIV bit cu bit deplasare la stnga deplasare la dreapta
// expresia returneaz valoarea -8 // expresia returneaz valoarea 2 // expresia returneaz valoarea 7 // expresia returneaz valoarea 5 // expresia returneaz valoarea 28 // 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; x= x*(y+1); a=a>>b; echivalent cu echivalent cu echivalent cu i+=2; x*=y+1; a>>=b;
- 40 -
Programare C++ - Lucrarea nr. 2 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
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 - 41 -
Programare C++ - Lucrarea nr. 2 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; p2=new int(15); p3=new int[n]; p4=new int[2][n]; // variabil ntreag neiniializat // variabil ntreag iniializat cu valoarea 15 // tablou unidimensional int de dimensiune 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. - 42 -
Programare C++ - Lucrarea nr. 2 Utilizarea altei valori este ilegal, putnd determina o comportare a programului nedefinit.
int *p; p=new int(10); delete p; int *p,*q; p=new int(7); q=p; delete q; *p=17; // se aloc spaiul de memorie necesar unui int i se face // iniializarea cu valoarea 10 // se elimin variabila p din memorie
// se aloc spaiul de memorie necesar unui int i se face // iniializarea cu valoarea 7 // variabila pointer q primete ca valoare adresa coninut n // variabila p // se elibereaz zona de memorie de la adresa coninut n // variabila q // incorect, folosete o zona de memorie care a fost deja // dealocat
// incorect, se cere dealocarea unei zone de memorie care nu a // fost alocat cu operatorul new
// se declar variabila local sir // se afieaz irul global // se afieaz irul local
- 43 -
// se declar variabila local funciei main() cu iniializare // se apeleaz funcia f() cu argumentul efectiv variabila local // funciei main(), a // se afieaz variabila global a
Conversia explicit de tip se poate utiliza n orice expresie utilizndu-se o construcie numit typecast n construcii de forma:
- 44 -
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;
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 - 45 -
Programare C++ - Lucrarea nr. 2 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; i=7; { int i; int j; i=9; j=2*i; printf(\ni=%d, i); } printf(\ni=%d, i); printf(\nj=%d, j); }
// se declar i // nceputul blocului // se redefinete i // se declar j // se afieaz i=9 // sfritul blocului // se afieaz i=7 // eroare, instruciunea se afl n afara domeniului de // existen a lui j
Instruciunile compuse se utilizeaz instruciunile de ciclare i cele condiionale. Instruciunea condiional if, if-else Sintaxa instruciunii poate fi:
deseori
combinaie
cu
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.
- 46 -
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);
- 47 -
Programare C++ - Lucrarea nr. 2 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.
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.
- 48 -
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. - 49 -
Programare C++ - Lucrarea nr. 2 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; }
- 50 -
Programare C++ - Lucrarea nr. 2 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; .........
- 51 -
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: - 52 -
Programare C++ - Lucrarea nr. 3 - 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.
Programare C++ - Lucrarea nr. 3 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; }
- 54 -
Programare C++ - Lucrarea nr. 3 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 &); variabile int void main() { int i, j; float p, q; scanf(%d%d, &i, &j); schimba(i, j); // lista de parametri este format din referine de
// 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
- 55 -
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.
Programare C++ - Lucrarea nr. 3 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); void main() { int (*p_f)(int, float) int i=5, j; float r=3.5; p_f=fct; j=p_f(i, r); cout<<j<<endl; } int fct(int a, float b) { cout<<a<<endl; cout<<b<<endl; return ( a + (int)b ); } // prototip de funcie
// p_f, pointer de funcie // se atribuie adresa funciei fct pointerului p_f // se apeleaz funcia fct prin pointerul p_f
- 57 -
Programare C++ - Lucrarea nr. 3 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*)); int comp1(char *, char *); int comp2(char *, char *); void main() { char s1[80], s2[80]; int (*p)(char*, char*); p=comp1; puts("Introduceti primul sir"); gets(s1); puts("Introduceti al doilea sir"); gets(s2); test(s1, s2, p); getch(); p=comp2; test(s1, s2, p); 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) { return(s1[0]-s2[0]); } int comp1(char *s1, char *s2) { int i=0, j=0; while( s1[i] ) i++; while( s2[j] ) j++; // funcia compar primele caractere ale irurilor // funcia compar lungimile irurilor // funcia test() are un parametru de tip // pointer de funcie
// se declar pointerul p de funcii cu prototipul : // int functie (char *, char *) // variabilei pointer p i se atribuie ca valoare // adresa funciei comp1()
// compar irurile folosind funcia comp1() // variabilei pointer p i se atribuie ca valoare // adresa funciei comp2() // compar irurile folosind funcia comp2()
- 58 -
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 - 59 -
Programare C++ - Lucrarea nr. 3 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); void f2(int , double = 7.5); void f3(int, long = 111, double = 3.5); void main() { float v = 1.5; f3( ); f3( 1 ); f3( 2, v); f3( 3, 99); f3( 4, 99, 7.8); } void f2(int i, double q = 7.5) { cout<<\ni=<<i; cout<<\tq=<<q; } void f3(int i, long j, double r) { cout<<\ni=<<i; cout<<\tj=<<j; cout<<\tr=<<r; } // definiie corect // eroare, se repet declararea valorii parametrului // implicit double // eroare , se cere valoare implicit pentru // parametrul float // prototip corect // prototip corect
// apel incorect, lipsete parametru efectiv pentru parametrul int // care nu are valoare implicit // apel corect // apel corect, se realizeaz conversia implicit, cu trunchiere, // float ->long // apel corect // apel corect
Programul afieaz:
i=1 i=2 i=3 i=4 j=111 j=1 j=99 j=99 r=3.5 r=3.5 r=3.5 r=7.8
- 60 -
- 61 -
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 ); void g( float, int ); void main() { int i = 5, j = 20; double r = 2.5; char c = a; g( i, r ); g( r, c ); g( i, j ); } void g(int a, float b) { cout<<\nApel functia 1 cu argumentele: cout<<a=<<a<<\tb=<<b; } void g(float a, int b) { // funcia 1 // funcia 2
// apel funcia 1- pentru i corespondena este exact (etapa 1), // iar pentru r este necesar conversie nedegradant (etapa 3) // apel funcia 2 cu conversie degradant double -> float pentru r // (etapa 3) i o conversie nedegradant char ->int pentru i // (etapa 2) // eroare la compilare datorat ambiguitii
- 62 -
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 ); double aduna(double , double ); char *aduna(char *, char *); void main() { int i = aduna(42, 17); double d = aduna(42.5, 17.9); char * s1 = "C++ "; char * s2 = "is the best!"; char * s = aduna(s1,s2); 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 );} // 1 // 2 // 3
- 63 -
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 ); j=fct( i+1 ); }
- 64 -
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 ); }
- 65 -
// definiie corect
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);
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; }
6. Redefinii funcia de criptare a unui ir prezentat n lucrarea nr. 1, folosind parametri variabile referin.
r.
- 67 -
Lucrarea nr. 4
Programare C++ - Lucrarea nr. 4 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 - 69 -
Programare C++ - Lucrarea nr. 4 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: int x, y; public: void init(int initx=0, int inity=0) {x = initx; y = inity; } int getx(){ return x; } // se specific accesul private la membrii clasei // date membre ale clasei // se specific acces public la membrii clasei // funcie de iniializare, funcie membr cu // parametri implicii // funcie inline, returneaz valoarea membrului x
- 70 -
void Punct::move(int dx, int dy) { x+=dx; y+=dy; } void main() { Punct Punct1; int x1, y1;
cout<<"\n Introduceti coordonata x= "; cin>>x1; cout<<" Introduceti coordonata y= "; cin>>y1; Punct1.init(x1, y1); cout<<"\n x este = "<<Punct1.getx(); cout<<"\n y este = "<< Punct1.gety(); Punct1.move(10, 20); cout<<"\n x este = "<<Punct1.getx(); cout<<"\n y este = "<< Punct1.gety(); Punct Punct2; Punct2.init(); Punct2.afisare(); Punct *p_Punct3; p_Punct3 = &Punct2; p_Punct3 -> move ( 5, 12); p_Punct3 -> afisare(); }
// iniializarea obiectului Punct1 cu valorile x1, // respectiv y1 // afiarea coordonatei x // afiarea coordonatei y // modificarea coordonatelor obiectului Punct1 // afiarea coordonatei x // afiarea coordonatei y // se declar un obiect de tip Punct, Punct2 // membrii x i y preiau valorile implicite ale // parametrilor, deci x=y=0 // afiarea caracteristicilor obiectului Punct2 // se declar un obiect pointer la Punct // apelul funciilor membre se face folosind // operatorul de selecie ->
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. - 71 -
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;
- 72 -
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.
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 - 74 -
Programare C++ - Lucrarea nr. 4 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.
// destructor
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
- 75 -
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() { cout<<\nConstructor implicit\n; x=0; y=0; } Punct(int initx, int inity) { cout<<\nConstructor cu parametri\n; x=initx; y=inity; } ~Punct() { cout<<\nDestructor Punct\n; } int getx(){ return x; } int gety(){ return y; } void move(int dx, int dy);
// constructor implicit
// constructor cu parametri
// destructor
- 76 -
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, - 77 -
Programare C++ - Lucrarea nr. 4 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); P2.afisare(); // se genereaz un constructor de copiere implicit care va face // atribuirile P2.x=P1.x, P2.y=P1.y
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 : - 78 -
#include <iostream.h> #define N 10 class tablou { float tab[N]; public: tablou(); void citire(); void afisare(); void modif(int, float); }; tablou::tablou() { for (int i=0; i<N; i++) tab[i]=0; } void tablou::citire() { for(int i=0; i<N; i++) { cout<<"\ntab["<<i<<"]="; cin>>tab[i]; } } void tablou::afisare() { cout<<"\nadresa="<<this; for(int i=0; i<N; i++) cout<<"\ntab["<<i<<"]="<<tab[i]; } void tablou::modif(int i, float el) { tab[i]=el; } // funcie membr prin care se modific valoarea unui // element al lui tab // constructor implicit - iniializeaz elementele tabloului // cu valoarea 0
// funcie membr prin care se citesc de la tastatur // valorile elementelor din tab
// se declar obiectul de tip tablou t1 // se creeaz obiectul de tip tablou t2 prin copierea lui t1; se // apeleaz constructorul de copiere generat de compilator
- 79 -
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; float *tab; public: tablou(); tablou(int); tablou(tablou &); ~tablou(); void citire(); void afisare(); void modif(unsigned int, float); }; tablou::tablou() { 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) { 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) { 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]; } // dimensiunea tabloului tab // adresa tabloului tab // constructor implicit // constructor cu parametru // constructor de copiere // destructor
- 80 -
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 - 81 -
Programare C++ - Lucrarea nr. 4 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. - 82 -
Lucrarea nr. 5
- 83 -
- 84 -
// p0 se creeaz prin apelul constructorului cu parametri // cu valorile implicite, p1 preia valorile x=1, y=1 // rezultatul funciei test(), p, este atribuit membru cu // membru lui p0
- 85 -
Programul afieaz:
Apel main() Constructor 0xfff2 x=0 y=0 Constructor 0xffee x=1 y=1 Constructor copiere 0xffe6 x=1 y=1 Apel test() Constructor copiere 0xffea x=11 y=11 Destructor 0xffe6 x=11 y=11 Destructor 0xffea x=11 y=11 x=11 y=11 Final main() Destructor 0xffee x=1 y=1 Destructor 0xfff2 x=11 y=11 // se creeaz p0 // se creeaz p1 // se creeaz parametrul ca obiect temporar echivalent // cu apelul p(p1) // se creeaz obiectul temporar care transfer rezultatul // se elimin p // se elimin obiectul care transfer rezultatul
// se elimin p1 // se elimin p0
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) { cout<<"\nApel test()"; p.deplasare(10, 10); return p; } // parametrul formal p de definete ca referin u unui punct
// rezultatul se returneaz printr-un obiect temporar creat prin // apelul constructorului de copiere
- 86 -
// se elimin p1 // se elimin p0
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.
- 87 -
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); }
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 - 88 -
Programare C++ - Lucrarea nr. 5 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(); pozitie(int abs, int ord); void deplasare(int abs, int ord); void afisare(); }; class text_poz { char *txt; pozitie orig; public: text_poz(); void citire_sir(); void afisare(); ... };
// constructor implicit la crearea unui obiect text_poz // se va apela nti constructorul implicit pentru membrul // pozitie i apoi cel al clasei text_poz
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) { x=abs; y=ord; cout<<"\nConstructor pozitie "; afisare(); } ~pozitie() { cout<<"\nDestructor pozitie"; }
//constructor cu parametri
// destructor
- 89 -
// 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();
- 90 -
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(); }
- 91 -
// 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]; for (i=0; i<5; i++) { p_poz[i].deplasare(i+2, i+2); p_poz[i].afisare(); }; // tablou alocat dinamic se apeleaz constructorul fr // parametri de 5 ori
- 92 -
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 ;
#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; int pozitie::*p_x, pozitie::*p_y;
// se declar un obiect pozitie i un pointer la pozitie // 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
- 94 -
Programare C++ - Lucrarea nr. 5 n cazul pointerilor la funcii membre nu este obligatorie utilizarea operatorului &, deci declaraiile: p_func=&pozitie::deplasare; i p_func=pozitie::deplasare; sunt echivalente.
// declarare membru static, care se va folosi ca un contor pentru // obiectele din clasa poziie create; definirea se face n // exteriorul clasei
- 95 -
// 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; void main() { pozitie::nr_obj(); pozitie::st; pozitie p1(1,1), p2(2,2), *p3; p1.nr_obj(); p3=new pozitie(3,3); pozitie::nr_obj(); delete p3; } // definirea membrului static st; implicit este iniializat // cu valoarea 0 definirea cu iniializare este : // int pozitie::st=0 // apel funcie static prin numele clasei // eroare, membrul st este private // apel funcie static prin numele unui obiect poziie // apel funcie static prin numele clasei
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;
- 96 -
// eroare, funcia func() este extern clasei // 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;
- 97 -
- 98 -
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; class B; class A { int a; public: A(int n) {a=n;} friend void func(A &, B &); }; class B { int b; public: B(int n) {b=n;} friend void func(A &, B &); }; void func( A &ob_a, B &ob_b) { // declaraie incomplet a clasei // declaraie incomplet a clasei
// funcia func() este declarat prieten clasei B //definirea funciei func(), extern claselor A i B
- 99 -
- 100 -
Programare C++ - Lucrarea nr. 5 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; void normalizare();
// membru static ce reprezint un contor pentru obiectele // clasei create // 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. - 101 -
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.
- 102 -
Programare C++ - Lucrarea nr. 6 Tabelul nr. 7 Operatorii care pot fi supradefinii Clasa de prioritate 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Tip Operatori Asociativitate de la stnga la dreapta de la dreapta la stnga de la stnga la dreapta de la stnga la dreapta de la stnga la dreapta de la stnga la dreapta de la stnga la dreapta de la stnga la dreapta de la stnga la dreapta de la stnga la dreapta de la stnga la dreapta de la stnga la dreapta de la stnga la dreapta de la dreapta la stnga de la stnga la dreapta
Binar () [] -> Unar ! ~ + - ++ -- & * (tip) new delete Binar ->* Binar * / % Binar + Binar << >> Binar < <= > >= Binar = = != Binar & Binar ^ Binar | Binar && Binar || Binar = *= /= %= += -= &= ^= |= <<= >>= Binar , (operator virgul)
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.
- 103 -
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();
- 104 -
Programul va afia:
re=1 re=3 re=4 re=8 im=2 im=4 im=6 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.
- 105 -
Programul va afia:
re=1 re=3 re=4 re=8 im=2 im=4 im=6 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: - 106 -
// supradefinire operator ++
// supradefinire operator --
- 107 -
// expresie echivalent cu r=p.operator++(); // expresie echivalent cu r=p.operator(); - diferena // dintre pre i post fixarea operatorului nu mai este // valabil
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
- 108 -
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: "<<∁ 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(); }
- 109 -
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();
- 110 -
- 111 -
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
- 112 -
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; }
Programare C++ - Lucrarea nr. 6 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[1]= 2.3 comp[2]= 5.1 // comp[0] i-a modificat valoarea prin atribuirea v2[0]=99.9
- 114 -
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]; public: void citire_date(void);
// membrul lista este un tablou de N elemente de // tip persoana // citirea datelor de la tastatur pentru cele N // persoane din lista
- 115 -
// se declar un obiect exemplu_index // se citesc datele persoanelor din lista // se caut n lista elementul cu numele=Adrian // se caut n lista elementul cu varsta=22;
- 116 -
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).
Programare C++ - Lucrarea nr. 6 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; public: vector(int gr=10); ~vector(); void afisare(); void * operator new(size_t dim); void operator delete(void *); }; 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) { cnt++; // supradefinirea funciei operator new() // la crearea unui obiect dinamic de tip // vector, contorul se incrementeaz // constructor cu parametru implicit // destructor // prototip funcie operator new() // prototip funcie operator delete()
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()
- 118 -
// se declar pointeri la tipul vector // se aloc dinamic memorie pentru un obiect vector; // operator new apeleaz constructorul cu parametru // implicit, valoarea parametrului fiind 5 // se aloc dinamic memorie pentru un obiect vector, // copie a celui de la adresa depus n pv1, ca urmare se // apeleaz constructorul de copiere // alocarea de memorie se face cu operatorul new // predefinit
// se elimin obiectul pv1, apelndu-se operatorul delete // definit de utilizator // se elimin obiectul pv1, apelndu-se operatorul delete // definit de utilizator // 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 .
- 119 -
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(); public: rational(int a=0, b=1); ~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.
- 120 -
Lucrarea nr. 7
Programare C++ - Lucrarea nr. 7 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 dintrun tip fundamental ntr-un tip clas; - supradefinirea constructorului nu permite conversia unui tip clas ntr-un tip fundamental.
- 122 -
- 123 -
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.
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; }
- 125 -
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. - 126 -
- 127 -
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
- 128 -
- 129 -
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 - 130 -
Programare C++ - Lucrarea nr. 7 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.
- 131 -
Lucrarea nr. 8
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 private protected public private protected public Modificatorul de acces private public Accesul motenit de clasa derivat inaccesibil private private inaccesibil protected public Accesul din exterior inaccesibil inaccesibil inaccesibil inaccesibil inaccesibil 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;}
- 133 -
// 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 {
- 134 -
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); c1.afisare(); }
// accesul la funciile membre clasei punct prin // obiecte de tip cerc este permis
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: - 135 -
// se creeaz un obiect punct cu x=0 i y=0 // 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;
- 136 -
// 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); }
La definirea constructorului clasei cerc, s-a specificat care dintre parametrii transmii sunt preluai de constructorul clasei punct. - 137 -
Programare C++ - Lucrarea nr. 8 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.
- 138 -
Programul afieaz:
Constructor punct: Constructor cerc: Destructor cerc: Destructor punct: Destructor cerc: Destructor punct: x=1.1 y=2.2 raza=10 raza=10 x=1.1 y=2.2 raza=10 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: - 139 -
Constructor de copiere punct: x=1.1 y=2.2 Destructor cerc: Destructor punct: Destructor cerc: Destructor punct: raza=10 x=1.1 y=2.2 raza=10 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; }
- 140 -
Constructor punct: x=1.1 y=2.2 Constructor de copiere cerc: raza=10 Destructor cerc: Destructor punct: Destructor cerc: Destructor punct: raza=10 x=1.1 y=2.2 raza=10 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.
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();
Noile definiii din clasa derivat nu substituie definiiile din clasa de baz, ci se adaug acestora.
Programare C++ - Lucrarea nr. 8 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); }
Constructor de copiere punct: x=1.1 y=2.2 Constructor de copiere cerc: raza=10 Destructor cerc: Destructor punct: Destructor cerc: Destructor punct: raza=10 x=1.1 y=2.2 raza=10 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)
- 143 -
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; c=p; cerc &r_c=p; // conversie pointer punct->pointer cerc // conversie punct->cerc // 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.
Programare C++ - Lucrarea nr. 8 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; }
- 145 -
// Pentru atribuirea d3=d1, n absena supradefinirii // operator=() pentru clasa dreptunghi, compilatorul // apeleaz operator=() definit pentru segment // Extinderea funcionrii operator+() definit // pentru segment asupra obiectelor dreptunghi // Rezultatul returnat de operator+() e preluat de un // obiect temporar segment
- 146 -
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.
- 147 -
Lucrarea nr. 9
- 148 -
- 149 -
vizibil=1 culoare=7
x=12 y=12
constructor punct: vizibil=1 culoare=15 constructor caracter: caracter = # # destructor caracter: caracter = # destructor punct: vizibil=1 culoare=15 destructor poz: x=12 y=12 destructor punct: destructor poz: destructor poz: vizibil=1 culoare=15 x=8 y=8 x=5 y=5
// se creeaz obiectul c1, apelndu-se // nti constructorul poz(), apoi punct (), // apoi caracter() // se distruge obiectul c1, apelndu-se // nti destructorul ~poz(), apoi // ~punct (), apoi ~caracter() // se distruge obiectul pct1, apelndu-se // nti destructorul ~poz(), apoi ~punct () // 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 - 150 -
Programare C++ - Lucrarea nr. 9 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 { 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) { // declarare clas pozitie
- 151 -
- 152 -
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
- 153 -
Programul afieaz:
Constructor pozitie pozitie: x=5 y=5 Constructor 2 - string string : TEXT Constructor 2 strpoz strpoz: TEXT x=5 y=5 // se creeaz obiectul sp1, apelndu-se, n ordine // constructorii pozitie(), string(), strpoz()
- 154 -
// se creeaz obiectul string s1 // se creeaz obiectul string s2 // se creeaz obiectul temp local funciei operator+() // se creeaz obiectul s3, copie a obiectului returnat de // functia operator+() // se elimin obiectul temp la ieirea din funcia // operator+() // apel al funciei afisare() pentru obiectul string s3 // se creeaz obiectul temp de tip strpoz , apelndu-se, n // ordine constructori pentru clasele pozitie,string, strpoz
// se elimin obiectul temp la ieirea din funcia // operator+(), apelndu-se destructorii n ordinea:
- 155 -
// se elimin obiectul sp3 la ieirea din funcia main(), // apelndu-se destructorii n ordinea: ~strpoz(), // ~string(), ~pozitie()
// se elimin obiectul s3 la ieirea din funcia main(), // apelndu-se destructorul ~string() // se elimin obiectul s2 la ieirea din funcia main(), // destructorul ~string() // se elimin obiectul s1 la ieirea din funcia main(), // apelndu-se destructorul ~string() // se elimin obiectul sp2 la ieirea din funcia main(), // apelndu-se destructorii n ordinea: ~strpoz(), // ~string(), ~pozitie()
// se elimin obiectul sp2 la ieirea din funcia main(), // apelndu-se destructorii n ordinea: ~strpoz(), // ~string(), ~pozitie()
- 156 -
Programare C++ - Lucrarea nr. 9 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; }
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.
- 157 -
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 - 158 -
Programare C++ - Lucrarea nr. 9 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 { protected: int x, y; public: poz(int abs=0, int ord=0) { cout<<"\nConstructor poz"; x=abs; y=ord; } void afisare() { cout<<"\nx="<<x<<" y="<<y; } }; // se declar clasa poz
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; }
- 159 -
- 160 -
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 {};
- 161 -
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 - 162 -
Programare C++ - Lucrarea nr. 9 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 { protected: int x, y; public: poz(int =0, int=0); virtual void afisare(); void deplasare(int, int); }; class punct:public poz { protected: int vizibil; int culoare; public: punct(int=0, int=0, int=1); void afisare(); void deplasare(int, int); }; class caracter:public punct { char c; public: caracter(int, int, int, char); void afisare(); void deplasare(int, int); }; // declarare clas poz // se permite accesul claselor derivate ctre datele // membre
// declararea clasei derivate punct // se permite accesul claselor derivate ctre datele // membre
- 163 -
- 164 -
Programul afieaz:
Constructor pozitie: 0xfff0 Constructor pozitie: 0xffe6 Constructor punct: 0xffe6 Constructor pozitie: 0xffd8 Constructor punct: 0xffd8 Constructor caracter:0xffd8 Pozitie: 0xfff0 Deplasare poz: Pozitie: 0xfff0 x=1 x=1 x=1 x=2 x=2 x=3 x=3 x=3 y=1 y=1 y=1 y=2 y=2 y=3 y=3 y=3
- 165 -
// 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;
- 166 -
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(); }
Pentru secvena:
- 167 -
se va afia:
Caracter:0xffd8 Deplasare punct: Caracter:0xffd8 x=8 y=8 culoare=3 vizibil=0 caracter=* 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);
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.
- 168 -