This shows you the differences between two versions of the page.
sd-ca:laboratoare:laborator-02 [2016/02/21 19:34] radu.stochitoiu |
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țe 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 | ||
+ | |||
+ | |||
+ | |||
+ | ==== Clase ==== | ||
+ | |||
+ | Formal am făcut deja primii pași mai sus pentru a implementa o clasă în C++, utilizând keyword-ul //struct//. | ||
+ | |||
+ | Totuși, ce înseamnă o clasă? Nu trebuie decât să ne gândim la ce am făcut mai sus: | ||
+ | *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> | ||
+ | |||
+ | <newcolumn> | ||
+ | |||
+ | <code c++ complex.cc> | ||
+ | #include "complex.h" | ||
+ | Complex Complex::conjugate() { | ||
+ | Complex conjugate; | ||
+ | conjugate.re = this->re; | ||
+ | conjugate.im = -(this->im); | ||
+ | |||
+ | return conjugate; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | <newcolumn> | ||
+ | |||
+ | <code c++ main.cc> | ||
+ | #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> | ||
+ | </columns> | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ==== Specificatori de acces ==== | ||
+ | Am observat mesajul de eroare în urma compilării fișierelor de mai sus. | ||
+ | |||
+ | 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: | ||
+ | double re; | ||
+ | double im; | ||
+ | |||
+ | Complex conjugate(); | ||
+ | }; | ||
+ | </code> | ||
+ | |||
+ | ==== Constructori și destructori ==== | ||
+ | |||
+ | Studiați codul de mai jos. | ||
+ | |||
+ | <columns 100% 100% -> | ||
+ | <code c++ complex.h> | ||
+ | class Complex { | ||
+ | public: | ||
+ | // Constructor | ||
+ | Complex(double re, double 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() { | ||
+ | Complex number(2, 3); | ||
+ | printf("%lf %lf\n", number.getRe(), number.getIm()); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </code> | ||
+ | </columns> | ||
+ | |||
+ | ==== Constructor ==== | ||
+ | |||
+ | Observăm două bucăți din cod în mod special: | ||
+ | <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. | ||
+ | |||
+ | Ce operații sunt uzuale în constructor? | ||
+ | *inițializarea membrilor clasei cu valori predefinite sau date ca parametru | ||
+ | *alocarea memoriei pentru anumiți membri | ||
+ | |||
+ | 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 this, direct în interiorul constructorului). | ||
+ | |||
+ | |||
+ | Î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 o 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ţi 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> | ||
+ | |||
+ | <note important>Operatorii >> și << întorc fluxul original, pentru a scrie înlănțuiri de tipul ''f>>ob1>>ob2''. </note> | ||
+ | |||
+ | 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. | ||
+ | |||
+ | <code c++ Complex.h> | ||
+ | #include <iostream> | ||
+ | |||
+ | class Complex | ||
+ | { | ||
+ | public: | ||
+ | double re; | ||
+ | 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); | ||
+ | |||
+ | //funcţii operator pentru supraîncărcarea operatorilor de intrare/ieşire | ||
+ | //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); | ||
+ | |||
+ | 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 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> | ||
+ | |||
+ | ==== Supraîncărcarea operatorului de atribuire ==== | ||
+ | |||
+ | Așa cum am amintit mai sus, majoritatea operatorilor pot fi supraîncărcați. O atenție importantă trebuie acordată operatorului de atribuire, dacă nu este supraîncărcat, realizează o copiere membru cu membru. | ||
+ | |||
+ | 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. | ||
+ | |||
+ | <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 | ||
+ | } | ||
+ | |||
+ | String& String::operator=(const char* p){ | ||
+ | if(s) | ||
+ | delete [] s; | ||
+ | n=strlen(p); | ||
+ | s=new char[n+1]; | ||
+ | strcpy(s, p); | ||
+ | return *this; | ||
+ | } | ||
+ | |||
+ | </code> | ||
Line 16: | 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 33: | 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 44: | 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 61: | 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++? |