3.5. Constructor de copiere (atribuire)

3.1. Declararea claselor

3.1. Declararea claselor

Forma general? de declarare a unei clase de baz? (adic?, a clasei care nu mo?tene?te nici de la o alt? clas?) este urm?toarea:

class nume_clasa{

      [date_func?ii_prototipuri_prieteni]

   [specificator_de_acces:

      date_func?ii_prototipuri_prieteni

   specificator_de_acces:

      date_func?ii_prototipuri_prieteni

   …

   specificator_de_acces:

      date_func?ii_prototipuri_prieteni]

} [list?_de_obiecte];

[defini?ia_func?iei_membre_sau_prietene

defini?ia_func?iei_membre_sau_prietene

defini?ia_func?iei_membre_sau_prietene]

Descriind construc?iile limbajului C++, vom folosi perechea de caractere [ ] pentru a marca subconstruc?iile a c?ror prezen?? nu este obligatorie ?n construc?ia respectiv?. Totodat?, caracterele [ ] vor ?nsemna caractere care reprezint? partea component? a construc?iei de descriere.

Dup? cum se vede, declara?ia ?ncepe cu cuv?ntul-cheie class, dup? care urmeaz? denumirea clasei. Denumirea clasei este orice identificator creat de c?tre programator conform sintaxei limbajului C++ ?i care este liber ?n momentul declar?rii. Apoi, urmeaz? corpul clasei luat ?n acolade { ?i }. Corpul poate fi gol, ?ns? practic ?ntotdeauna el reprezint? declar?ri de c?mpuri (date) membre, declar?ri de prototipuri ale func?iilor membre ?i ale func?iilor prietene, definiri de func?ii membre ?i de func?ii prietene, declar?ri de clase prietene.

Declararea c?mpurilor ?i a func?iilor membre se repartizeaz? pe sec?iuni ?n dependen?? de regimul dorit de accesare a lor. Acest regim este stabilit prin utilizarea a trei specificatori de acces: public, protected, private. Ordinea sec?iunilor, precum ?i num?rul de repet?ri sunt arbitrare. Dac? la ?nceputul corpului de declarare a clasei, pentru un set de c?mpuri ?i func?ii membre specificatorul de acces nu este indicat, implicit, pentru c?mpuri ?i func?ii membre din aceast? sec?iune va fi stabilit regimul de accesare private (privat sau particular) – cel mai dur regim de accesare din trei posibile.

C?mpurile ?i func?iile membre private sunt accesibile numai din interiorul acestei clase, adic? acces la ele au numai func?iile membre ale sale, sau func?iile de tip prieten (friend) al acestei clase. Regimul de accesare protected (protejat) este pu?in mai liber ?n compara?ie cu regimul private. El stabile?te c? c?mpurile ?i func?iile membre ale clasei definite vor fi accesibile ?i din interiorul claselor derivate ale clasei definite. Specificatorul de acces public este cel mai liber din cei trei. El admite accesarea c?mpurilor ?i a func?iilor membre ale acestei sec?iuni din orice loc al programului unde va fi vizibil obiectul concret al clasei definite. A?a o accesare este asigurat? prin intermediul numelui acestui obiect.

?n C++ declararea claselor este similar? cu declararea structurilor. ?n cazul structurilor se folose?te cuv?ntul-cheie struct ?n loc de class. Unica diferen?? const? ?n aceea c? ?n cazul structurilor, implicit, va fi stabilit regimul de accesare public ?i nu private, cum a fost descris mai sus pentru clase.

Imediat dup? descrierea corpului clasei (acolada de ?nchidere) putem crea un set de obiecte ale acestei clase. ?ns?, acest lucru nu este obligatoriu. Obiectele pot fi create ?i mai departe ?n program.

Dup? caracterul ';' urmeaz? defini?iile (realiz?rile) func?iilor membre, dac? prototipurile lor au fost declarate ?n corpul clasei.

