Differences

This shows you the differences between two versions of the page.

Link to this comparison view

sd-ca:laboratoare:laborator-02 [2016/02/21 19:33]
darius.neatu
sd-ca:laboratoare:laborator-02 [2016/02/21 19:51] (current)
radu.stochitoiu
Line 3: Line 3:
 ===== Obiective ===== ===== Obiective =====
  
-În urma parcurgerii acestui ​laborator ​studentul va: +În urma parcurgerii acestui ​articol ​studentul va: 
  
-  * înțelege conceptul de template +  * învăța ce înseamnă o clasă 
-  * înțelege conceptul de referințdin C++ +  * învăța ce înseamnă constructor / destructor 
-  * înțelege conceptul de read-only introdus prin identificatorul const+  * afla funcționalitățile claselor / funcțiilor prietene 
 +  * realiza supraîncărcarea operatorilor ​din C++ 
 +  * înțelege conceptul de copy constructor 
 +  * înțelege conceptul de rule of three
  
-===== Templates ===== 
  
-Motivul principal pentru care folosim C++ în cadrul SD este datorită funcționalității oferite de template-uri. 
  
-Acestea permit generalizarea tipurilor de date folosite în interiorul funcțiilor și claselor.+==== Clase ====
  
-Sintaxa ​pentru ​acestea este+Formal am făcut deja primii pași mai sus pentru ​a implementa o clasă în C++, utilizând keyword-ul //​struct//​. 
-<code c++> + 
-template <class identifier>​ declaratie+Totuși, ce înseamnă o clasă? Nu trebuie decât să ne gândim la ce am făcut mai sus: 
-template <​typename identifier>​ declaratie;+  *am definit un tip de date 
 +  *i-am adăugat atribute (am definit ce proprietăți îl caracterizează:​ partea reală și partea imaginară) 
 +  *i-am adăugat metode (am definit cum se comportă: inițializarea și conjugarea) 
 + 
 +Cu această adăugare menționată,​ putem să ne referim la ceea ce înseamnă o **clasă**, respectiv un **obiect**. 
 + 
 +Ne referim la o **clasă** ca fiind o amprentă (blueprint) sau descriere generală. 
 +Un **obiect** sau o **instanță a clasei** este o variabilă concretă ce se conformează descrierii clasei. 
 + 
 +Vom numi **clasă** tipul de date definit de //struct complex// sau //class complex// și **obiect** o instanțiere (o alocare dinamică sau locală) a tipului de date. 
 + 
 +Când discutăm despre tipul de date //complex// ne referim la clasă. 
 +Când discutăm despre variabila //number// ne referim la un obiect, o instanță a clasei. 
 + 
 +==== Keyword-ul "​class"​ vs. "​struct"​ ==== 
 + 
 +Și totuși, C++ adăugă keyword-ul //class//. Care este diferența între //class// și //​struct//?​ 
 +Iată cum definim complet clasa de mai sus, separând antetul de implementare și de programul principal. 
 + 
 +<columns 100% 100% -> 
 +<code c++ complex.h
 +class Complex { 
 +    double re
 +    ​double im; 
 +     
 +    Complex conjugate();​  
 +};
 </​code>​ </​code>​
-//​declaratie//​ poate fi fie o funcție, fie o clasă. 
-Nu există nicio diferență între keyword-ul //class// și //​typename//​ - important este că ceea ce urmează după ele este un placeholder pentru un tip de date. 
  
-==== Function Template ==== +<​newcolumn>​
-În primul rând template-urile pot fi aplicate funcțiilor.+
  
-Un exemplu comun și simplu este următorul:​ +<code c++ complex.cc
-<code c++> +#include "​complex.h"​ 
-template<​typename T> +Complex Complex::​conjugate() { 
-T getMax(T a, T b) { +    ​Complex conjugate;​ 
-    ​return a b ? a : b;+    conjugate.re = this->re; 
 +    conjugate.im = -(this->​im);​ 
 +     
 +    return conjugate;
 } }
 </​code>​ </​code>​
  
