Sunteți pe pagina 1din 7

13.

Matrice, pointeri si referinte

Initializarea obiectelor cu un singur paramteru in matrici

#include<iostream>
using namespace std;

class C {
int i;
public:
C(int j) { i = j; } //constructor
int da() {
return i;
}
};

void main()
{
C ob[4] = {1, 2, 3, 4}; //aici se face initializarea

for(int i = 0; i < 4; i++) cout<<ob[i].da()<<endl;


}

Initializarea obiectelor cu mai multi parametri in matrici

#include<iostream>
using namespace std;

class C {
int a, b;
public:
C(int j, int k) { //constructor
a = j;
b = k;
}
int da_a() {return a;}
int da_b() {return b;}
};

void main()
{
C ob[3] = { C(1, 2), C(3, 4), C(5, 6) }; //initializare

for(int i = 0; i < 3; i++)


cout<<"obiectul "<<i<<" = "<<ob[i].da_a()<<" "<<ob[i].da_b()<<endl;
}

Matrici initializate / neinitializate

Pentru a declara matrici de obiecte care sa poata fi initializate trebuie definit un constructor cu parametri
care sa faca posibila initializarea.
Pentru a declara matrici de obiecte care sa nu fie initializate trebuie definit un constructor fara parametri.
Pentru a declara matrici de obiecte care sa fie cand initializate cand neinitializate se supraincarca functia
constructor.

#include<iostream>
using namespace std;

class C {
int i;
public:
C() { i = 0; } //apelare ptr. matrice neinitializate
C(int j) { i = j; } //apelare ptr. matrici initializate
int da() {return i; }
};

void main()
{
C ob1[3] = {1, 2, 3}; //initializat
C ob2[34]; //neinitializat
}

Pointeri catre obiecte

Atunci cand este incrementat un pointer, el indica spre urmatorul element de acelasi tip cu al sau. De
exemplu, un pointer de tip intreg va indica urmatorul intreg. Tot asfel se intampla in cazul pointerului la obiecte.

#include<iostream>
using namespace std;

class C {
public:
int i, j;
C(int k, int m) {i = k; j = m;}
};

void main()
{
C ob(1, 2);
int *p = NULL, *q = NULL;

p = &ob.i; //preia adresa lui ob.i


q = &ob.j; //preia adresa lui ob.j

cout<<*p<<endl; //are acces la ob.i prin p; output: 1


cout<<*q<<endl; //are acces la ob.j prin p; output: 2
}

Deoarece p indica spre un intreg, el este declarat ca un pointer de tip intreg. In aceasta situatie este
irelevant ca i este un membru al obiectului ob.

Pointerul this

Cand este apelata o functie membru, i se paseaza un argument implicit, care este un pointer catre obiectul
care a generat apelarea (obiectul care a invocat functia). Acest pointer este numit this.
La membrii unei class se poate capata acces direct dintr-o functie membru. Instructiunea b = j; ar comanda
ca valoarea continuta in baza sa fie atribuita unei copii a lui b asociata obiectului care a generat apelarea. Totusi,
aceeasi intructiune poate fi scrisa si astfel:

this->b = j;

Pointerul this indica spre obiectul care a apelat ADUNA(). Astfel, this->b se refera la copia b pentru acel
obiect. De exemplu, daca ADUNA() este apelata de ob (ca in ob(1, 2)), atunci this din instructiunea precedenta
indica spre ob. ATENTIE: scrierea instructiunii fara this este doar o prescutare.
Pointerul this este transmis automat catre toate functiile membru.
PRECAUTII:
1. functiile friend nu sunt membri ai clasei si de aceea nu le sunt pasati pointeri this
2. functiile membre static nu au un pointer this

#include<iostream>
using namespace std;

class ADUNA {
int a, b, suma;
public:
ADUNA(int i, int j) {
this->a = i;
this->b = j;
this->suma = this->a + this->b;
}
int da() {return this->suma;}
};

void main()
{
ADUNA ob(1, 2);
cout<<ob.da()<<endl;
}

Pointeri catre tipuri derivate