Trecem la exemplu. Declar?m versiunea simplificat? la maximum a clasei pentru operarea cu frac?ii ra?ionale f?r? semn. Vom numi aceast? clas? fractie_rationala:

#include 
 
class fractie_rationala
{
   protected:
      unsigned int numarat;
      unsigned int numit;
};
 
void main()
{
   fractie_rationala fr1; // cre?m un obiect numit fr1, 
                          // reprezentantul clasei 
                          // fractie_rationala 
   fractie_rationala fr2; // mai cre?m un obiect numit
                          // fr2, reprezentantul aceleia?i
                          // clase 
   fr1 = fr2; // atribuim obiectului fr1 obiectul fr2 
 
   char c; 
   cin >> c; // a?tept?m ap?sarea oric?rei taste
}
//De fapt, conform sintaxei, versiunea minimal? 
//a clasei fractie_rationala este:
class fractie_rationala
{
 
};

 


?ns?, fiind perfect valid? din punctul de vedere al sintaxei, ea nu este interesant? din punctul de vedere al semanticii, de aceea nici nu va fi examinat?.

Clasa fractie_rationala con?ine dou? c?mpuri numarat ?i numit pentru memorizarea, respectiv, a num?r?torului ?i a numitorului ce compun o frac?ie ra?ional? f?r? semn. Ambele c?mpuri sunt de tipul unsigned int ?i au unul ?i acela?i regim de acces protected. Dac? ?nl?tur?m linia:

   protected:

din programul de mai sus, atunci, implicit, c?mpurile numarat ?i numit vor avea regimul de acces private.

?ns?, ?n ambele cazuri nu avem altceva de f?cut dup? crearea obiectelor clasei fractie_rationala dec?t s? atribuim valorile de la un obiect la altul, cum este ar?tat ?n func?ia main(). Operatorul de atribuire este implicit realizat cu orice clas? declarat?. El presupune copierea pe bi?i a con?inutului memoriei ocupate de c?tre obiectul din partea dreapt? a semnului operatorului de atribuire (?n exemplul nostru – obiectul fr2), ?n zona de memorie ocupat? de c?tre obiectul din partea st?nga a semnului operatorului de atribuire (?n exemplul nostru – obiectul fr1).

Nu se ?tie ce va fi copiat ?n fr1 ?n exemplul nostru, fiindc? nici ?n c?mpurile obiectului fr1, nici ?n c?mpurile obiectului fr2 nu a fost ?nscris? nici o valoare c?nd aceste obiecte au fost create ?n memoria operativ?. Ini?ializarea pur ?i simplu nu a fost prev?zut? ?n versiunea clasei de mai sus. Mai mult ca at?t, nici nu exist? posibilitatea de a ?nscrie cumva valorile ?n c?mpurile obiectelor fr1 ?i fr2. Unicul lucru care poate fi spus este c? dup? atribuire, con?inuturile zonelor de memorie ocupate de c?tre obiectele fr1 ?i fr2 vor fi identice.

?n primul r?nd, nu putem accesa c?mpurile obiectelor. Astfel, orice ?ncerc?ri de acest tip de accesare, cum ar fi, de exemplu, atribuiri (?i nu numai) ca:

fr1.numarat = 2;

   fr2.numit = 3;

vor fi respinse de c?tre compilator, fiind interzise.

?n versiunea propus? a clasei fractie_rationala nu putem ini?ializa c?mpurile obiectelor create, nu putem afi?a obiectele, nu putem efectua calculele asupra lor etc. Lucrul acesta se face cu ajutorul func?iilor membre (metodelor) ale clasei.

Exerci?iul 3.1. Aduce?i exemple de declarare a claselor pentru fiecare defini?ie de obiect din vocabularele explicative ale limbii rom?ne.
_________________________
Autorul: dr.conf. S. Pereteatcu

3.2. No?iune de constructor ?i destructor

3.2. No?iune de constructor ?i destructor

La prima vedere, clasa fractie_rationala din exemplul de mai sus nu are nici o func?ie membr?. Totu?i, c?teva func?ii speciale au fost create implicit de c?tre compilator. Una dintre ele este constructorul f?r? parametri al clasei ?i are denumirea fractie_rationala().

