This shows you the differences between two versions of the page.
sd-ca:laboratoare:laborator-02 [2016/02/21 19:37] 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 | ||
- | *Învățăm ce înseamnă constructor / destructor | + | |
+ | |||
+ | ==== 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 ==== | ==== Constructori și destructori ==== | ||
Line 106: | Line 198: | ||
Cei doi constructori sunt identici ca funcționalitate. | 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 ==== | ==== Destructor ==== | ||
Line 117: | Line 254: | ||
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. | 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 ==== | ||
- | <hidden> | + | Reprezintă un concept de ** must do** pentru C++. Astfel: |
- | *[**5p**] Clasa MappingEntry - conține 2 membri de tipuri potențial diferite și realizează, din punct de vedere conceptual, asocierea între două valori (una se numește //cheie//, iar cealaltă //valoare//). | + | |
- | *[**2p**] Implementați și folosiți utilizând template-uri clasa MappingEntry de mai sus adăugând constructor, destructor si 2 metode de tip getter pentru cele doua valori private. | + | |
- | *[**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. | + | |
- | </hidden> | + | |
- | *[**5p**] Clasa Complex - clasă ce implementează conceptul de număr complex | + | <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> |
- | *[**2p**] Implementați și folosiți utilizând template-uri clasa Complex, adăugând constructor și destructor. | + | |
- | *[**2p**] Adăugați clasei Complex metode pentru adunare, scădere și înmulțire cu un alt număr complex. | + | |
- | *[**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. | + | |
- | *[**3p**] Simple use of const | + | 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. |
- | *[**0.5p**] Creati un pointer variabil la o variabila de tip intreg constanta (in 2 moduri). | + | |
- | *[**0.5p**] Creati un pointer constant la o variabila de tip intreg non-constanta. | + | |
- | *[**0.5p**] Creati un pointer constant la o variabila de tip intreg constanta. | + | |
- | *[**1.5p**] Initializati pointerul si variabila referita pentru fiecare caz. Explicati si rezolvati erorile de compilare. | + | |
- | *[**5p**] Clasa MappingEntry - conține 2 membri de tipuri potențial diferite și realizează, din punct de vedere conceptual, asocierea între două valori (una se numește //cheie//, iar cealaltă //valoare//). | + | <code c++ rule_of_3.cpp> |
- | *[**2p**] Implementați și folosiți utilizând template-uri clasa MappingEntry de mai sus adăugând constructor și destructor. | + | class Complex |
- | *[**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. | + | private: |
- | <hidden> | + | int re; |
- | *[**5p**] Clasa Punct2D - clasă ce implementează conceptul de punct în plan | + | int im; |
- | *[**2p**] Implementați și folosiți utilizând template-uri clasa Punct2D, adăugând constructor și destructor. | + | public: |
- | *[**2p**] Adăugați clasei Punct2D metode pentru determinarea proiecțiilor pe axe (OX/OY), respectiv pentru determinarea distanței față de un alt Punct2D. | + | Complex() |
- | *[**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. | + | { |
- | *[**5p**] Clasa KeyStorage | + | re = 0; |
- | *[**2p**] Implementați și folosiți utilizând template-uri clasa KeyStorage de mai sus adăugând constructor și destructor. | + | im = 0; |
- | *[**2p**] Alocați o instanță de tip KeyStorage local și dinamic (utilizând new / delete). | + | printf("constructor default\n"); |
- | *[**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. | + | } |
+ | |||
+ | 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 ===== | ===== Clase/metode prietene ===== | ||
- | Așa cum am văzut în primul laborator, fiecare membru al clasei poate avea 3 specificatori de acces: | + | Așa cum am văzut în primul articol, fiecare membru al clasei poate avea 3 specificatori de acces: |
* public | * public | ||
* private | * private | ||
Line 425: | Line 567: | ||
</code> | </code> | ||
- | ==== 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 | + | <hidden> |
+ | *[**5p**] Clasa MappingEntry - conține 2 membri de tipuri potențial diferite și realizează, din punct de vedere conceptual, asocierea între două valori (una se numește //cheie//, iar cealaltă //valoare//). | ||
+ | *[**2p**] Implementați și folosiți utilizând template-uri clasa MappingEntry de mai sus adăugând constructor, destructor si 2 metode de tip getter pentru cele doua valori private. | ||
+ | *[**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. | ||
- | * MyClass(const MyClass& obj); | + | *[**5p**] Clasa Complex - clasă ce implementează conceptul de număr complex |
- | * MyClass(MyClass& obj); | + | *[**2p**] Implementați și folosiți utilizând template-uri clasa Complex, adăugând constructor și destructor. |
+ | *[**2p**] Adăugați clasei Complex metode pentru adunare, scădere și înmulțire cu un alt număr complex. | ||
+ | *[**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. | ||
- | === Când se apelează? === | + | *[**3p**] Simple use of const |
+ | *[**0.5p**] Creati un pointer variabil la o variabila de tip intreg constanta (in 2 moduri). | ||
+ | *[**0.5p**] Creati un pointer constant la o variabila de tip intreg non-constanta. | ||
+ | *[**0.5p**] Creati un pointer constant la o variabila de tip intreg constanta. | ||
+ | *[**1.5p**] Initializati pointerul si variabila referita pentru fiecare caz. Explicati si rezolvati erorile de compilare. | ||
- | 1) Apel explicit | + | *[**5p**] Clasa MappingEntry - conține 2 membri de tipuri potențial diferite și realizează, din punct de vedere conceptual, asocierea între două valori (una se numește //cheie//, iar cealaltă //valoare//). |
+ | *[**2p**] Implementați și folosiți utilizând template-uri clasa MappingEntry de mai sus adăugând constructor și destructor. | ||
+ | *[**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. | ||
- | <code c++ explicit_copy_constructor_call.cpp> | + | *[**5p**] Clasa Punct2D - clasă ce implementează conceptul de punct în plan |
- | MyClass m; | + | *[**2p**] Implementați și folosiți utilizând template-uri clasa Punct2D, adăugând constructor și destructor. |
- | MyClass x = MyClass(m); /* apel explicit al copy-constructor-ului */ | + | *[**2p**] Adăugați clasei Punct2D metode pentru determinarea proiecțiilor pe axe (OX/OY), respectiv pentru determinarea distanței față de un alt Punct2D. |
- | </code> | + | *[**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. |
+ | *[**5p**] Clasa KeyStorage | ||
+ | *[**2p**] Implementați și folosiți utilizând template-uri clasa KeyStorage de mai sus adăugând constructor și destructor. | ||
+ | *[**2p**] Alocați o instanță de tip KeyStorage 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. | ||
- | 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> | ||
- | |||
- | ==== 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> | ||
- | </hidden> | ||
*[**5p**] Clasa Punct2D - clasă ce implementează conceptul de punct în plan | *[**5p**] Clasa Punct2D - clasă ce implementează conceptul de punct în plan | ||
Line 523: | 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++? |