This shows you the differences between two versions of the page.
sd-ca:articole:tutorial-02-1 [2016/02/28 20:27] darius.neatu created |
sd-ca:articole:tutorial-02-1 [2016/03/03 01:02] (current) andrei.vasiliu2211 [Obiective] |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | TO DO | + | ====== Tutorial 2.1 - Point2D ====== |
+ | |||
+ | În acest tutorial, ne propunem să construim o structură de tip Point2D capabil să rețină coordonatele unui punct în plan și să permită anumite prelucrări pe un set de puncte. | ||
+ | |||
+ | ===== Obiective ===== | ||
+ | |||
+ | Ne dorim: | ||
+ | * să construim class Point2D, cu doi membri de tip double astfel încât să poată memora cele două coordonate | ||
+ | * să adaugam lui class Point2D o metode de tip SETTER/GETTER pentru a putea accesa/modifica coordonatele unui punct | ||
+ | * să adaugam si alte functionalitati lui class Point2D, precum posibilitatea calculării distanței dintre două puncte | ||
+ | * să testam class Point2D printr-un cod simplu care arată cum creează elemente de tip Point2D și cum se calculează distanța dintre două puncte | ||
+ | * să exemplificăm conceptul de rule of three | ||
+ | * să găsim cele mai apropiate două puncte dintr-un set de puncte date | ||
+ | * să verificam cu Valgrind că nu sunt memory leaks. | ||
+ | |||
+ | ===== Dezvoltare ===== | ||
+ | |||
+ | ==== Varianta de bază a lui class Point2D ==== | ||
+ | |||
+ | Vom considera ca variantă de bază un struct care conține două elemente de tip double. | ||
+ | |||
+ | <code cpp> | ||
+ | #ifndef __POINT2D__ | ||
+ | #define __POINT2D__ | ||
+ | |||
+ | struct Point2D { | ||
+ | double x, y; // coordonatele punctului | ||
+ | }; | ||
+ | |||
+ | #endif __POINT2D__ | ||
+ | </code> | ||
+ | |||
+ | Această abordare are dezavantajul că variabilele x și y sunt publice, lucru care nu este dorit în general. O abordare elegantă este următoarea: | ||
+ | |||
+ | <code cpp> | ||
+ | #ifndef __POINT2D__ | ||
+ | #define __POINT2D__ | ||
+ | |||
+ | class Point2D { | ||
+ | private: | ||
+ | double x, y; // coordonatele punctului | ||
+ | |||
+ | public: | ||
+ | // Metoda setter pentru x | ||
+ | void setX(const double& x) { | ||
+ | this->x = x; | ||
+ | } | ||
+ | |||
+ | // Metoda setter pentru y | ||
+ | void setY(const double& y) { | ||
+ | this->y = y; | ||
+ | } | ||
+ | |||
+ | // Metoda getter pentru x | ||
+ | double getX() { | ||
+ | return x; | ||
+ | } | ||
+ | |||
+ | // Metoda getter pentru y | ||
+ | double getY() { | ||
+ | return y; | ||
+ | } | ||
+ | |||
+ | }; | ||
+ | |||
+ | #endif // __POINT2D__ | ||
+ | </code> | ||
+ | |||
+ | Observați folosirea referințelor și a cuvântului cheie const! | ||
+ | |||
+ | ==== Adaugarea de constructori ==== | ||
+ | |||
+ | Pentru a putea inițializa un punct, mai exact coordonatele punctului, cu anumite valori, putem folosi un constructor. În această situație putem inițializa cu valori prestabilite la compile time (Default Constructor), valori cunoscute la runtime (un constructor cu parametri). | ||
+ | |||
+ | <code cpp> | ||
+ | // Constructor default | ||
+ | Point2D() { | ||
+ | x = 0; | ||
+ | y = 0; | ||
+ | } | ||
+ | |||
+ | // Constructor cu parametri | ||
+ | Point2D(const double &x, const double &y) { | ||
+ | this->x = x; | ||
+ | this->y = y; | ||
+ | } | ||
+ | |||
+ | // Copy-constructor | ||
+ | Point2D(Point2D& p) { | ||
+ | this->x = p.x; | ||
+ | |||
+ | </code> | ||
+ | |||
+ | Metoda care implemenetează calculul distanței dintre două puncte este următoarea: | ||
+ | |||
+ | <code cpp> | ||
+ | // Metoda care calculeaza distanta intre doua puncte in plan | ||
+ | double distance(const Point2D &p) { | ||
+ | return sqrt( (x - p.x) * (x - p.x) + (y - p.y) * (y - p.y) ); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Atenție! Trebuie inclusă biblioteca cmath pentru a putea utiliza funcția sqrt! | ||
+ | <code cpp> | ||
+ | #ifndef __POINT2D__ | ||
+ | #define __POINT2D__ | ||
+ | |||
+ | #include <cmath> | ||
+ | .... | ||
+ | |||
+ | </code> | ||
+ | |||
+ | Putem testa funcționalitățile implementate cu o sursă foarte scurtă. | ||
+ | |||
+ | <code cpp> | ||
+ | #include <iostream> | ||
+ | #include "point2D.h" | ||
+ | |||
+ | int main() { | ||
+ | Point2D A, B; | ||
+ | |||
+ | std::cin >> A >> B; | ||
+ | |||
+ | std::cout << A.distance(B); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Pentru compilare și rulare in bash folosiți comanda | ||
+ | <code cpp> | ||
+ | g++ main.cpp -o main && ./main | ||
+ | </code> | ||
+ | |||
+ | |||
+ | ==== Adaugarea de alte elemente (e.g. functii de citire/scriere) ==== | ||
+ | |||
+ | Pentru a putea lucra cu ușurință cu elemente de tip Point2D, putem supraîncărca operatorii de citire/afișare, atribuire, egalitate. | ||
+ | De asemenea, întrucât am definit Copy-Constructir și operator de atribuire, se impune definirea unui destructor. | ||
+ | |||
+ | <code cpp> | ||
+ | #ifndef __POINT2D__ | ||
+ | #define __POINT2D__ | ||
+ | |||
+ | #include <cmath> | ||
+ | |||
+ | class Point2D { | ||
+ | |||
+ | ............ | ||
+ | |||
+ | public: | ||
+ | // Destructor | ||
+ | ~Point2D() { | ||
+ | // Nu avem memorie de eliberat. | ||
+ | } | ||
+ | |||
+ | // Operator de atribuire | ||
+ | Point2D& operator=(const Point2D& p) { | ||
+ | this->x = p.x; | ||
+ | this->y = p.y; | ||
+ | return *this; // intoarce referinta pentru atribuiri inlantuite | ||
+ | } | ||
+ | |||
+ | // Operator de egalitate | ||
+ | bool operator==(const Point2D& p) { | ||
+ | // Testez daca cele doua puncte au coordonate egale. | ||
+ | if (x == p.x && y == p.y) { | ||
+ | return true; | ||
+ | } | ||
+ | |||
+ | return false; | ||
+ | } | ||
+ | |||
+ | //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 Point2D& p); | ||
+ | friend std::istream& operator>> (std::istream& is, Point2D& p); | ||
+ | }; | ||
+ | |||
+ | |||
+ | std::ostream& operator<<(std::ostream& out, const Point2D& p){ | ||
+ | out << "(" << p.x << "," << p.y << ")"<< std::endl; | ||
+ | return out; | ||
+ | } | ||
+ | |||
+ | std::istream& operator>>(std::istream& in, Point2D& p){ | ||
+ | in >> p.x>> p.y; | ||
+ | return in; | ||
+ | } | ||
+ | |||
+ | #endif // __POINT2D__ | ||
+ | </code> | ||
+ | |||
+ | ===== Testare si Evaluare ===== | ||
+ | |||
+ | ==== Program de test ==== | ||
+ | |||
+ | În continuare vom face un program care prima dată citește un număr n si un element de tip Point2D, numit p, apoi încă n element de tip Point2D. Se cere să se găsească cel mai apropiat punct (din cele n) față de punctul de referință p. | ||
+ | |||
+ | <code cpp> | ||
+ | #include <iostream> | ||
+ | #include <cmath> // pentru abs | ||
+ | #include "point2D.h" | ||
+ | |||
+ | int main() { | ||
+ | int n; // numarul de puncte | ||
+ | Point2D p; // punctul de referinta | ||
+ | Point2D *v; // vectorul de puncte dat de la tastatura | ||
+ | |||
+ | //citire | ||
+ | std::cout << "n = "; | ||
+ | std::cin >> n;; | ||
+ | |||
+ | std::cout << "p = "; | ||
+ | std::cin >> p; | ||
+ | |||
+ | v = new Point2D[n]; | ||
+ | for ( int i = 0; i < n; ++i ) { | ||
+ | std::cout << "v[" << i << "]="; | ||
+ | std::cin >> v[i]; | ||
+ | } | ||
+ | |||
+ | double minDistance = (1<<30); // initializez distanta minima cu ceva foarte mare | ||
+ | int index = -1; // indicele celui mai apropiat punct | ||
+ | |||
+ | for ( int i = 0; i < n; ++i) { | ||
+ | double currentDistance = p.distance(v[i]); //distanta fata de punctul curent | ||
+ | |||
+ | if ( currentDistance < minDistance ) { | ||
+ | minDistance = currentDistance; | ||
+ | index = i; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // afisare rezultat | ||
+ | std::cout << "Cel mai apropiat punct este : " << index << " = " << v[index] << '\n'; | ||
+ | |||
+ | //eliberare memorie | ||
+ | delete[] v; | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Exemplu: | ||
+ | |||
+ | <code bash> | ||
+ | n = 5 | ||
+ | p = 0 0 | ||
+ | v[0]= 5 5 | ||
+ | v[1]=40 5 | ||
+ | v[2]= 5 -5 | ||
+ | v[3]= 4 3 | ||
+ | v[4]= 8 8 | ||
+ | Cel mai apropiat punct este : 3 = (4,3) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | ==== Verificare cu Valgrind ==== | ||
+ | |||
+ | Pentru acelasi test rulat cu <code bash> valgrind ./main </code> toata memoria va fi eliberata. | ||
+ | |||
+ | <code bash> | ||
+ | ==9360== Memcheck, a memory error detector | ||
+ | ==9360== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. | ||
+ | ==9360== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info | ||
+ | ==9360== Command: ./main | ||
+ | ..................... | ||
+ | ==9360== | ||
+ | ==9360== HEAP SUMMARY: | ||
+ | ==9360== in use at exit: 0 bytes in 0 blocks | ||
+ | ==9360== total heap usage: 13 allocs, 13 frees, 772 bytes allocated | ||
+ | ==9360== | ||
+ | ==9360== All heap blocks were freed -- no leaks are possible | ||
+ | ==9360== | ||
+ | ==9360== For counts of detected and suppressed errors, rerun with: -v | ||
+ | ==9360== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | ==== Aprofundare ==== | ||
+ | Încercați să extindeți class Point2D la class Point3D (punct în spațiu). Care sunt diferențele? |