A doua func?ie este destructorul clasei ?i are denumirea ~fractie_rationala(). Alt? func?ie creat? implicit – constructorul de copiere – va fi discutat? ?n 3.5.

Existen?a func?iilor create implicit o putem demonstra modific?nd ?n exemplul precedent doar func?ia main() astfel:

void main()
{
   // crearea obiectului cu apelul evident al constructorului
   fractie_rationala fr1=fractie_rationala();
 
   fractie_rationala fr2; // crearea obiectului cu apelul
                          // implicit al constructorului
   fr2 = fractie_rationala();// atribuirea obiectului fr2
                             // unui obiect creat temporar
                             // prin apelul constructorului
   fr1 = fr2;
   fr1.~fractie_rationala(); // apelul explicit al
                             // destructorului pentru
                             // obiectul fr1
   fr2.~fractie_rationala(); // apelul explicit al
                             // destructorului pentru fr2
   char c; 
   cin >> c;
}

 


Re?ine?i c?:

   fractie_rationala fr3();

nu este crearea obiectului fr3, ci declararea prototipului unei func?ii fr3 care ?ntoarce, ca rezultat, un obiect de tipul fractie_rationala.

La crearea oric?rui obiect va fi apelat? func?ia constructor. Dac? acest lucru nu va fi f?cut direct de c?tre programator, el va fi f?cut implicit de c?tre compilator. Iar la distrugerea oric?rui obiect va fi apelat? implicit func?ia destructor.

Constructorul este func?ia membr? special? a clasei, destinat? pentru ini?ializarea fiec?rui obiect al acestei clase dup? repartizarea memoriei la crearea lui.

Denumirea func?iei constructor ?ntotdeauna coincide cu denumirea clasei. Prin aceasta ea ?i devine constructor. O clas? poate s? posede mai mul?i constructori, care trebuie s? difere prin lista parametrilor.

 

Destructorul este func?ia membr? special? a clasei, destinat? pentru efectuarea unor lucr?ri ad?ug?toare care pot fi necesare ?nainte de eliberarea memoriei la distrugerea fiec?rui obiect al clasei, c?nd expir? termenul lui de „via??”.

Denumirea destructorului coincide cu denumirea clasei prefixat? de caracterul ~. Anume prin aceasta func?ia membr? devine destructor. Orice clas? poate s? posede doar un singur destructor ?i el, la r?ndul lui, nu poate s? posede parametri. Denumirea pentru destructor provine de la notarea operatorului ~ (complement fa?? de 1, NOT) din limbajul C, ?n sens c? „destructorul este complementul constructorului”.

De exemplu, la clasa care se nume?te punct constructorii vor fi punct(), iar destructorul ~punct(), la clasa care se nume?te caseta_de_dialog constructorii vor fi caseta_de_dialog(), iar destructorul ~caseta_de_dialog() etc.

Constructorul ?i destructorul implici?i nu fac nimic, ei pur ?i simplu vor fi apela?i automat: respectiv, la crearea obiectului ?i la distrugerea lui. De fapt, constructorul mai are ?i o destina?ie, legat? de func?iile virtuale, care va fi explicat? mai t?rziu.
_________________________
Autorul: dr.conf. S. Pereteatcu

3.3. Apelurile constructorilor ?i destructorilor

3.3. Apelurile constructorilor ?i destructorilor

Cercet?m apelurile constructorului ?i destructorului. Pentru aceasta, redefinim temporar, numai cu scopuri demonstrative, func?iile implicite constructor ?i destructor ?n clasa fractie_rationala, modific?nd totodat? ?i func?ia main():

#include "iostream.h"
 
class fractie_rationala
{
   protected:
      unsigned int numarat;
      unsigned int numit;
   public: // sec?iunea de descriere a membrilor publici
      fractie_rationala(); // antetul constructorului
      ~fractie_rationala(); // antetul destructorului
};
 