-Funcția poate fi apelată astfel: +<​newcolumn>​ 
-<code c++> + 
-getMax<int>(2, 3); +<code c++ main.cc
-getMax<​double>​(3.24.6);+#​include ​<stdio.h> 
 +#include "​complex.h"​ 
 + 
 +int main() 
 +    Complex number
 +    ​number.re = 2
 +    number.im = 4
 +     
 +    printf("​%.2lf %.2lf\n",​ number.re, number.im); 
 +     
 +    return 0; 
 +}
 </​code>​ </​code>​
 +</​columns>​
  
-==== Class Template ==== 
-Concret, să presupunem că avem o clasă numită KeyStorage care are: 
-  *o cheie (de tip int) 
-  *un membru de date generic (al cărui tip de date nu îl știm la momentul scrierii clasei). 
  
-Vrem să putem folosi codul clasei indiferent de tipul de date al membrului. 
  
-Iată cum putem face acest lucru+ 
-<code c++ KeyStorage.h+==== Specificatori de acces ==== 
-template<​typename T+Am observat mesajul de eroare în urma compilării fișierelor de mai sus. 
-class KeyStorage ​{+ 
 +Astfel, **singura diferență** folosirea celor două keyword-uri este nivelul implicit de vizibilitate a metodelor și atributelor. 
 +  ***private** - pentru clasele declarate cu **class** 
 +  ***public** ​ - pentru clasele declarate cu **struct** 
 + 
 +Membri precedați de label-ul **private** pot fi folosiți numai în interiorul clasei, în cadrul metodelor acesteia. 
 +Ei nu pot fi citiți sau modificați din afara clasei. 
 + 
 +Iată cum puteam remedia soluția
 +<code c++ complex.h> 
 +class Complex ​{
 public: public:
-    ​int key+    ​double re
-    ​T member;+    ​double im; 
 + 
 +    Complex conjugate();
 }; };
 </​code>​ </​code>​
  
