POO - c2 Functii ClaseSiObiecte SupraincarcareaOperatorilor
-
Upload
ciurescu-mihaela -
Category
Documents
-
view
229 -
download
0
description
Transcript of POO - c2 Functii ClaseSiObiecte SupraincarcareaOperatorilor
11/28/2014
1
Funcții Forma generală de definire a unei funcţii este:
tip_returnat nume_func(tip1 arg1,…,tipn argn)
{
//corpul functiei
}
Funcţia cu numele nume_func returnează o valoare de tip tip_returnat şi are un număr n de argumente formale declarate ca tip şi nume în lista de argumente formale.
Argumentele formale din declaraţia unei funcţii se mai numesc şi parametrii funcţiei.
Dacă o funcţie nu are argumente, atunci lista din parantezele rotunde este vidă.
Funcții O funcţie este un nume global, dacă nu este declarată de tip
static.
O funcţie declarată static are domeniul de vizibilitate restrâns la fişierul în care a fost definită.
Corpul funcţiei nu poate fi accesat din afara acesteia.
Corpul unei funcţii este o instrucţiune compusă, adică o succesiune de instrucţiuni şi declaraţii incluse între acolade.
În corpul funcţiei se pot defini variabile, care sunt locale şi se memorează în segmentul de stivă al programului.
Dacă nu sunt declarate static, variabilele locale se creează la fiecare apel al funcţiei şi se distrug atunci când este părăsit blocul în care au fost definite.
Funcții Nu se pot defini funcţii în interiorul unei funcţii.
Argumentele formale ale unei funcţii sunt considerate variabile locale ale funcţiei, ca orice altă variabilă definită în funcţia respectivă.
Dacă o funcţie nu are de returnat nici o valoare, atunci tip_returnat din declaraţia funcţiei este tipul void şi nu este necesară o instrucţiune de returnare (return) în funcţie.
În toate celelalte cazuri, în corpul funcţiei trebuie să fie prevăzută returnarea unei variabile de tipul tip_returnat, folosind instrucţiunea return.
Dacă în definiţie nu este prevăzut un tip_returnat, se consideră implicit returnarea unei valori de tip întreg.
Funcții #include <iostream>
using namespace std;
int addition (int a, int b)
{
int r;
r = a+b;
return (r);
}
int main ()
{
int z;
z = addition (5,3);
cout << "The result is " << z;
return 0;
}
int addition (int a, int b)
8
z = addition ( 5 , 3 );
int addition (int a, int b)
z = addition ( 5 , 3 );
11/28/2014
2
Funcții #include <iostream>
using namespace std;
int addition (int a, int b)
{
int r;
r=a+b;
return (r);
}
int main ()
{
int x=5,y=3,z;
z = addition (x,y);
cout << "The result is " << z;
return 0;
}
Funcții Prototipurile funcţiilor. Pentru apelul unei funcţii este
necesară cunoaşterea definiţiei sau a prototipului acesteia. Prototipul unei funcţii este de forma: tip_returnat nume_func(tip1 arg1,……., tipn argn);
Numele argumentelor formale sunt opţionale în prototipul unei funcţii.
Prototipurile permit compilatorului să verifice tipurile argumentelor de apel şi să semnaleze eroare la conversii ilegale.
Spre deosebire de limbajul C, unde este admisă şi simpla declaraţie a numelui funcţiei (fără tipurile argumentelor de apel), utilizarea prototipurilor este obligatorie în C++.
Funcții
Prototipurile funcţiilor. double f2(int, double); // prototip functie f2
double f3(int a, double f) // definție functie f3
{
double t = f/a;
return t;
}
Argumentele funcțiilor
La apelul unei funcţii, argumentele de apel (argumente
reale sau efective) iniţializează argumentele formale
din declaraţia funcţiei, în ordinea din declaraţie.
Argumentele unei funcţii se pot transfera în două
moduri:
apelul prin valoare
apelul prin referinţă.
11/28/2014
3
Argumentele funcțiilor
Apelul prin valoare - se copiază valoarea
argumentului real în argumentul formal corespunzător
al funcţiei.
În acest caz, modificările efectuate asupra
argumentului formal (al funcţiei) nu modifică
argumentul real.
int x=5, y=3, z;
z = addition ( x , y );
int addition (int a, int b)
z = addition ( 5 , 3 );
Argumentele funcțiilor
Apelul prin referinţă - este accesată direct variabila
din argumentul real transmis funcţiei, care poate fi
deci modificată.
De exemplu - o funcţie swap() care realizează schimbul
între valorile a două variabile.
Dacă nu se folosesc referinţe, argumentele de apel ale
funcţiei trebuie să fie pointeri la variabilele respective.
Pointerii, ca argumente de apel, nu vor fi modificaţi, dar
variabilele indicate de aceştia pot fi modificate.
Argumentele funcțiilor
Funcţia swap() cu
argumente pointeri
void swap(int* x, int* y)
{
int t;
t = *x;// dereferentiere
*x = *y;
*y = t;
}
Funcţia swap() cu
argumente de tip referinţă
void swap(int& x, int& y)
{
int t;
t = x;
x = y;
y = t;
}
Argumentele funcțiilor Implementările sunt simetrice. Referinţa foloseşte adresa
variabilei pentru a o putea modifica.
În cazul referinţelor, pointerul şi defererenţierea sunt
ascunse, programatorul nu trebuie să le prevadă explicit,
programul rezultat este mai concis şi mai clar.
Referinţele sunt deosebit de utile în apelul funcţiilor ale
căror argumente sunt obiecte de dimensiuni mari şi
copierea lor în argumentele formale (plasate în segmentul
de stivă al programului) ar fi foarte ineficientă.
11/28/2014
4
Argumentele funcțiilor Argumente implicite ale funcţiilor.
Se întâmplă frecvent ca o funcţie să aibă un număr mai mare de argumente decât sunt necesare în cazurile simple dar frecvente de apel.
Dacă nu este necesar să fie transmisă întotdeauna valoarea reală a unui argument şi acesta poate lua, de cele mai multe ori, o valoare implicită, atunci în declaraţia funcţiei se prevede o expresie de iniţializare a acestui argument, iar din apel poate să lipsească valoarea argumentului corespunzător.
De exemplu, o funcţie pentru stabilirea datei calendaristice, care prevede valori implicite pentru argumentele luna şi an:
Argumentele funcțiilor struct data{
int zi;
int luna;
int an;
} g_data;
void setdata(int zi, int luna=9, int an=1999){
g_data.zi = zi;
g_data.luna = luna;
g_data.an = an;
}
void main(){
setdata(15); // 15 9 1999
setdata(21,7); // 21 7 1999
setdata(20,1,2000); // 21 1 2000
}
Argumentele funcțiilor
Argumente implicite ale funcţiilor.
Numai argumentele de la sfârşitul listei pot fi
argumente implicite. De exemplu, este eronată
următoarea declaraţie: void setdata(int zi, int luna=9, int an); // eroare!
Pointeri la funcții
Dat fiind că o funcţie are o localizare în memorie,
această valoare poate fi atribuită unui pointer.
Adresa unei funcţiei este punctul de intrare în funcţie
şi, de aceea, funcţia poate fi apelată folosind un
pointer.
Tipul unui pointer la o funcţie se defineşte folosind
tipul valorii returnate şi tipurile argumentelor formale
ale funcţiei.
Înainte de apelul unei funcţii prin pointer, trebuie să fie
asignată valoarea pointerului folosind numele funcţiei.
11/28/2014
5
Pointeri la funcții float func1(int x, float y){
return x + y;
}
float func2(int x, float y){
return x * y;
}
void main(){
float (*pf)(int, float); // definire tip pointer
pf = func1; // asignare valoare pointer
float z = (*pf)(3, 1.2); // apel functie
cout << z << endl; // afiseaza 4.2
pf = func2; // asignare valoare pointer
z = (*pf)(3, 1.2); // apel functie
cout << z << endl; // afiseaza 3.6
}
Pointeri la funcții - exemplu int addition (int a, int b)
{ return (a+b); }
int subtraction (int a, int b)
{ return (a-b); }
int (*minus)(int,int) = subtraction;
int operation (int x, int y, int (*functocall)(int,int)){
int g;
g = (*functocall)(x,y);
return (g);
}
int main (){
int m,n;
m = operation (7, 5, addition);
n = operation (20, m, minus);
cout << n;
return 0;
}
Supraîncărcarea funcțiilor
În limbajul C, fiecare funcţie definită în program
trebuie să aibă un nume diferit.
În C++, mai multe funcţii pot avea acelaşi nume în
acelaşi domeniu de definiţie, dacă se pot diferenţia prin
numărul sau tipul argumentelor de apel.
Acest mecanism se numeşte supraîncărcarea funcţiilor
(function overloading).
În traducere, se mai întrebuinţează şi denumirile de
suprapunere, redefinire, supradefinire.
Supraîncărcarea funcțiilor
Dacă în acelaşi domeniu sunt definite mai multe
funcţii cu acelaşi nume, la fiecare apel se selectează
funcţia corectă prin compararea tipurilor argumentelor
reale de apel cu tipurile argumentelor formale ale
funcţiei.
double abs(double);
int abs(int);
abs(1); // apeleaza abs(int)
abs(1.0); // apeleaza abs(double)
11/28/2014
6
Supraîncărcarea funcțiilor Acceptarea mai multor versiuni ale unei funcţii cu
acelaşi nume este condiţionată de posibilitatea selectării fără ambiguitate a uneia dintre acestea după tipul sau numărul argumentelor.
Nu este admis ca funcţiile să difere doar prin tipul returnat.
Două funcţii declarate cu acelaşi nume se referă la aceeaşi funcţie dacă sunt în acelaşi domeniu şi au număr şi tipuri identice de argumente.
Supraîncărcarea funcțiilor
O funcţie declarată local nu este în acelaşi domeniu cu
o funcţie cu domeniul la nivel de fişier.
int f(char*);
void g()
{
extern f(int);
f(“abcd”); // eroare, f(int) ascunde f(char*)
//deci nu exista f(char*) în acest domeniu
}
Supraîncărcarea funcțiilor
Selecţia funcţiei la apel se bazează pe “cea mai bună
potrivire a argumentelor” (best argument matching).
Dacă printre funcţiile supraîncărcate există o funcţie
care are toate argumentele de tipuri identice cu
argumentele din apel, atunci această funcţie prezintă
cea mai bună potrivire a argumentelor şi este selectată.
Supraîncărcarea funcțiilor
Se consideră de exemplu funcţia supraîncărcată de calcul
a ridicării la o putere a unui număr, funcţia power():
int power(int, int);
long power(long, int);
double power(double, int);
double power(double, double);
power(7, 8); // selecteaza power(int, int)
power(5.6, 2); // selecteaza power(double, int)
power(6.7, 2.5);// selecteaza power(double, double)
11/28/2014
7
Supraîncărcarea funcțiilor
În aceste apeluri s-a selectat versiunea funcţiei power()
pe baza coincidenţei dintre argumentele de apel şi
argumentele formale.
În cazul în care nu există o astfel de coincidenţă, se fac
conversii ale argumentelor efective (de apel) către tipul
argumentelor formale.
Compilatorul încearcă cea mai bună potrivire pentru
fiecare argument în parte prin conversii pentru tipuri
predefinite care să nu conducă la trunchieri (pierderi de informaţie).
Supraîncărcarea funcțiilor De exemplu:
long a = 100;
int b = 10;
float c = 2.3f;
float d = 5.6f;
double e = 7.0;
double f = 2;
power(a,b); // selecteaza power(long, int)
power((int)a, b); // selecteaza power(int, int)
power(c,a); // selecteaza power(double, int)
power(c,d); // selecteaza power(double, double)
power(e,d); // selecteaza power(double, double)
Supraîncărcarea funcțiilor
Dacă prin astfel de conversii nu se poate stabili cea mai
bună potrivire a argumentelor, atunci sunt încercate şi
conversii pentru tipuri definite de utilizatori.
Apelul este acceptat numai dacă selecţia unei versiuni a
funcţiei (pe baza criteriului de cea mai bună potrivire a argumentelor) este unică.
Clase și obiecte
O clasă este un tip de date definit de utilizator.
O declarare a unei clase defineşte un tip nou care
reuneşte date şi funcţii.
Acest tip nou poate fi folosit pentru a declara obiecte
de acest tip, deci un obiect este un exemplar (o
instanţă) a unei clase.
11/28/2014
8
Clase și obiecte
Forma generală de declaraţie a unei clase care nu
moşteneşte nici o altă clasă este următoarea:
class nume_clasa {
date şi funcţii membre private
specificator_de_acces date şi funcţii membre
specificator_de_acces date şi funcţii membre
…………………………………………….
specificator_de_acces date şi funcţii membre
} lista_obiecte;
Clase și obiecte Cuvântul-cheie class introduce declaraţia clasei (a tipului
de date) cu numele nume_clasa.
Dacă este urmată de corpul clasei (cuprins între acolade), această declaraţie este totodată o definiţie.
Dacă declaraţia conţine numai cuvântul-cheie class şi numele clasei, atunci aceasta este doar o declaraţie de nume de clasă.
Corpul clasei conţine definiţii de date membre ale clasei şi definiţii sau declaraţii (prototipuri) de funcţii membre ale clasei, despărţite printr-unul sau mai mulţi specificatori de acces: public private protected
Ascunderea datelor: public, private C++ conţine posibilităţi speciale pentru ascunderea datelor
(de alte părţi ale programului), evitând adresări gresite sau coliziuni de nume.
Există doua cuvinte cheie speciale: private şi public.
Ele pot fi inserate în definitia unei structuri.
public defineşte toate câmpurile unei structuri accesibile de întreg codul program;
private defineşte toate câmpurile accesibile doar de codul ce face parte din structură (adică accesibile numai funcţiilor membru).
Într-o structură toate câmpurile sînt public dacă nu este specificat explicit altceva.
Ascunderea datelor: public, private
Deşi ascunderea datelor poate fi realizată prin
intermediul structurilor, cel mai adesea sunt folosite
clasele.
O clasă este în principiu echivalentă cu o structură cu
excepţia faptului că toate câmpurile sunt, dacă nu e
specificat altfel, private.
11/28/2014
9
Clase și obiecte Efectul unui specificator de acces durează până la următorul
specificator de acces.
Implicit, dacă nu se declară nici un specificator de acces, datele sau funcţiile membre sunt de tip private.
De aceea, toate datele sau funcţiile declarate de la începutul blocului clasei până la primul specificator de acces sunt de tip private.
Diferenţa între tipurile de acces private şi protected constă în modul în care sunt moştenite drepturile de acces în clase derivate şi va fi detaliat în secţiunea care se ocupă cu clasele derivate.
Clase și obiecte
Definirea unei clase introduce în program un tip nou de
date, definit de utilizator, care reuneşte date şi funcţii
membre într-un fel de “cutie neagră” (black-box).
Aceasta reprezintă abstractizarea, adică identificarea
caracteristicilor definitorii pentru entitatea pe care o
modelează clasa respectivă.
În limbajele de programare orientate pe obiecte, clasele
permit abstractizarea datelor, care este o caracteristică
fundamentală a modelului obiect.
Clase și obiecte
Datele declarate într-o clasă se numesc date membre
de obicei, ele sunt protejate (private sau protected),
dar există şi situaţii în care sunt declarate public.
nu se pot declara auto, extern sau register datele
membre ale unei clase.
Funcţiile definite într-o clasă se numesc funcţii membre
(sau metode ale clasei)
de obicei ele sunt de tip public, dar pot fi şi protejate.
Clase și obiecte
Clasa Complex conţine
2 date membre private: re şi im de
tip double
3 funcţii membre publice: init(),
set() şi display().
11/28/2014
10
Clase și obiecte Clase și obiecte
În funcţia main() a programului de mai sus s-a declarat un obiect cu numele c1 de tipul (clasa) Complex.
Pentru un obiect dintr-o clasă dată se poate apela oricare funcţie membră a clasei, folosind operatorul punct de acces la un membru;
dacă se scrie: c1.init(), acest lucru însemnă apelul funcţiei init() pentru obiectul c1 din clasa Complex.
Constructori și destructori Constructori și destructori
11/28/2014
11
Constructori și destructori Constructori și destructori class Vector{ double v[4]; public: Vector(); Vector( *pv); void set_element(int i, double e){ v[i] = e;} double get_element(int i) const { return v[i]; } }; Vector::Vector(){ for (int i = 0; i < 4; i++) v[i] = 0.0; } Vector::Vector(double *pv){ for (int i = 0; i < 4; i++) v[i] = pv[i]; }
int main() { double v1[]={1,2,3,4}; Vector vec(v1); return 0; }
Constructori și destructori
class Vector{ int n; double* pv; public: Vector(int); Vector(int, double*); ~Vector(); }; Vector::Vector(int dim){ n = dim; pv = new double[n]; for ( int i = 0; i < n; i++) pv[i] = 0.0; } Vector::Vector(int dim, double *pvi){ n = dim; pv = new double[n]; for ( int i = 0; i < n; i++) pv[i] = pvi[i]; }
Vector::~Vector(){ cout << "Destructor ! " << endl; delete pv; pv = NULL; } int main(){ int nr; cout<<"Nr elemente dorit:"<<endl; cin >> nr; Vector v2(nr); return 0; }
Constructori și destructori class Person{ char name[5]; public: Person(); Person(char *pv); void display(){ for(int i = 0; i<5; i++) cout << name[i]; cout << endl; } }; Person::Person(){ for (int i = 0; i < 5; i++) name[i] = 'x'; } Person::Person(char *pv){ for (int i = 0; i < 5; i++) name[i] = pv[i]; }
int main() { char v1[5]={}; cout<<"Dati numele (4 char):”<<endl; cin.get(v1,5); for(int i=0; i<strlen(v1); i++) { cout << v1[i]; } Person p1(v1), p2; cout << "p1 name" << endl; p1.display(); cout << "p2 name" << endl; p2.display(); return 0; }
11/28/2014
12
Constructori de copiere Forma generală a constructorului de copiere al unei clase X
este: X::X(X& r){
// initializare obiect folosind referinţa r
}
De exemplu, pentru obiecte de tip Complex:
class Complex {
...
public:
Complex(Complex &r); //Constructorul de copiere
};
Complex::Complex(Complex &r){
re = r.re;
im = r.im;
}
Constructori de copiere
Programul principal:
void main(){
Complex c1(4,5); // Constructor
initializare
Complex c2(c1); // Constructor copiere
c2.display(); // afiseaza 4 5
Complex c3 = c2; // Constructor copiere
c3.display(); // afiseaza 4 5
}
Constructori de copiere
class Vector{ int n; double* pv; public: Vector(int); Vector(int, double*); Vector(Vector &v); ~Vector(); }; Vector::Vector(Vector &v){ n = v.n; pv = new double[n]; for ( int i = 0; i < n; i++) pv[i] = v.pv[i]; }
Vector::~Vector(){ cout<<"Destructor!"<<endl; delete pv; pv = NULL; } int main(){ int nr=10; Vector v2(nr); Vector v3(v2); Vector v4 = v3; return 0; }
Date membre de tip static
O dată membră a unei clase poate fi declarată static în declaraţia clasei.
Va exista o singură copie a unei date de tip static, care nu aparţine nici unuia dintre obiectele (instanţele) clasei, dar este partajată de toate acestea.
Declaraţia unei date de tip static într-o clasă este doar o declaraţie, nu o definiţie şi este necesară definiţia acesteia în altă parte în program (în afara clasei).
Aceasta se face redeclarând variabila de tip static folosind operatorul de rezoluţie pentru clasa căreia îi aparţine, şi fără specificatorul static.
11/28/2014
13
Date membre de tip static
O variabilă membră de tip static a unei clase există
înainte de a fi creat un obiect din clasa respectivă
Dacă nu este iniţializată explicit, este iniţializată
implicit cu 0.
Cea mai frecventă utilizare a datelor membre statice
este de a asigura accesul la o variabilă comună mai
multor obiecte, deci pot înlocui variabilele globale.
Date membre de tip static
class S{ int v; static int s; // declaratia var. statice s public: S(){ v = 0; } int gets() { return s; } int getv() { return v; } void incs() { s++;} void incv() { v++;} }; int S::s; // definitia var. statice s
Date membre de tip static void main(){ S x, y; cout << "Inainte de incrementare:\n"; cout << "x.s: " << x.gets() << " y.s: " << y.gets() << endl; cout << "x.v: " << x.getv() << " y.v: " << y.getv() << endl; x.incs(); x.incv(); cout << "Dupa incrementare:\n"; cout << "x.s: " << x.gets() << " y.s: " << y.gets() << endl; cout << "x.v: " << x.getv() << " y.v: " << y.getv() << endl; }
Date membre static - exemplu: class Directory
{
public: // constructori, destructori, etc.
.
.
private: // data membru
static char path [];
};
Data membru path este o variabilă statică privată.
Ea există într-un singur exemplar, chiar dacă se creează mai multe obiecte din clasa Directory.
Această dată poate fi inspectată sau alterată de constructor, destructor sau orice altă funcţie membru al clasei Directory.
11/28/2014
14
Date membre de tip static Deoarece constructorii sunt apelaţi pentru fiecare nou
obiect al clasei, datele static nu sunt niciodată iniţializate de constructori, ci cel mult modificate.
De obicei, definiţia şi iniţializarea datelor static apar în fişierul sursă ce conţine şi definiţia clasei.
Data membru path poate fi definită şi iniţializată în fişierul sursă al constructorului:
// data membru static: definitie si initializare
char Directory::path [200] = "/usr/local";
// constructorul implicit
Directory::Directory ()
{
…
}
Date membre de tip static Trebuie reţinut că definiţia unei date statice poate apare în
orice fişier sursă (dar numai o singură dată).
În declaraţia clasei, membrii statici sunt doar declaraţi; numai la definiţia lor tipul şi numele clasei apare explicit.
Un alt exemplu de folosire al unei date private static:
class Graphics {
public: // constructorul, destructorul
Graphics ();
~Graphics ();
// alte functii de interfata
...
private:
static int nobjects; // numara obiectele
void setgraphicsmode ();
void settextmode ();
};
Date membre de tip static
// data membru statica
int Graphics::nobjects = 0;
// constructorul
Graphics::Graphics ()
{
if (! nobjects)
setgraphicsmode ();
nobjects++;
}
// destructorul
Graphics::~Graphics ()
{
nobjects--;
if (! nobjects)
settextmode ();
}
Clasa Graphics defineşte
comunicarea programului cu un device
grafic.
Pregătirea iniţială a device-ului, care ar
reprezenta o trecere din modul text în cel
grafic, este o acţiune a constructorului şi
depinde de o variabilă statică nobjects.
Variabila respectivă numără obiectele
de tip Graphics prezente la un moment
dat.
Când se creează primul obiect, este
iniţializat şi modul grafic (de către
constructor).
În mod similar, destructorul face
trecerea de la modul grafic la cel text
dacă nu mai există nici un obiect grafic.
Funcții membre de tip static
Funcţiile membre ale unei clase pot fi de asemenea declarate de tip static.
O funcţie membră de tip static se declară în interiorul clasei şi se defineşte în interiorul clasei sau în afara acesteia, folosind operatorul de rezoluţie.
O funcţie membră statică are vizibilitatea limitată la fişierul în care a fost definită şi este independentă de instanţele (obiectele) clasei.
11/28/2014
15
Funcții membre de tip static
class Directory
{
public: // constructori, destructori
...
// functii static publice
static void setpath (char const *newpath);
private:
static char path [];
};
// definitia variabilei static
char Directory::path [199] = "/usr/local";
// functia static
void Directory::setpath (char const *newpath)
{
strncpy (path, newpath, 199);
}
Funcții membre de tip static
int main ()
{
// Alternativa (1): apelul setpath() fara nici
un obiect din clasa Directory
Directory::setpath ("/etc");
// Alternativa (2): cu un object
Directory dir;
dir.setpath ("/etc");
return (0);
}
Obiecte membre ale claselor
Datele membre ale unei clase pot fi atât variabile de tipuri predefinite, cât şi obiecte de tipuri definite de utilizator.
Membrii care sunt de tip definit de utilizator (clasă) trebuie să fie obiecte de tipuri (clase) definite anterior, nu doar declarate ca nume.
Dacă o clasă conţine obiecte membre de tipuri definite de utilizator, argumentele necesare pentru construirea acestora sunt plasate în definiţia (nu în declaraţia) constructorului clasei care le conţine.
Obiecte membre ale claselor class X{ int *px; public: X(); X(int sx); ~X(); }; inline X::X(){ cout<<"Constructor X implicit"<<endl; px = NULL; } inline X::X(int sx){ cout<<"Constructor X cu 1 arg”<<endl; px = new int[sx]; } inline X::~X(){ cout<<"Destructor X”<<endl; if(px){ delete px; px = NULL; } }
class Y{ int *py; X x; // data membru = obiect x tip X public: Y(int sy); Y(int sx, int sy); ~Y(); }; inline Y::Y(int sy){ cout<<"Constructor Y cu 1 arg”<<endl; py = new int[sy]; } inline Y::Y(int sx, int sy):x(sx){ cout<<"Constructor Y cu 2 arg”<<endl; py = new int[sy]; } inline Y::~Y(){ cout << "Destructor Y”<<endl; if(py){ delete py; py = NULL; } }
11/28/2014
16
Obiecte membre ale claselor
Mesajele care se afișează la rularea acestui program sunt:
Se observă că se construiește întâi data membră x și apoi obiectul de clasă Y care o conține!
Obiecte membre ale claselor
Dacă sunt mai multe date membre care se iniţializează,
acestea se pot trece în orice ordine, separate prin virgulă în
definiţia constructorului obiectului care le conţine.
Constructorii obiectelor membre sunt apelaţi în ordinea în
care acestea sunt specificate în declaraţia clasei.
La distrugerea unui obiect, se execută mai întâi
destructorul obiectului şi apoi, în ordinea inversă
declaraţiei, destructorii datelor membre ale acestuia.
Ordinea de execuție a constructorilor
și destructorilor
Un constructor este apelat la definirea obiectului, iar destructorul este apelat atunci când obiectul este distrus. Dacă există mai multe declaraţii de obiecte, atunci ele sunt construite în ordinea declaraţiei şi sunt distruse în ordinea inversă a declaraţiei.
Obiectele membre ale unei clase se construiesc înaintea obiectului respectiv. Destructorii sunt apelaţi în ordine inversă: destructorul obiectului şi apoi destructorii membrilor.
Funcţiile constructor ale obiectelor globale sunt executate înaintea execuţiei funcţiei main(). Destructorii obiectelor globale sunt apelaţi în ordine inversă, după încheierea funcţiei main().
Clase locale. Clase imbricate. O clasă locală este o clasă definită în interiorul unei
funcţii. O astfel de clasă este cunoscută numai în interiorul acelei funcţii şi este supusă mai multor restricţii:
toate funcţiile clasei locale trebuie să fie definite în interiorul clasei;
clasele locale nu admit variabile de tip static;
clasele locale nu au acces la variabilele locale ale funcţiei în care au fost declarate.
Din cauza acestor restricţii, clasele locale sunt rar utilizate în programarea C++ !
11/28/2014
17
Clase locale. Clase imbricate. O clasă imbricată (denumită şi clasă internă) este o clasă
definită în interiorul altei clase.
O astfel de clasă este cunoscută numai în domeniul clasei în care a fost definită, de aceea numele acesteia trebuie să fie precedat de numele clasei care o conţine folosind operatorul de rezoluţie (::).
Utilizarea specifică a claselor imbricate este în mecanismele de tratare a excepţiilor.
Deoarece excepţiile sunt definite pentru o anumită clasă, este normal ca tipul excepţiei (definit ca o clasă) să aparţină clasei care o defineşte.
Funcții și clase friend
O funcţie nemembră a unei clase poate accesa datele private sau protected ale acesteia dacă este declarată funcţie de tip friend a clasei.
Pentru declararea unei funcţii f() de tip friend a clasei X se include prototipul funcţiei f(), precedat de specificatorul friend în definiţia clasei X, iar funcţia însăşi se defineşte în altă parte în program astfel:
class X{ //…… friend tip_returnat f(lista_argumente); }; ……… tip_returnat f(lista_argumente){ // corpul functiei }
Funcții și clase friend Funcții și clase friend
Dat fiind că o funcţie nu poate fi membră a două
clase, cel mai natural mod de înmulţire a unei matrice
cu un vector ar părea să fie prin definirea unei funcţii
nemembre multiply() care accesează elementele
celor două clase prin funcţiile de interfaţă get() şi set().
11/28/2014
18
Funcții și clase friend
Dar acest mod de operare poate fi foarte ineficient, dacă
funcţiile de interfaţă get() şi set() ar verifica încadrarea
argumentului (indicele) în valorile admisibile.
Soluţia pentru această problemă o constituie declararea
funcţiei multiply() ca funcţie friend în cele două clase.
Funcții și clase friend
Funcții și clase friend Este posibil ca o funcţie membră a unei clase să fie declarată
friend în altă clasă.
De asemenea, este posibil ca o întreagă clasă Y să fie declarată friend a unei clase X şi, în această situaţie, toate funcţiile membre ale clasei Y sunt funcţii friend ale clasei X şi pot accesa toate datele şi funcţiile membre ale acesteia.
class Y{
//………
};
class X{
friend class Y;
//………
};
Supraîncărcarea operatorilor
Forma generală a funcţiilor operator membre ale clasei
este următoarea:
tip_returnat operator#(lista_argumente){
// operaţii
}
unde semnul # reprezintă oricare din operatorii care pot fi
supraîncărcaţi
11/28/2014
19
Supraîncărcarea operatorilor
Operatorii care pot fi supraîncărcaţi prin asocierea la o
clasa sunt:
Operatori supraîncărcabili
+ - * / = < > += -= *= /=
<< >> <<= >>= == != <= >= ++ -- %
& ^ ! | ~ &= ^= |= && || %=
[] () , ->* -> new delete new[] delete[]
Supraîncărcarea operatorilor Operatorii de asignare care pot fi supraîncărcaţi :
T& T::operator #=( argumente)
returnează de obicei o referință la propriul obiect ( *this ).
Operator Sintaxa Operator Sintaxa
asignare a = b AND cu asignare a &= b
adunare cu asignare a += b OR cu asignare a |= b
scădere cu asignare a -= b
XOR cu asignare a ^=b
înmulțire cu
asignare
a *= b shift la stinga cu
asignare
a <<= b
împărțire cu
asignare
a /= b shift la dreapta cu
asignare
a >>= b
modulo și asignare a %= b
Supraîncărcarea operatorilor Operatorii de incrementare/decrementare care pot fi
supraîncărcaţi :
Operatorii prefix returnează referințe ( *this )..
Operatorii postfix returnează valori.
Parametrul int este ”dummy”, doar face diferența între prefix și
postfix. La apel este de obicei dat 0.
Operator Sintaxa Exemplu prototip pt clasa T
preincrementare ++a T& T::operator++();
predecrementare --a T& T::operator--();
postincrementare a++ T T::operator++(int);
postdecrementare a-- T T::operator--(int);
Supraîncărcarea operatorilor Operatorii aritmetici care pot fi supraîncărcaţi :
T T::operator#( argumente)
returnează de obicei o valoare.
Operator Sintaxa Operator Sintaxa
plus unar +a modulo a % b
minus unar -a negare bitwise ~a
adunare a + b AND a & b
scădere a - b OR a | b
înmulțire a * b XOR a ^ b
împărțire a / b shift la stînga a << b
shift la dreapta a >> b
11/28/2014
20
Supraîncărcarea operatorilor
Operatorii logici care pot fi supraîncărcaţi :
Operatorii returnează de obicei o valoare bool.
Operator Sintaxa Exemplu prototip pt clasa T
negare !a bool T::operator!() const;
AND a && b bool T::operator&&(const T2 &b) const;
OR inclusiv a || b bool T::operator||(const T2 &b) const;
Supraîncărcarea operatorilor
Operatorii de comparare care pot fi supraîncărcaţi :
Operatorii returnează de obicei o valoare bool.
Operator Sintaxa Exemplu prototip pt clasa T
egalitate a == b
bool T::operator#(const T2 &b) const;
diferit a != b
mai mic a < b
mai mare a > b
mai mic sau
egal
a <= b
mai mare sau
egal
a >= b
Supraîncărcarea operatorilor
Operatori diverși care pot fi supraîncărcaţi :
Operatorii returnează de obicei o referință sau un pointer.
Operator Sintaxa Exemplu prototip pt clasa T
apel funcție a(a1,a2) R T::operator()(Arg1 &a1, Arg2 &a2,...);
virgulă a,b T2& T::operator,(T2 &b);
conversie (type)a operator type()
Supraîncărcarea operatorilor
Operatorul , built-in. Ce se afișează la consolă?
#include <iostream>
int main()
{
int n = 1;
int m = (++n, std::cout << "n = " << n << '\n', ++n, 2*n);
std::cout << "m = " << (++m, m) << '\n';
}
11/28/2014
21
Supraîncărcarea operatorilor
Există câteva reguli care trebuie să fie respectate la supraîncărcarea operatorilor:
Funcţiile operator=(), operator()(), operator[]() şi operator ->() trebuie să fie membri nestatici ai clasei.
Cu excepţia funcţiei operator =() toate celelate funcţii operator pot fi moştenite.
Nu pot fi supraîncărcaţi operatorii pentru tipurile predefinite ale limbajului.
Funcţiile operator nu pot avea argumente implicite.
Exemplu: class Complex
Exemplu: class Complex int main(){
Complex c1(7.2,9.3);
Complex c2(0.4,-9.3);
Complex c3;
c3 = c1 + c2;
c3.display();
// afiseaza 7.6+j0
return 0;
}
#include <iostream>
using namespace std;
class Complex {
double re;
double im;
public:
Complex();
Complex(double, double);
Complex operator + (Complex);
void display(){
cout << re << "+j" << im << endl;
}
};
Complex Complex::operator + (Complex b){
Complex c;
c.re = re + b.re;
c.im = im + b.im;
return (c);
}
Exemplu: class Point
class Point{
double x, y;
public:
Point(){ x = 0; y = 0;}
Point(double a, double b){x = a; y = b;}
void display() {
cout << x << " " << y << endl;
}
};
void main(){
Point pct1(10,20);
Point pct2(30,40);
Point pct3;
}
11/28/2014
22
Exemplu: class Point
class Point{
double x, y;
public:
// ...
Point operator +(Point op2);
Point operator -(Point op2);
};
Point Point::operator +(Point op2){
Point temp;
temp.x = x + op2.x;
temp.y = y + op2.y;
return temp;
}
void main(){
Point pct1(10,20);
Point pct2(30,40);
Point pct3;
pct3 = pct1 + pct2;
pct3 =pct1.operator+(pct2);
}
Exemplu: class Point
class Point{
double x, y;
public:
// ...
Point operator +(Point op2);
Point operator -(Point op2);
double operator *(Point op2);
Point& operator *=(double v);
};
double Point::operator *(Point op2){ // produs scalar
return x*op2.x + y*op2.y;
}
void main(){
Point pct1(10,20);
Point pct2(30,40);
Point pct3;
pct3 = pct1 * pct2;
pct3 =pct1.operator*(pct2);
}
Exemplu: class Point
class Point{
double x, y;
public:
// ...
Point operator +(Point op2);
Point operator -(Point op2);
double operator *(Point op2);
Point& operator *=(double v);
};
Point& Point::operator *=(double v){ //!!modifică obiectul
x *= v;
y *= v;
return *this; //returnează referinţa la propriul obiect
}
void main(){
Point pct1(10,20);
Point pct2(30,40);
Point pct3;
pct3 = pct1 + pct2;
double p = pct1 * pct2;
pct1 *= 2;
}
Exemplu: class Point
class Point{
double x;
double y;
public:
// ...
Point operator ++();
Point operator ++(int a);
};
Point Point::operator ++(){ // incrementare prefix
++x;
++y;
return *this;
}
Point Point::operator ++(int a){ // incrementare postfix
Point temp(*this); // a ia valoarea 0
x++;
y++;
return temp;
}
void main(){
Point pct1(10,20);
Point pct2(30,40);
Point pct3;
++pct1;
pct2++;
}
11/28/2014
23
Funcții operator friend
class Point{
double x;
double y;
public:
//...
friend Point operator +(Point op1, Point op2);
friend Point operator ++(Point &p);
friend Point operator ++(Point &p, int a);
};
Point operator +(Point op1, Point op2){
Point temp;
temp.x = op1.x + op2.x;
temp.y = op1.y + op2.y;
return temp;
}
void main(){
Point pct1(10,20);
Point pct2(30,40);
Point pct3;
pct3 = pct1 + pct2;
}
Funcții operator friend
class Point{
double x;
double y;
public:
//...
friend Point operator +(Point op1, Point op2);
friend Point operator ++(Point &p);
friend Point operator ++(Point &p, int a);
};
Point operator++(Point &p){ // incrementare prefix
++p.x;
++p.y;
return p;
}
Point operator++(Point &p, int a){// incrementare postfix
Point temp(p);
p.x++;
p.y++;
return temp;
}
void main(){
Point pct1(10,20);
Point pct2(30,40);
++pct1;
pct2++;
}
Exemplu class Vector class Vector {
double v[4];
public:
Vector();
Vector(double *pv);
void set(int i, double e) {v[i]=e;}
double get(int i) const {return v[i];}
};
Vector::Vector(){
for (int i=0;i<4;i++)
v[i]=0.0;
}
Vector::Vector(double *pv){
for (int i=0;i<4;i++)
v[i]= pv[i];
}
void main()
{
double v[]={1,2,3,4};
Vector v1(v);
}
Exemplu class Vector class Vector {
double v[4];
public:
Vector();
Vector(double *pv);
Vector operator + (Vector);
void set(int i, double e) {v[i]=e;}
double get(int i) const {return v[i];}
};
Vector Vector::operator +(Vector a){
Vector b;
for (int i=0;i<4;i++)
b.v[i] = v[i] + a.v[i];
return b;
}
void main()
{
double u[]={1,2,3,4};
double v[]={0,1,0,1};
Vector v1(u),v2(v),v3;
v3 = v1 + v2;
}
11/28/2014
24
Exemplu class Vector class Vector { double v[4]; public:
... Vector operator + (Vector); Vector operator + (double);
... }; Vector Vector::operator +(Vector a){ Vector b; for (int i=0;i<4;i++) b.v[i] = v[i] + a.v[i]; return b; } Vector Vector::operator +(double a) { Vector b; for (int i=0;i<4;i++) b.v[i] = v[i] + a; return b; }
void main()
{
double u[]={1,2,3,4};
double v[]={0,1,0,1};
Vector v1(u),v2(v),v3,v4;
v3 = v1 + v2;
v4 = v1 + 3.5;
}
Exemplu class Vector class Vector {
int n;
double* pv;
public:
Vector(int m);
Vector(int m, double *pvi);
~Vector();
};
Vector::Vector(int m){
n = m;
pv = new double[n];
for (int i=0;i<n;i++)
pv[i]=0.0;
}
Vector::Vector(int m, double *pvi){
n = m;
pv = new double[n];
for (int i=0;i<n;i++)
pv[i]= pvi[i];
}
void main(){
int nr;
cout << “Nr elem”;
cin >> nr;
Vector v1(nr);
}
Vector::~Vector(){
if (pv){
delete pv;
pv = NULL;
}
Exemplu class Vector class Vector {
int n;
double* pv;
public:
Vector(int n);
Vector(int n, double *pvi);
Vector operator + (Vector);
~Vector();
};
Vector Vector::operator +(Vector a)
{
Vector b(n);
for (int i=0;i<n;i++)
b.v[i] = v[i] + a.v[i];
return b;
}
void main(){
int nr;
cout << “Nr elem”;
cin >> nr;
Vector v1(nr);
}
Vector::~Vector(){
if (pv){
delete pv;
pv = NULL;
}
Exemplu class Vector class Vector {
int n;
double* pv;
public:
Vector(int n);
Vector(int n, double *pvi);
Vector(Vector &v);
~Vector();
};
//Constructorul de copiere
Vector::Vector(Vector &v){
n = v.n;
pv = new double[n];
for (int i=0;i<n;i++)
pv[i] = v.pv[i];
}
void main(){
int nr = 10;
Vector v1(nr);
Vector v2(v1);
Vector v3 = v2;
}
Vector::~Vector(){
if (pv){
delete pv;
pv = NULL;
}
11/28/2014
25
Exemplu class Vector class Vector {
int n;
double* pv;
public:
Vector(int n);
Vector(int n, double *pvi);
Vector(Vector &v);
Vector operator = (Vector);
~Vector();
};
//Operatorul de asignare
Vector& Vector::operator =(Vector& a)
{
n = a.n;
pv = new double[n];
for (int i=0;i<n;i++)
pv[i] = a.pv[i];
return *this; }
void main(){
int nr = 10;
Vector v1(nr);
Vector v2(v1);
Vector v3 = v2;
}
Vector::~Vector(){
if (pv){
delete pv;
pv = NULL;
}
Exemplu class Vector class Vector {
int n;
double* pv;
public:
Vector(int n);
Vector(int n, double *pvi);
Vector(Vector &v);
Vector operator = (Vector);
double operator [](int i);
~Vector();
};
//Operatorul de indexare
double Vector::operator[](int i){
if ((pv!=NULL) && (i<n))
return pv[i];
else
return 0;
}
void main(){
int nr=10;
Vector v1(nr);
Vector v2(v1);
Vector v3 = v2;
double d = v2[5];
}
Vector::~Vector(){
if (pv){
delete pv;
pv = NULL;
}