fractie_rationala::fractie_rationala() // realizarea
{                                    // constructorului
   cout << "Lucreaza constructorul\n";
}
 
fractie_rationala::~fractie_rationala() // realizarea 
{                                     // destructorului
   cout << "Lucreaza destructorul\n";
}
 
void main()
{
   {  // deschidem un bloc intern
      fractie_rationala fr1;
      fractie_rationala fr2=fractie_rationala();
 
      fr1 = fr2;
 
      fr2.~fractie_rationala(); // apelarea destructorului
                                // pentru fr2
   }  // ?nchidem blocul intern
 
   char c; 
   cin >> c;
}

 


Dup? cum observ?m, ?n clasa fractie_rationala au fost redefinite func?iile constructor ?i destructor implici?i. Acum ele afi?eaz? informa?ii c? sunt ?n ac?iune (special f?cut pentru exemplu). Toate ac?iunile cu obiecte ?n func?ia main() au fost incluse ?ntr-un bloc intern. Astfel, obiectele fr1 ?i fr2 au devenit locale pentru acest bloc ?i vor fi distruse la ie?irea din el. Rezultatul programului va fi afi?area urm?torului text:

Lucreaza constructorul
Lucreaza constructorul
Lucreaza destructorul
Lucreaza destructorul
Lucreaza destructorul

Analiz?m: din care cauz? va fi a?a? Imediat dup? intrarea ?n blocul interior al func?iei main() se creeaz? obiectul fr1 ?i, deci, va fi apelat constructorul (primul mesaj: Lucreaza constructorul).

Apoi, se creeaz? obiectul fr2 (al doilea mesaj Lucreaza constructorul). Mai departe se execut? atribuirea de la fr2 la fr1, care a fost deja discutat? mai sus.

Apoi, special (?n scopul demonstr?rii) se apeleaz? destructorul pentru obiectul fr2 (ca rezultat, apare primul mesaj: Lucreaza destructorul). Dup? aceasta va fi atins sf?r?itul blocului intern.

La ie?ire din bloc vor fi distruse obiectele fr1 ?i fr2, fiind locale pentru acest bloc. La distrugerea obiectelor, ?n mod automat, pentru fiecare dintre aceste dou? obiecte va fi apelat destructorul clasei fractie_rationala (?nc? dou? mesaje: Lucreaza destructorul).

C?teva observa?ii.

Urm?toarele dou? instruc?iuni:

fractie_rationala fr2;

?i:

fractie_rationala fr2=fractie_rationala();

sunt perfect echivalente. A doua instruc?iune arat? ?n mod evident apelul constructorului. Pe c?nd instruc?iunea:

fractie_rationala fr2;

nu este echivalent? cu instruc?iunea:

fr2=fractie_rationala();

fiindc? a doua atribuie obiectului deja creat (fr2) valoarea obiectului care se creeaz? temporar. La crearea obiectului temporar va fi apelat implicit constructorul, apoi valoarea obiectului temporar va fi atribuit? obiectului fr2, dup? aceasta va fi apelat destructorul pentru obiectul temporar, ?i, ?n consecin??, obiectul temporar va fi distrus, iar obiectul fr2 r?m?ne cu noua valoare.

?n ce ordine vor fi apela?i destructorii la ie?ire din blocul intern? Mai ?nt?i pentru fr1 sau pentru fr2? R?spunsul, a c?rui verificare r?m?ne ca exerci?iu, este urm?torul: destructorii vor fi apela?i ?n ordine opus? ordinii de creare a obiectelor. Mai ?nt?i a fost creat fr1 ?i apoi fr2. Deci, mai ?nt?i va fi apelat destructorul pentru obiectul fr2, dup? ce va fi apelat destructorul pentru fr1.

Atrage?i aten?ia ?nc? o dat? cum au fost apelate func?iile destructor ?n exemplele precedente. Acesta este modul general de apelare a func?iilor membre accesibile.

 