Un pointer din clasa de bază poate să fie folosit ca un pointer spre un obiect din oricare clasă derivată din
clasa de bază.
Reciproca nu este adevărată: un pointer din clasa derivată nu poate indica spre un obiect din clasa de bază.
Mai mult, chiar dacă se poate folosi un pointer din baza pentru a indica un obiect derivat, accesul este permis doar la
membrii de tip derivat care au fost importati din baza. Deci nu este permis accesul la membrii adaugaţi în clasa
derivată. Totuşi pointerul din clasa de bază poate fi convertit la unul (pointer) derivat şi câştigă acces la deplin la
întreaga clasă derivată.

#include<iostream>
using namespace std;

class BAZA {
int b;
public:
void pune_b(int num) {b = num; }
int da_b() {return b; }
};

class DERIVAT : public BAZA {


int d;
public:
void pune_d(int num) {d = num; }
int da_d() {return d; }
};

void main()
{
BAZA *pb;
DERIVAT d;

pb = &d; //pb indica adresa obiectului derivat 'd'

pb->pune_b(10);

cout<<pb->da_b()<<endl; //acces prin pointer

cout<<d.da_b()<<endl; //acces prin obiectul accesat de pointerul de mai sus

//pb->pune_d(20);// error C2039: 'pune_d' : is not a member of 'BAZA'

((DERIVAT*)pb)->pune_d(20); /*OK: conversia tipului de baza la cel derivat


totusi NEPROFESIONIST */
cout<<((DERIVAT*)pb)->da_d()<<endl;
}
IMPORTANT: ARITMETICA POINTERILOR ESTE RELATIVĂ LA TIPUL DE BAZĂ AL POINTERULUI.
Aşadar, un pointer la tipul de bază ce indică la un tip derivat, prin incrementare va indica spre următorul obiect de
tip bază şi nu spre următorul tip derivat.

Pointeri către membrii clasei (.*) si (->*)

Pointerul care “indică” generic către un membru al unei clase şi nu către un anumit exemplar al acelui
membru dintr-un obiect se numeşte pointer către un membru al clasei (sau pointer-la-membru). Pointerul la membru
nu este acelaşi lucru cu pointerul normal. Pointerul la membru asigură doar un offset (o poziţie) într-un obiect din
clasa membrului, unde poate fi găsit acel membru. Deoarece pointerul la membru nu este pointer adevărat, nu i se
poat aplica operatorii . şi ->. Pentru a avea acces la membrul unei clase prin intermediul unui pointer spre el, va
trebui să folosiţi operatorii speciali ai pointerilor la membri, .* şi ->*. Misiunea lor este să vă permită accesul la un
membru al unei clase prin intermediul unui pointer către acesta.

#include<iostream>
using namespace std;

class C {
public:
C(int i) {val = i;}
int val;
int val_dubla() {return val+val;}
};

void main()
{
int C::*date; //pointer la o data membru
int (C::*func)(); //pointer la o functie membru

date = &C::val; //da offsetul (pozitia) pentru val (generic)


func = &C::val_dubla; //da offsetul (pozitia) pentru val_dubla

C ob(1), *pob;
pob = &ob;

cout<<ob.*date<<endl; //apel prin obiect


cout<<pob->*date<<endl; //apel prin pointer la obiect

cout<<(ob.*func)()<<endl;
cout<<(pob->*func)()<<endl;
}

NOTA: Pointerii-la-membru se aplică în situaţii speciale. Ei nu sunt folosiţi curent în programarea de zi cu


zi.

Referinţe

O referinţă este un pointer implicit care acţionează ca un alt nume al unui obiect.

Parametri de referinţă

Referinţa permite crearea unor funcţii care folosesc automat transmiterea prin referinţă.
Pentru a crea o apelare prin referinţă în C, trebuie pasată explicit funcţiei adresa argumentului.

#include<iostream>
using namespace std;

void fa_10(int *i) {*i = 10;}

void main() {
int x = 1;
fa_10(&x);
cout<<x<<endl;
}