-În funcția mainsă presupunem ​că vrem să folosim clasa cu membrul de tip long+==== Constructori și destructori ==== 
-<code c++ main.cpp+ 
-#include "KeyStorage.h"+Studiați codul de mai jos. 
 + 
 +<columns 100% 100% -> 
 +<code c++ complex.h>​ 
 +class Complex { 
 +public: 
 +    // Constructor 
 +    Complex(double redouble im); 
 +     
 +    // Destructor 
 +    ~Complex();​ 
 +     
 +    double getRe(); 
 +    double getIm(); 
 +     
 +    Complex conjugate();​  
 + 
 +private: 
 +    double re; 
 +    double im; 
 +}; 
 +</​code>​ 
 + 
 +<​newcolumn>​ 
 + 
 +<​code ​c++ complex.cc> 
 +#include "​complex.h"​ 
 +Complex::​Complex(double re, double im) { 
 +    this->re = re; 
 +    this->im = im; 
 +
 + 
 +Complex::​~Complex() { 
 +
 + 
 +Complex Complex::​conjugate() { 
 +    Complex conjugat(re,​ -im); 
 +    return conjugat; 
 +
 + 
 +double Complex::​getRe() { 
 +    return re; 
 +
 + 
 +double Complex::​getIm() { 
 +    return im; 
 +
 +</​code>​ 
 + 
 +</​columns>​ 
 + 
 +<code c++ main.cc> 
 +#include <stdio.h
 +#include "complex.h"
  
 int main() { int main() {
-    ​KeyStorage<​long>​ keyElement;+    ​Complex number(2, 3); 
 +    printf("​%lf %lf\n",​ number.getRe(),​ number.getIm());​ 
 +    ​
     return 0;     return 0;
 } }
 </​code>​ </​code>​
 +</​columns>​
  
-Practic, oriunde folosim tipul de date T în clasă, este înlocuit cu tipul pe care îl specificăm.+==== Constructor ====
  
-==== Where'​s the magic happening? ==== +Observăm două bucăți din cod în mod special: 
-Sunt destul ​de multe lucruri ​de spus despre template-uridar ne vom concentra pe lucrurile ​care schimbă modul în care ați implementat până acum.+<code c++> 
 +Complex::​Complex(double re, double im); 
 +</​code>​ 
 +Linia de mai sus **nu are tip** returnat, spre deosebire ​de celelalte linii. 
 +Acesta este **constructorul** clasei, care este apelat ​în momentul alocării unui obiect.
  
-Template-urile ​sunt de fapt indicii pentru compilator pentru a genera cod la rândul lui! +Ce operații ​sunt uzuale în constructor?​ 
-Practic, voi îi spuneți compilatorului un șablon generic pe care ați vrea să-l folosiți și el trebuie să fie pregătit să îl pună la dispoziția voastră când aveți nevoie.+  *inițializarea membrilor clasei cu valori predefinite sau date ca parametru 
 +  *alocarea memoriei pentru anumiți membri
  
-Ce trebuie să rețineți din asta? Totul se întâmplă ​la **compile time**nu la run time.+A doua bucată observată este: 
 +<code c++> 
 +Complex numar(2, 3); 
 +</​code>​ 
 +Până acum nu ați mai alocat astfel structurile. Ce se întâmplă ​în spate este exact ceea ce intuiți: este apelat constructorul obiectului și se execută instrucțiunile acestuia pentru variabila numar (reprezentată ca pointer prin thisdirect în interiorul constructorului).
  
-Compilatorul practic analizează modul în care voi folosiți clasa respectivă și generează pentru fiecare ​mod în care o folosiți șablonul corespunzător+ 
-Folosirea KeyStorage<int> și KeyStorage<floatdetermină compilatorul ​să genereze cod pentru ​ambele clase (înlocuind ​dată cu int șaltă cu float).+În constructorul definit mai sus, tot ceea ce se întâmplă este să se inițializeze membri. Pentru asta, C++ vă pune la dispoziție o sintaxă simplă: 
 +<code c++> 
 +Complex::​Complex(double real, double imaginar) : 
 +    re(real), 
 +    im(imaginar) { 
 +
 +</​code>​ 
 + 
 +Cei doi constructori sunt identici ca funcționalitate. 
 + 
 +==== Copy-constructor ==== 
 + 
 +Reprezintă un tip de constructor special ​care se folosește când se dorește/​este necesară o copie a unui obiect existent. Dacă nu este declarat, se va genera unul default de către compilator. 
 + 
 +Poate avea unul din următoarele prototipuri 
 + 
 +   * MyClass(const MyClass&​ obj); 
 +   * MyClass(MyClass&​ obj); 
 + 
 +=== Când se apelează? === 
 + 
 +1) Apel explicit 
 + 
 +<code c++ explicit_copy_constructor_call.cpp>​ 
 +MyClass m; 
 +MyClass x = MyClass(m); /* apel explicit al copy-constructor-ului */ 
 +</​code>​ 
 + 
 +2) Transfer prin valoare ca argument într-o funcție 
 + 
 +<code c++ call_by_value.cpp>​ 
 +void f(MyClass obj); 
 +... 
 +MyClass o; 
 +f(o); /* se apelează copy-constructor */ 
 +</​code>​ 
 + 
 +3) Transfer prin valoare ca return al unei funcții 
 + 
 +<code c++ return_by_value.cpp>​ 
 +MyClass f() 
 +
 +    MyClass a; 
 +    return a; /* se apelează copy-constructor */ 
 +
 +</​code>​ 
 + 
 +4) La inițializarea unei variabile declarate pe aceeași linie 
 + 
 +<code c++ init.cpp>​ 
 +MyClass m; 
 +MyClass x = m; /* se apelează copy-constructor */ 
 +</​code>​ 
 + 
 + 
 +==== Destructor ==== 
 +Așa cum probabil ați observat, **constructorul** este apelat în mod **explicit** de către ​voi. **Destructorul** însă, în cazul de mai sus, este apelat **implicit** la terminarea blocului care realizează dealocărea automată a obiectului. 
 + 
 +Un destructor nu are parametri și se declară în interiorul clasei astfel: 
 +<code c++> 
 +~Complex();​ 
 +</​code>​ 
 + 
 +Dacă în constructor sau în interiorul clasei ați fi alocat memorie, cel mai probabil în destructor ați fi făcut curat și ați fi apelat free pe membrul ​respectiv
 + 
 +==== Rule of Three ==== 
 + 
 +Reprezintă un concept de ** must do** pentru C++. Astfel: 
 + 
 +<note important>​Dacă programatorul ​și-a declarat/​definit unul dintre ** constructor default**, ** operator de assignment** sau ** copy-constructor**,​ trebuie să îi declare/​definească și pe ceilalți 2</​note>​ 
 + 
 +Explicație:​ dacă funcționalitatea vreunuia dintre cei 3 se vrea mai specială decât cea oferită default, atunci mai mult ca sigur se dorește schimbarea funcționalității default și pentru ​ceilalți 2 rămași. 
 + 
 +<code c++ rule_of_3.cpp>​ 
 +class Complex 
 +
 +    private: 
 +        int re; 
 +        int im; 
 +    public: 
 +        Complex() 
 +        { 
 +            re = 0; 
 +            im = 0; 
 +            printf("​constructor default\n"​);​ 
 +        } 
 + 
 +        Complex(const Complex&​ c) 
 +        { 
 +            re = c.re; 
 +            im = c.im; 
 +            printf("​copy contructor\n"​);​ 
 +        } 
 + 
 +        void operator=(const Complex&​ c) 
 +        { 
 +            re = c.re; 
 +            im = c.im; 
 +            printf("​assignment operator\n"​);​ 
 +        } 
 +}; 
 +</​code>​ 
 + 
 +=====  Clase/​metode prietene ​ ===== 
 + 
 +Așa cum am văzut în primul articol, ​fiecare ​membru al clasei poate avea 3 specificatori de acces: 
 +  * public 
 +  * private 
 +  * protected 
 + 
 +Alegerea specificatorilor se face în special în funcție de ce funcționalitate vrem să exportăm din clasa respectivă. 
 + 
 +Dacă vrem să accesăm datele private/​protejate din afara clasei, avem următoarele opțiuni: 
 +  * Funcții ​care ne întorc/​setează valorile membre 
 +  * Funcții/​Clase prietene (friend) cu clasa curentă.  
 + 
 +O funcție prieten are următoarele proprietăți
 +  * O funcţie este considerată prietenă al unei clase, dacă în declararea clasei, este declarată funcţia respectivă precedată de specificatorul **friend** 
 +  * Declararea unei funcţii prieten poate fi făcută în orice parte a clasei(publică,​ privată sau protejată)
 +  * Definiţia funcţiei prieten se face global, în afara clasei. 
 +  * Funcția declarată ca **friend** are acces liber la orice membru din interiorul clasei. 
 + 
 + 
 +O clasă prieten are următoarele proprietăți:​ 
 +  * O clasă B este considerată prieten al unei clase A, dacă în declararea clasei A s-a întâlnit expresia: ''​friend class B''​ 
 +  * Clasa B poate accesa orice membru din clasa A, fără nici o restricție. 
 + 
 +De asemenea, dacă clasa A este considerată prieten cu clasa B, nu înseamnă că si clasa B este considerată prieten cu clasa A. Nici tranzitivitatea nu este valabilă în relaţia de prietenie dintre clase. 
 + 
 +Exemplu: 
 + 
 +<code cpp> 
 +class Complex{ 
 + 
 +private: 
 +    int re; 
 +    int im; 
 +public: 
 +    int GetRe(); 
 +    int GetIm(); 
 +    friend double ComplexModul(Complex c);   //am declarat fct ComplexModul ca prieten 
 +    friend class Polinom; ​  //​Acum clasa Polinom care acces deplin la membrii **re** ​și **im** 
 +}; 
 + 
 +double ComplexModul(Complex c) 
 +
 +   ​return sqrt(c.re*c.re+c.im*c.im); ​ //are voie, intrucat e prietena 
 +
 + 
 +</code> 
 + 
 +===== Supraîncarcarea operatorilor ===== 
 + 
 +Un mecanism specific C++ este supraîncarcarea operatorilor,​ prin care programatorul poate asocia noi semnificaţii operatorilor deja existenţi. De exemplu, dacă dorim ca două numere complexe ​să fie adunate, în C trebuie să scriem funcții specifice, nenaturale. În C++ putem scrie foarte ușor: 
 + 
 +<code c> 
 +Complex a(2,3); 
 +Complex b(4,5); 
 +Complex c=a+b; //​operatorul + a fost supraîncarcat ​pentru ​a aduna două numere complexe 
 +</​code>​ 
 + 
 +Acest lucru este posibil, întrucât un operator este văzut ca o funcție, cu declarația:​ 
 + 
 +     ​tip_rezultat operator#(listă_argumente);​ 
 + 
 +Așadar pentru a supraîncărca un operator pentru ​anumită clasă, este necesar să declarăm funcția următoare în corpul acesteia: 
 +     ​tip_rezultat operator#​(listă_argumente);​ 
 + 
 + 
 +Există câteva restricții ​cu privire la supraîncarcare:​ 
 + 
 +  * Nu pot fi supraîncărcaţoperatorii: ::, ., .*, ?:, sizeof. 
 +  * Setul de operatori ai limbajul C++ nu poate fi extins prin asocierea de semnificaţii noi unor caractere, care nu sunt operatori, de exemplu nu putem defini operatorul === . 
 +  * Prin supraîncărcarea unui operator nu i se poate modifica aritatea (astfel operatorul ! este unar şi poate fi redefinit numai ca operator unar).  
 +  * Asociativitatea şi precedenţa operatorului se menţin. 
 +  * La supraîncărcarea unui operator nu se pot specifica argumente ​cu valori implicite. 
 + 
 + 
 + 
 +==== Operatori supraîncărcaţi ca funcţii prieten ==== 
 + 
 + 
 + 
 +Un operator binar va fi reprezentat printr-o funcţie nemembră cu două argumente, iar un operator unar, printr-o funcţie nemembră cu un singur argument. 
 + 
 +Utilizarea unui operator binar sub forma **a#b** este interpretată ca **operator#​(a,​b)**. 
 + 
 +Argumentele sunt clase sau referinţe constante la clase.  
 + 
 + 
 +==== Supraîncărcarea operatorilor << şi >> ==== 
 + 
 +În C++, orice dispozitiv de I/O este văzut drept un stream, așadar operațiile de I/O sunt operații cu stream-uri, care se definesc în felul următor: 
 +  * **Citire**: se execută cu operatorul de extracție >>, membru al clasei istream 
 +  * **Scriere**:​ se execută cu operatorul de inserție <<, membru al clasei ostream 
 + 
 +Acești operatori pot fi supraîncărcați pentru o clasă pentru a defini operații de I/O direct pe obiectele clasei.  
 + 
 +Supraîncărcarea se poate efectua folosind funcții friend utilizând următoarea sintaxă: 
 + 
 +<code c> 
 +istream&​ operator>>​ (istream&​ f, clasa & ob);        //Acum pot scrie in >> ob 
 +ostream&​ operator<<​ (ostream&​ f, const clasa & ob);  //Acum pot scrie out << ob 
 +</​code>​
    
-==== Guideline-uri implementare ==== +<note important>​Operatorii >> și << întorc fluxul originalpentru a scrie înlănțuiri de tipul ''​f>>​ob1>>​ob2''​. </note>
-Pentru că totul se întâmplă la compile timeînseamnă că în momentul în care compilatorul întâlnește secvențde cod ce folosește template-uri trebuie să știe //toate// modurile în care aceasta este folosita.+
  
-Asta înseamnă că: +Funcţiile operator pentru supraîncărcarea operatorilor de I/O le vom declara ca funcţii prieten al clasei ​care interacţionează cu fluxul.
-  *Trebuie să scrieți întreaga implementare în header! ​//sau// +
-  *Scrieți descrierea ​clasei ​generice în header, în fișierul de implementare fiecare metodă declarată este de fapt o funcție ​cu template și la sfârșitul implementării adăugat //template class numeclasa<​numetip>//;​+
  
-Ultimul rând de fapt forțează folosirea template-ului cu un anumit tip de date și deci compilatorul generează cod corespunzător (trebuie să scrieți asta pentru toate tipurile).+<code c++ Complex.h>   
 +#include <​iostream>​
  
-==== Clasa KeyStorage ==== +class Complex 
-Iată mai jos o structură mai dezvoltată pentru clasa KeyStorage, în care cheia este setată în constructor. +{
-+
-<code c++ KeyStorage.h>​ +
-template<​typename T> +
-class KeyStorage ​{+
 public: public:
-    ​KeyStorage(int k); +    ​double re; 
-    ​~KeyStorage();+    double im; 
 +  
 +    Complex(double real=0, double imag=0): re(real), im(imag) {}; 
 +  
 +    //​supraîncărcarea ​ operatorilor +, - ca functii de tip "​friend"​  
 +    friend Complex operator+(const Complex&​ s, const Complex&​ d); 
 +    ​friend Complex operator-(const Complex&​ s, const Complex&​ d);
     ​     ​
-    ​T getMember(); +    ​//funcţii operator pentru supraîncărcarea operatorilor de intrare/​ieşire ​  
-    ​T setMember(T element);+    //declarate ca funcţii de tip "​friend" ​   
 +    friend std::​ostream&​ operator<<​ (std::​ostream&​ out, const Complex&​ z); 
 +    friend std::​istream&​ operator>>​ (std::​istream&​ is, Complex&​ z); 
 +}; 
 +</​code>​ 
 + 
 +<code c++ Complex.cpp> ​  
 +#include "​complex.h"​ 
 + 
 +Complex operator+(const Complex&​ s, const Complex&​ d){ 
 +  return Complex(s.re+d.re,​s.im+d.im);​ 
 +
 +  
 +Complex operator-(const Complex&​ s, const Complex&​ d){ 
 +  return Complex(s.re-d.re,​s.im-d.im);​ 
 +
 +  
 +std::​ostream&​ operator<<​(std::​ostream&​ out, const Complex&​ z){ 
 +   out << "​("​ << z.re << ","​ << z.im << "​)"<<​ std::​endl;​ 
 +   ​return out; 
 +
 +  
 +std::​istream&​ operator>>​(std::​istream&​ is, Complex&​ z){ 
 +  is >> z.re >> z.im; 
 +  return is; 
 +
 +</​code>​ 
 + 
 + 
 +<code c++ main.cpp>​ 
 +#include "​complex.h"​ 
 + 
 +int main() { 
 + ​Complex a(1,1), b(-1,2); 
 + ​std::​cout << "A: " << a << "B: " << b; 
 + ​std::​cout << "A+B: " << (a+b); 
 + ​std::​cin >> b; 
 + ​std::​cout << "B: " << b; 
 + ​a=b;​ 
 + ​std::​cout << "A: " << a << "B: " << b; 
 +}</​code>​ 
 + 
 +==== Operatori supraîncărcaţi ca funcţii membre ==== 
 + 
 +Funcţiilor membru li se transmite un argument implicit **this** (adresa obiectului curent), motiv pentru care un operator binar poate fi implementat printr-o funcţie membru nestatică cu un singur argument. ​  
 + 
 +Operatorii sunt interpretați în modul următor: 
 +  * Operatorul binar **a#b** este interpretat ca **a.operator#​(b)** 
 +  * Operatorul unar prefixat **#a** este interpretat ca **a.operator#​()** 
 +  * Operatorul unar postfixat **a#** este interpretat ca **a.operator#​(int)** 
 + 
 +<code c++ Complex.h>​ 
 +#include <​iostream>​ 
 + 
 +class Complex 
 +
 +public: 
 +    double re; 
 +    double im; 
 +  
 +    Complex(double real, double imag): re(real), im(imag) {}; 
 +  
 +    //operatori supraîncărcaţi ca funcţii membre 
 +    Complex operator+(const Complex&​ d); 
 +    Complex operator-(const Complex&​ d); 
 +    ​Complex&​ operator+=(const Complex&​ d);
     ​     ​
-private: +    friend std::​ostream&​ operator<<​ (std::​ostream&​ out, const Complex&​ z)
-    T member+    ​friend std::​istream&​ operator>>​ (std::​istream&​ is, Complex&​ z);
-    ​int key;+
 }; };
 +</​code>​
  
 +<code c++ Complex.cpp>​
 +#include "​complex.h"​
 +
 +Complex Complex::​operator+(const Complex&​ d){
 +  return Complex(re+d.re,​ im+d.im);
 +}
 + 
 +Complex Complex::​operator-(const Complex&​ d){
 +  return Complex(re-d.re,​ im-d.im);
 +}
 + 
 +Complex&​ Complex::​operator+=(const Complex&​ d){
 +  re+=d.re;
 +  im+=d.im;
 +  return *this;
 +}
 + 
 +std::​ostream&​ operator<<​(std::​ostream&​ out, const Complex&​ z){
 +   out << "​("​ << z.re << ","​ << z.im << "​)"<<​ std::endl;
 +   ​return out;
 +}
 + 
 +std::​istream&​ operator>>​(std::​istream&​ is, Complex&​ z){
 +  is >> z.re >> z.im;
 +  return is;
 +}
 </​code>​ </​code>​
  
-Implementarea completa a ei poate fi realizată+==== Supraîncărcarea operatorului ​de atribuire ====
-  *în header (în cazul template-urilor,​ acest mod este cel mai indicat). +
-  *în fișierul ​de implementare .cc / .cpp (al cărui schelet parțial îl găsiți mai jos).+
  
-<code c++ KeyStorage.cpp> +Așa cum am amintit mai sus, majoritatea operatorilor pot fi supraîncărcațiO atenție importantă trebuie acordată operatorului de atribuire, dacă nu este supraîncărcat,​ realizează o copiere membru cu membru.
-#include "​KeyStorage.h"+
  
-template<typename T+Pentru obiectele care nu conţin date alocate dinamic la iniţializare,​ atribuirea prin copiere membru cu membru funcţionează corect, motiv pentru care nu se supraîncarcă operatorul de atribuire. 
-KeyStorage<T>::KeyStorage(int k) { + 
-//TODO+<note important>​Pentru clasele ce conţin date alocate dinamic, copierea membru cu membru, executată în mod implicit la atribuire conduce la copierea pointerilor la datele alocate dinamic, în loc de a copia datele.</​note
 + 
 +Operatorul de atribuire poate fi redefinit numai ca funcţie membră, el fiind legat de obiectul din stânga operatorului =, motiv pentru care va întoarce o referinţă la obiect. 
 + 
 +<code c++ String.h> 
 +class String{ 
 +  char* s; 
 +  int n; // lungimea sirului 
 + 
 +  public: 
 + String();​  
 + String(const char* p);  
 + String(const String& r); 
 + ~String();​  
 + String&​ operator=(const String& d); 
 + String&​ operator=(const char* p); 
 +}; 
 + 
 + 
 +</​code>​ 
 + 
 +<code c++ String.cpp>​ 
 +#include "​String.h"​ 
 +#include <​string.h>​ 
 + 
 +String& String::operator=(const String& d){ 
 +  ​if(this != &​d){ ​   //evitare autoatribuire 
 +    if(s)            //​curatire 
 +      delete [] s; 
 +    n=d.n; ​          //​copiere 
 +    s=new char[n+1];​ 
 +    strcpy(s, d.s); 
 +  } 
 +  return *this; ​     ​//intoarce referinta la obiectul modificat
 } }
  
-template<​typename T> +String& String::operator=(const char* p){ 
-KeyStorage<​T>​::~KeyStorage() {+  if(s) 
 +      delete [] s; 
 +  n=strlen(p);​ 
 +  s=new char[n+1];​ 
 +  strcpy(s, p); 
 +  return *this;
 } }
  
-//TODO: restul metodelor.+</code>
  
-// La sfarsit, cu tipurile de date pe care le veti folosi. 
-template class KeyStorage<​int>;​ 
-template class KeyStorage<​long>;​ 
  
-</​code>​ 
  
 <​hidden>​ <​hidden>​
Line 138: Line 574:
     *[**2p**] Setati tipul de return a metodelor de tip getter astfel incat sa puteti modifica valorea intoarsa.     *[**2p**] Setati tipul de return a metodelor de tip getter astfel incat sa puteti modifica valorea intoarsa.
     *[**1p**] Arătați funcționalitatea prin adăugarea de cod în fișierul main.cpp. Rezolvați, dacă e cazul, toate erorile/​leak-urile depistate de Valgrind.     *[**1p**] Arătați funcționalitatea prin adăugarea de cod în fișierul main.cpp. Rezolvați, dacă e cazul, toate erorile/​leak-urile depistate de Valgrind.
-</​hidden>​ 
  
   *[**5p**] Clasa Complex - clasă ce implementează conceptul de număr complex   *[**5p**] Clasa Complex - clasă ce implementează conceptul de număr complex
Line 155: Line 590:
     *[**2p**] Alocați o instanță de tip MappingEntry local și dinamic (utilizând new / delete).     *[**2p**] Alocați o instanță de tip MappingEntry local și dinamic (utilizând new / delete).
     *[**1p**] Arătați funcționalitatea prin adăugarea de cod în fișierul main.cpp. Rezolvați, dacă e cazul, toate erorile/​leak-urile depistate de Valgrind.     *[**1p**] Arătați funcționalitatea prin adăugarea de cod în fișierul main.cpp. Rezolvați, dacă e cazul, toate erorile/​leak-urile depistate de Valgrind.
-<​hidden>​+
   *[**5p**] Clasa Punct2D - clasă ce implementează conceptul de punct în plan   *[**5p**] Clasa Punct2D - clasă ce implementează conceptul de punct în plan
     *[**2p**] Implementați și folosiți utilizând template-uri clasa Punct2D, adăugând constructor și destructor.     *[**2p**] Implementați și folosiți utilizând template-uri clasa Punct2D, adăugând constructor și destructor.
Line 166: Line 601:
  
  
-</​hidden>​+
  
   *[**5p**] Clasa Punct2D - clasă ce implementează conceptul de punct în plan   *[**5p**] Clasa Punct2D - clasă ce implementează conceptul de punct în plan
Line 183: Line 618:
     *[**1p**] Arătați funcționalitatea prin adăugarea de cod în fișierul main.cpp. Rezolvați, dacă e cazul, toate erorile/​leak-urile depistate de Valgrind.     *[**1p**] Arătați funcționalitatea prin adăugarea de cod în fișierul main.cpp. Rezolvați, dacă e cazul, toate erorile/​leak-urile depistate de Valgrind.
  
-<​hidden>​+
 ===== Interviu ===== ===== Interviu =====
  
-Această secțiune nu este punctată și încearcă să vă facă o oarecare idee a tipurilor de întrebări pe care le puteți întâlni la un job interview (internship,​ part-time, full-time, etc.) din materia prezentată în cadrul ​laboratorului.+Această secțiune nu este punctată și încearcă să vă facă o oarecare idee a tipurilor de întrebări pe care le puteți întâlni la un job interview (internship,​ part-time, full-time, etc.) din materia prezentată în cadrul ​articolului.
  
   * Care este diferența între struct și class în C++?   * Care este diferența între struct și class în C++?
sd-ca/laboratoare/laborator-02.1456076011.txt.gz · Last modified: 2016/02/21 19:33 by darius.neatu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0