Modul general de apelare al func?iilor membre din afara clasei este:obiect.[nume_clas?::]nume_func?ie_membr?(list?_argumente)

Deci, indic?m numele obiectului, apoi operatorul de accesare la membrii (.), dup? aceea numele func?iei membre ?i lista argumentelor ?n paranteze. Atunci c?nd se acceseaz? o func?ie membr? dintr-o alt? func?ie membr? pentru unul ?i acela?i obiect, prefixul ?obiect.? se omite.

 

Modul general de apelare al func?iilor membre din interiorul clasei este:[nume_clas?::]nume_func?ie_membr?(list?_argumente)

_________________________
Autorul: dr.conf. S. Pereteatcu

3.4. Definirea ?i supra?nc?rcarea constructorului

3.4. Definirea ?i supra?nc?rcarea constructorului

Mai dezvolt?m clasa fractie_rationala, d?nd posibilitate constructorului de a ini?ializa c?mpurile numarat ?i numit. Vom lua ?n considera?ie c? numitorul nu poate lua valoarea 0:

#include "iostream.h"
#include "stdlib.h"
 
class fractie_rationala
{
   protected:
      unsigned int numarat;
      unsigned int numit;
   public:
      fractie_rationala(unsigned int, 
                        unsigned int);
   private:
      void eroare(char* mesaj);
};
 
fractie_rationala::fractie_rationala(unsigned int numarat_init,
                                     unsigned int numit_init)
{
   if(numit_init!=0)
   {
      numarat=numarat_init; // ini?ializarea num?r?torului
      numit=numit_init;     // ini?ializarea numitorului
   }
   else
      eroare("Numitorul este egal cu zero!");
}
 
void fractie_rationala::eroare(char* mesaj)
{
   // trimiterea mesajului ?n fluxul standard pentru mesaje de erori  
   cerr << mesaj;       
   exit(1);  // ie?ire for?at? din program  
}  
void main() {
    fractie_rationala fr1;       // aceast? instruc?iune este gre?it? 
    fractie_rationala fr2(2, 3); // crearea corect? a obiectului        
    fr1 = fr2;        
    char c;     
    cin >> c;
}


?n primul r?nd, observ?m c? s-a schimbat antetul constructorului. Acum constructorul accept? ca argumente dou? valori de tipul unsigned int. Valoarea parametrului numarat_init reprezint? valoarea de ini?ializare pentru num?r?tor (c?mpul numarat al clasei), iar valoarea parametrului numit_init reprezint? valoarea de ini?ializare pentru numitor (c?mpul numit al clasei).

Constructorul verific? ?nt?i de toate dac? valoarea parametrului numit_init este diferit? de zero. Numai dac? aceast? condi?ie are loc el ?nscrie valorile de ini?ializare ?n c?mpurile clasei. Cazul ?n care utilizatorul ?nt?mpl?tor a pus pe 0 valoarea parametrului numarat_init va fi prins de c?tre constructor. ?n acest caz, constructorul apeleaz? func?ia eroare() transmi??nd prin parametru mesajul respectiv.

Func?ia eroare() este func?ia membr? a clasei fractie_rationala ?i a fost introdus? pentru prelucrarea situa?iilor de eroare (precum este cazul descris mai sus). Antetul acestei func?ii a fost declarat ?n sec?iunea de declarare a membrilor priva?i (private). Aceasta ?nseamn? c? ea poate fi apelat? numai din alte func?ii membre ale clasei fractie_rationala sau din func?ii de tip prieten al acestei clase (?n exemplul dat clasa noastr? nu are a?a func?ii, func?iile prietene vor fi analizate ?n 4.3).

Atrage?i aten?ia ?nc? o dat? cum se apeleaz? func?ia eroare() ?n constructorul clasei fractie_rationala: doar numele func?iei, iar ?n paranteze – argumentul. A?a ea poate fi apelat? din orice alt? func?ie membr? a clasei fractie_rationala, fiindc? aici este cunoscut? ?i accesibil? (bine?n?eles, clasa dat? ?n exemplul respectiv nu are alte func?ii membre dec?t constructorul, destructorul implicit ?i eroare()).