fa_10() preia ca parametru un pointer către un întreg pe care îl va transforma în 10. În interiorul funcţiei fa_10()
trebuie folosit operatorul * pentru a avea acces la variabila spre care indică i. Acesta este modul de generare a unei
apelări-prin-referinţă-manuale.
În C++, pentru a crea un parametru de referinţă, numele parametrului trebuie precedat de &. Funcţia
fa_10() ar deveni:
void fa_10(int &i);
i devine practic un alt nume pentru orice argument folosit la apelul funcţiei. Altfel spus, i este un pointer implicit
care se referă la argumentul folosit pentru invocarea funcţiei fa_10(). Folosirea mai departe a operatorului * nu mai
este permisă. De asemenea, în apelul funcţiei nu mai este permisă folosirea operatorului &.

#include<iostream>
using namespace std;

void fa_10(int &i) {i = 10;}

void main() {
int x = 1;

fa_10(x); //x este apelat prin referinta si schimbarea lui este definitiva
cout<<x<<endl;
}

NOTĂ: în apelul prin referinţă se modifică obiectul apelat şi nu copia lui.

Transmiterea referinţelor către obiecte

Când se face apel prin referinţă, nu se face nici o copie a obiectului, aşa cum se întâmplă cu apelul prin
valoare. Aceasta înseamnă că nici un obiect folosit ca parametru nu este distrus atunci când se termină funcţia, iar
destructorul parametrului nu este apelat.
NOTĂ: când parametrii sunt transmişi prin referinţă, schimbările obiectului din interiorul funcţiei afectează
obiectul apelant.

Returnarea referinţelor

O funcţie poate să returneze o referinţă ceea ce face ca ea să poată fi folosită în membrul stâng al unei
instrucţiuni de atribuire.

#include<iostream>
using namespace std;

int var_globala = 1;

int &schimba( /*orice parametru ar fi aici ar fi unul temporar


chiar da ca ar avea numele 'var_globala' */ )
{
var_globala = 2; //var_globala devine 2
return var_globala;
}

void main() {
schimba();
cout<<var_globala<<endl; //output: 2
schimba() = 3;
cout<<var_globala<<endl; //output: 3
}

ATENŢIE!!!
#include<iostream>
using namespace std;

int var = 1;

int &schimba(int var) /* var din argumentul functiei este locala


deci temporara */
{
var = 2; //copia lui var devine 2;
return var; //warning C4172: returning address of local variable or temporary
} //LA IESIREA DIN FUNCTIE COPIA LUI var ESTE DISTRUSA

void main() {
schimba(3); //local var devine 2 dar var e distrus la iesirea din functie
cout<<var<<endl; //aici var este globala; output: 1
schimba(4); //local var devine 2 dar var e distrus la iesirea din functie
cout<<var<<endl; //aici var este globala; output: 1
}

CORECT AR FI:

#include<iostream>
using namespace std;

int var = 1;

int &schimba(int &var)


{
var = 2;
return var;
}

void main() {
schimba(var); cout<<var<<endl; //output: 2
schimba(var) = 3; cout<<var<<endl; //output: 3
}

Referinţe independente

O referinţă care este doar o simplă variabilă este numită referinţă independentă.

#include<iostream>
using namespace std;

void main() {
int a;
int &ref = a; //referinta independenta

a = 10; cout<<a<<" "<<ref<<endl;

ref = 100; cout<<a<<" "<<ref<<endl;

int b = 19; ref = b; //aceasta pune valoarea lui b in a


cout<<a<<" "<<ref<<endl;

ref = ref-10; //aceasta decrementeaza a


//nu afecteaza la ce se refera ref
cout<<a<<" "<<ref<<endl;
}

NOTĂ: referinţa independentă este de mică valoare practică deoarece ea este de fapt doar un alt nume aceeaşi
variabilă. Având două nume care descriu acelaşi obiect programul poate deveni confuz.

RESTRICŢII:
1. o referinţă nu poate referi altă referinţă
2. nu se pot crea matrice de referinţe
3. nu se poate crea un pointer spre o referinţă
4. nu se pot face referinţe la câmpuri de biţi

ATENŢIE:
1. o referinţă trebuie iniţializată la declarare
2. o referinţă poate să nu fie declarată dacă este membru al unei clase, parametru de funcţie sau o valoare returnată
Operatorii de alocare dinamică

Operatorul delete eliberează memoria alocată în heap de new.


p_var = new tip;
delete p_var;