Constructorul de copiere este un constructor folosit clonarea unui obiect instanta clasei, adica pentru a crea un obiect nou copie cat mai fidela a unui obiect existent. Orice clasa C++ are constructor de copiere. In lispa definirii de catre programator explicit a constructorului de copiere se creeaza la compilare construcotr de copiere implicit. Constructorul de copiere implicit initializeaza toate campurile obiectului nou cu valorile campurilor obiectului clonat. Acest mecanism poate conduce la creearea unui obiect nou avand cateva probleme nedorite. Exemplu: presupunem ca un obiect prelucreaza un fisier. De exemplu, scrie mesaje in el. Un obiect creat cu constructorul de copiere implicit va folosi acelasi fisier, cele doua obiecte depunand ambele mesaje in acelasi fisier. Este foarte probabil ca functionalitatea unui astfel de obiect sa fie gandita pentru a functiona cu un fisier unic atasat acelui obiect. Pentru ca obiectul clona sa lucreze cu un alt fisier ar trebui sa scriem noi constructorul de copiere care va creea un alt fisier pentru obiectul clona. O situatie similiara ar fi atunci cand un obiect are deschisa o conexiune pe internet sau cu o baza de date si obiectul clona ar trebui si obiectul clona ar trebui sa aiba o alta conexiune decat a obiectului clonat. Cea mai mare problema apare atunci cand un obiect aloca dinamic memorie si adresele acestor zone sunt retinute in campurile obiectului. Constructorul de copiere implicit creeaza un nou obiect in care campurile pointer vor retine adresele de memorie alocata dinamic din obiectul clonat, astfel, cele doua obiecte vor folosi in comun zone de memorie alocate dinamic. In cele mai multe situatii acest lucru este nedorit, adica fiecare obiect trebuie sa aiba zone de memorie distincte alocate dinamic. Situatia descrisa mai sus poate genera mai multe probleme:
- orice modificare a zonei de memorie alocata dinamic de catre un obiect este vizibila si in celalalt obiect
- atunci cand unul dintre obiecte elibereaza zona de memorie alocata dinamica (la distrugere), celalalt obiect ramane fara zona de memorie alocata dinamic; mai mult, este posibil ca la distrugerea celuilalt obiect sa se incerce reeliberarea zonei de memorie alocata dinamic, ceea ce conduce la eroare run time In consecinta, pentru a evita probleme de mai sus, se procedeaza astfel: pentru obiectul nou creat se aloca zone noi de memorie si informatiile din zonele alocate dinamic ale primului obiect se copiaza in zonele de memorie ale obiectului clona.
Constructorul de copiere poate fi explicit apelat de catre programator la creearea unui nou obiect, ca orice constructor, insa, in cele mai multe situatii, constructorul de copiere este apelat automat atunci cand avem transmitere prin valoare a unor obiecte instanta clasei respective ca parametri unei functii si atunci cand un obiect este returnat prin valoare, ca valoarea functiei, existand si exceptii.
//polinom.h
class Polinom
{
int n; //grad
float* a; //coeficienti
public:
Polinom(){n = -1; a = NULL;}
Polinom(const Polinom&); //construcor de copiere
/*
* Constructorul de copiere il recunoastem prin faptul
* ca are un singur parametru de tip referinta catre
* clasa respectiva. Mai mult, acest parametru se
* declara ca fiind const.
*/
friend ostream& operator<<(ostream&, Polinom);
friend istream& operator>>(istream&, Polinom);
Polinom operator+(Polinom);
Polinom operator=(Polinom&);
~Polinom();
}
//polinom.cpp
Polinom::Polinom(const Polinom& p)
{
if (p.n < 0)
{
n = -1;
a = NULL;
return;
}
n = p.n;
a = new float[n+1];
for (int i=0; i <= n; i++)
a[i] = p.a[i];
}
ostream& operator<<(ostream& fl, Polinom p)
{
if (p.n < 0)
throw "Polinom neinitializat.";
fl << p.n;
for (int i = 0; i <= n; i++)
fl << " " << p.a[i];
return fl;
/*
* Din cauza ca operatorul p este transmis prin valoare in
* cazul operatorului << pentru el este apelat
* constructorul de copiere; in implementarea operatorului
* ajungand o clona a celui de-al doilea operand.
*/
}
istream& operator>>(istream& fl, Polinom p)
{
fl >> p.n;
if (p.n < 0)
throw "Grad invalid";
if (p.a)
delete[]p.a;
p.a = new float[p.n+1];
for (int i = 0; i <= p.n; i++)
fl >> p.a[i];
return fl;
/*
* La supraincarcarea operatorului >> nu se foloseste
* constructorul de copiere
*/
}
Polinom::Polinom()
{
if (a)
delete[] a;
}
Polinom Polinom::operator+(Polinom p)
{
if (n < 0 || p.n < 0)
throw "Grad invalid";
Polinom s;
if (n <= p.n)
{
s.n = p.n;
s.a = new float[s.n+1];
for (int i = n+1; i <= p.n; i++)
s.a[i] = p.a[i];
for (int i = 0; i <= n; i++)
s.a[i] = a[i] + p.a[i];
}
else
{...}
return s;
}
Polinom Polinom::operator=(Polinom& p)
{
if (this == &p)
return *this; //pentru atribuirea p = p
if (a)
delete[]a;
n = p.n;
if (!p.a)
a = NULL;
else
{
a = new float [n+1];
for (int i = 0; i <= n; i++)
a[i] = p.a[i];
}
return *this;
}
La implementarea operatorului +
primim parametrul prin valoare ceea ce inseamna ca polinomul s
este returnat prin valoare ceea ce ar insemna ca se apeleaza constructorul de copiere. Din cauza ca variabila s
,dupa parasirea operatorului +
iese din stiva de executie, obiectul distrugandu-se, compilatorul face o optimizare care consta in returnarea obiectului s
in locul clonei acesteia.
La apelul operatorului =
, constructorul de copiere va fi apelat o singura data, la momentul in care se ajunge la return *this
, deoarece se doreste returnarea unei copii a obiectului curent si obiectul curent nu se distruge, asa cum s-a intamplat la +
, unde s
se distruge.
Primul if
de la implementarea lui =
evita situatia speciala in care operatorul =
ar fi apelat sub forma p = p
.
TPA:
+
real-polinom-
real-polinom*
polinom-polinom, real-polinom, polinom-real/
polinom-real, polinom-polinom (catul)%
polinom-polinom (restul)==
,!=
!
returneaza gradul polinomului