Lua?i ?n considera?ie urm?torul moment. Descriind anteturile func?iilor membre ?n declara?ia clasei, putem indica numai tipurile parametrilor (cum este prototipul constructorului) sau ?i tipurile ?i denumirile parametrilor (cum este antetul func?iei eroare()). ?n exemplul nostru, special au fost ar?tate ambele posibilit??i. ?ns?, ?n anteturile la defini?iile func?iilor trebuie numaidec?t de scris ?i tipurile, ?i denumirile parametrilor. Mai mult dec?t at?t, la definirea func?iilor tipurile ?i denumirile parametrilor trebuie s? coincid? cu respectivele la descrierea prototipurilor acestor func?ii ?n declararea clasei. Ceea ce a ?i fost ar?tat ?n exemplul dat.

Memoriza?i bine cum se scriu anteturile func?iilor membre la realizarea lor. Linia:

fractie_rationala::fractie_rationala …

?nseamn? c? descriem constructorul (?nainte de :: denumirea clasei, dup? :: denumirea func?iei membr?, ?n cazul nostru – constructorul). Constructorul nu poate avea tip, ca ?i destructorul. Linia:

void fractie_rationala::eroare(char* mesaj)

?nseamn? c? descriem func?ia eroare() care este membr? a clasei fractie_rationala. Func?ia ?n cazul nostru are tipul void, adic? nu ?ntoarce nici o valoare.

?nc? un moment foarte important! Instruc?iunea:

fractie_rationala fr1;

care creeaz? obiectul fr1 ?n func?ia main() ?i care era corect? ?n exemplele precedente, ?n exemplul acesta va fi gre?it?. Cauza este urm?toarea: ?n clasa fractie_rationala este declarat cel pu?in un constructor; astfel, compilatorul nu mai creeaz? constructorul implicit (f?r? parametri). Instruc?iunea sus-numit? va fi ?n?eleas? de c?tre compilator ca o indica?ie pentru crearea obiectului fr1 cu apelarea simultan? a constructorului f?r? parametri. Dar, un astfel de constructor nu exist? ?n clasa fractie_rationala ?i compilatorul va afi?a un mesaj de eroare, ceva de tipul: „Could not find a match for fractie_rationala::fractie_rationala()”, ceea ce semnific? c? el nu g?se?te varianta constructorului f?r? parametri.

Sunt dou? ie?iri din aceast? situa?ie:

1) Modific?m exemplul de mai sus, ad?ug?nd ?n clasa fractie_rationala ?nc? un constructor, dar f?r? parametri. Pentru aceasta, ?n sec?iunea de descriere a membrilor deschi?i, dup? linia:

public:

ad?ug?m linia:

fractie_rationala();

iar dup? declararea clasei inser?m realizarea acestui constructor:

fractie_rationala::fractie_rationala()
{
   numarat=0; // ini?ializarea num?r?torului cu 0
   numit=1;   // ini?ializarea numitorului cu 1
}


Adic?, constructorul f?r? parametri ini?ializeaz? obiectul cu valoarea 0/1. ?n a?a mod am programat. Alt? solu?ie era de a ini?ializa obiectul cu valoarea 1/1, sau cu orice alt? valoare corect?. Acum instruc?iunea:

fractie_rationala fr1;

a devenit din nou corect?. Ea creeaz? obiectul fr1, ini?ializ?ndu-l cu valoarea 0/1 prin apelarea din cei doi constructori a constructorului f?r? parametri.

2) ?n loc s? introducem doi constructori, l?s?m numai unul, indic?nd valorile implicite pentru parametri. La definire modific?m doar antetul constructorului ?n felul urm?tor:

fractie_rationala::fractie_rationala(unsigned int numarat_init=0, 
                                     unsigned int numit_init=1)


Aceasta ?nseamn? c? la apelul constructorului putem da dou? valori, sau numai o valoare pentru primul argument (consim?ind la valoarea implicit? pentru al doilea), sau nici o valoare (consim?ind la cele implicite pentru ambele argumente). Adic?, obiectul poate fi creat a?a:

fractie_rationala fr1(3, 5); // valoarea ini?ial? este 3/5
sau a?a:
fractie_rationala fr1(2);    // valoarea ini?ial? este 2/1
sau a?a:
fractie_rationala fr1;       // valoarea ini?ial? este 0/1
Mai exist? ?nc? o posibilitate:
fractie_rationala fr1=2;     // valoarea ini?ial? este 2/1


Ultima variant? provine de la forma special?, ad?ug?toare, de apelare a constructorilor cu un singur parametru. Constructorul nostru are doi parametri, iar dac? pentru un obiect oarecare ne aranjeaz? valoarea implicit? a parametrului doi, putem indica numai primul argument sau astfel:

fractie_rationala fr(5);
sau:
fractie_rationala fr=5; // forma special? de apelare a
// constructorului cu un singur parametru


Aceste dou? instruc?iuni sunt echivalente.

Valorile implicite pentru fiecare parametru pot fi indicate numai ?ntr-un singur loc, sau ?n declararea clasei, ori la definirea constructorului. Valoarea implicit? pentru parametrul concret poate fi indicat? numai dac? sunt deja concretizate valorile implicite pentru to?i parametrii din partea dreapta a lui. Adic?, pentru constructorul nostru se admit numai urm?toarele perechi de anteturi – respectiv, ?n declararea clasei ?i ?n descrierea constructorului:

1)

fractie_rationala(unsigned int numarat_init, 
                  unsigned int numit_init);
cu
fractie_rationala::fractie_rationala(unsigned int numarat_init,
                                     unsigned int numit_init)


Nu au fost indicate valorile implicite pentru parametri;

2)

fractie_rationala(unsigned int numarat_init,
                  unsigned int numit_init=1);
cu
fractie_rationala::fractie_rationala(unsigned int numarat_init,
                                     unsigned int numit_init)


Valoarea implicit? a fost indicat? numai pentru parametrul doi ?n declararea clasei;

3)

fractie_rationala(unsigned int numarat_init,
                  unsigned int numit_init);
cu
fractie_rationala::fractie_rationala(unsigned int numarat_init,
                                     unsigned int numit_init=1)


Valoarea implicit? a fost indicat? numai pentru parametrul doi la descrierea constructorului;

4)

fractie_rationala(unsigned int numarat_init=0,
                  unsigned int numit_init=1);
cu
fractie_rationala::fractie_rationala(unsigned int numarat_init,
                                     unsigned int numit_init)


Valorile implicite au fost indicate pentru ambii parametri ?n declararea clasei;

5)

fractie_rationala(unsigned int numarat_init,
                  unsigned int numit_init);
cu
fractie_rationala::fractie_rationala(unsigned int numarat_init=0,
                                     unsigned int numit_init=1)


Valorile implicite au fost indicate pentru ambii parametri la descrierea constructorului;

6)

fractie_rationala(unsigned int numarat_init,
                  unsigned int numit_init=1);
cu
fractie_rationala::fractie_rationala(unsigned int numarat_init=0,
                                     unsigned int numit_init)


Valorile implicite au fost indicate pentru parametrul doi ?n declararea clasei ?i pentru primul parametru la definirea constructorului.

_________________________
Autorul: dr.conf. S. Pereteatcu

3.5. Constructor de copiere (atribuire)

3.5. Constructor de copiere (atribuire)

Dac? ?ntr-o clas? nu definim nici un constructor, compilatorul creeaz? implicit un constructor f?r? parametri. ?n afar? de acesta, compilatorul mai creeaz? implicit un constructor care se denume?te constructor de copiere, sau constructor de atribuire, sau constructor ini?ializator. Constructorul de copiere are un parametru – referin?? la un obiect surs? al aceleia?i clase. ?ndeplinirea constructorului de copiere const? ?n copierea pe bi?i a con?inutului argumentului (obiectului surs? deja cunoscut) ?n obiectul ce se creeaz?.

Existen?a constructorului implicit de copiere ?n clasa fractie_rationala() poate fi demonstrat? prin apelul explicit al acestuia. Inser?m ?n func?ia main() a exemplului precedent urm?torul cod:

fractie_rationala fr2(1, 3);

fractie_rationala fr3(fr2); // forma uzual? de aplicare a constructorului de copiere

// ?nc? o modalitate

fractie_rationala fr4=fractie_rationala(fr2);

Acest fragment va fi compilat cu succes. Aici constructorul de copiere a fost folosit la crearea obiectului fr3 ?n baza obiectului fr2, ?i la crearea obiectului fr4 la fel ?n baza obiectului fr2. Aten?ie! A doua modalitate este mai pu?in uzual?. Ea poate s? se comporte diferit, ?n dependen?? de compilator. Un compilator poate s? creeze un obiect nou direct cu ajutorul constructorului de copiere. Alt compilator creeaz? acest obiect ?n patru etape: creeaz? obiectul cu ajutorul constructorului f?r? argumente, apoi unul temporar cu ajutorul constructorului de copiere, dup? aceasta aplic? operatorul de atribuire de la obiectul temporar la obiectul nou creat, iar la sf?r?it distruge obiectul temporar.

?n exemplul precedent s-a folosit constructorul de copiere creat implicit de c?tre compilator. ?ns?, putem defini constructorul de copiere ?i explicit. Forma general? de definire explicit? a unui constructor de copiere este urm?toarea:

 

c?nd constructorul se define?te ?n declararea clasei[inline ][nume_clas?::]nume_clas?(

[const ]nume_clas? &nume_obiect_surs?)

{

   // codul constructorului de copiere

   …

}

sau antetul

[inline ][nume_clas?::]nume_clas?(

[const ]nume_clas? &[nume_obiect_surs?]);

?n declararea clasei, ?i defini?ia

[inline ]nume_clas?::nume_clas?(

[const ]nume_clas? &nume_obiect_surs?)

{

   // codul constructorului de copiere

   …

}

dup? declararea clasei.

Re?ine?i! Compilatorul nu creeaz? implicit constructorul f?r? parametri, dac? clasa dispune m?car de un singur constructor oarecare definit explicit. Constructorul de copiere nu se creeaz? implicit numai ?n cazul c?nd el este definit explicit.

Constructorul de copiere se folose?te explicit sau implicit ?n urm?toarele cazuri:

  • c?nd cre?m un obiect al unei clase ini?ializ?ndu-l cu un alt obiect deja existent al aceleia?i clase (exemple au fost aduse);
  • c?nd obiectul se folose?te la apelul unei func?ii ?n calitate de argument care se transmite prin valoare (copie). ?n acest caz, ?n func?ia respectiv? se creeaz? o copie a obiectului cu ajutorul constructorului de copiere (exemplele vor urma pe parcursul expunerii);
  • c?nd se creeaz? cu ajutorul constructorului de copiere un obiect temporar care poate fi folosit ?n expresii. Cu toate c? ?nc? nu am cercetat expresiile cu obiecte, imagina?i-v? urm?torul exemplu. ?n clasa fractie_rationala definim un constructor de copiere care nu copie pe bi?i, ci atribuie frac?ia redus? obiectului ce se creeaz?. Presupunem c? obiectul fr5 are valoarea 2/4, atunci expresia fractie_rationala(fr5) va avea valoarea 1/2. Re?ine?i acest moment, ?l ve?i cerceta de sine st?t?tor ?n viitorul apropiat. Pentru aceasta va trebui s? defini?i explicit ?n clasa fractie_rationala acest constructor de copiere.

_________________________
Autorul: dr.conf. S. Pereteatcu

Translate Переводчик

Подписка на новости

SmartResponder.ru
Ваш e-mail: *
Ваше имя: *

Хостинг для Wordpress сайтов