This shows you the differences between two versions of the page.
sd-ca:laboratoare:laborator-03 [2016/02/21 19:37] radu.stochitoiu |
sd-ca:laboratoare:laborator-03 [2016/02/21 19:51] (current) darius.neatu |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Laborator 03 - Notiuni avansate de C++ ====== | + | ====== Articol 03 - Notiuni avansate de C++ ====== |
- | Responsabili | ||
- | * [[andrei.vasiliu2211@cti.pub.ro| Andrei Vasiliu]] | ||
- | * [[daniel.ciocirlan1607@cti.pub.ro| Daniel Ciocîrlan]] | ||
===== Obiective ===== | ===== Obiective ===== | ||
- | În urma parcurgerii acestui laborator studentul va: | + | În urma parcurgerii acestui articol studentul va: |
+ | * înțelege conceptul de template | ||
- | * afla funcționalitățile claselor/funcțiilor prietene | + | ===== Templates ===== |
- | * realiza supraîncărcarea operatorilor din C++ | + | |
- | * înțelege conceptul de copy constructor | + | |
- | * înțelege conceptul de rule of three | + | |
+ | 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. | ||
+ | |||
+ | Sintaxa pentru acestea este: | ||
+ | <code c++> | ||
+ | template <class identifier> declaratie; | ||
+ | template <typename identifier> declaratie; | ||
+ | </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 ==== | ||
+ | În primul rând template-urile pot fi aplicate funcțiilor. | ||
+ | |||
+ | Un exemplu comun și simplu este următorul: | ||
+ | <code c++> | ||
+ | template<typename T> | ||
+ | T getMax(T a, T b) { | ||
+ | return a > b ? a : b; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Funcția poate fi apelată astfel: | ||
+ | <code c++> | ||
+ | getMax<int>(2, 3); | ||
+ | getMax<double>(3.2, 4.6); | ||
+ | </code> | ||
+ | |||
+ | ==== 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> | ||
+ | template<typename T> | ||
+ | class KeyStorage { | ||
+ | public: | ||
+ | int key; | ||
+ | T member; | ||
+ | }; | ||
+ | </code> | ||
+ | |||
+ | În funcția main, să presupunem că vrem să folosim clasa cu membrul de tip long. | ||
+ | <code c++ main.cpp> | ||
+ | #include "KeyStorage.h" | ||
+ | |||
+ | int main() { | ||
+ | KeyStorage<long> keyElement; | ||
+ | return 0; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Practic, oriunde folosim tipul de date T în clasă, este înlocuit cu tipul pe care îl specificăm. | ||
+ | |||
+ | ==== Where's the magic happening? ==== | ||
+ | Sunt destul de multe lucruri de spus despre template-uri, dar ne vom concentra pe lucrurile care schimbă modul în care ați implementat până acum. | ||
+ | |||
+ | Template-urile sunt de fapt indicii pentru compilator pentru a genera cod la rândul lui! | ||
+ | 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. | ||
+ | |||
+ | Ce trebuie să rețineți din asta? Totul se întâmplă la **compile time**, nu la run time. | ||
+ | |||
+ | 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<float> determină compilatorul să genereze cod pentru ambele clase (înlocuind o dată T cu int și altă cu float). | ||
+ | |||
+ | ==== Guideline-uri implementare ==== | ||
+ | Pentru că totul se întâmplă la compile time, înseamnă că în momentul în care compilatorul întâlnește secvența de cod ce folosește template-uri trebuie să știe //toate// modurile în care aceasta este folosita. | ||
+ | |||
+ | Asta înseamnă că: | ||
+ | *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). | ||
+ | |||
+ | ==== Clasa KeyStorage ==== | ||
+ | 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: | ||
+ | KeyStorage(int k); | ||
+ | ~KeyStorage(); | ||
+ | | ||
+ | T getMember(); | ||
+ | T setMember(T element); | ||
+ | | ||
+ | private: | ||
+ | T member; | ||
+ | int key; | ||
+ | }; | ||
+ | |||
+ | </code> | ||
+ | |||
+ | Implementarea completa a ei poate fi realizată: | ||
+ | *î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> | ||
+ | #include "KeyStorage.h" | ||
+ | |||
+ | template<typename T> | ||
+ | KeyStorage<T>::KeyStorage(int k) { | ||
+ | //TODO | ||
+ | } | ||
+ | |||
+ | template<typename T> | ||
+ | KeyStorage<T>::~KeyStorage() { | ||
+ | } | ||
+ | |||
+ | //TODO: restul metodelor. | ||
+ | |||
+ | // La sfarsit, cu tipurile de date pe care le veti folosi. | ||
+ | template class KeyStorage<int>; | ||
+ | template class KeyStorage<long>; | ||
+ | |||
+ | </code> | ||
+ | <hidden> | ||
-[**5p**] Implementati clasa **Fractie**, cu următoarele particularități: | -[**5p**] Implementati clasa **Fractie**, cu următoarele particularități: | ||
Line 39: | Line 155: | ||
<note warning>Tipul parametrului pentru copy-constructor trebuie să fie identic cu cel al parametrului pentru operatorul de assignment</note> | <note warning>Tipul parametrului pentru copy-constructor trebuie să fie identic cu cel al parametrului pentru operatorul de assignment</note> | ||
- | <hidden> | + | |
===== Exerciții ===== | ===== Exerciții ===== | ||
- [**3p**] Implementați clasa **Complex**, cu următoarele particularități: | - [**3p**] Implementați clasa **Complex**, cu următoarele particularități: | ||
Line 80: | Line 196: | ||
- [**1p**] supraîncărcarea operatorului de atribuire între două obiecte de tip vector | - [**1p**] supraîncărcarea operatorului de atribuire între două obiecte de tip vector | ||
- [**0.5p**] supraîncărcarea operatorului de indexare [] ce va permite accesul la elementele individuale prin indexare **Operatorul de indexare** este un operator binar, având ca prim termen obiectul care se indexează, iar ca al doilea termen indicele. (//''obiect[indice]'' este interpretat ca ''obiect.operator[](indice)''//. | - [**0.5p**] supraîncărcarea operatorului de indexare [] ce va permite accesul la elementele individuale prin indexare **Operatorul de indexare** este un operator binar, având ca prim termen obiectul care se indexează, iar ca al doilea termen indicele. (//''obiect[indice]'' este interpretat ca ''obiect.operator[](indice)''//. | ||
- | </hidden> | + | |
- [**4p**] Implementaţi clasa template **Set** care să permită lucrul cu mulțimi de obiecte, cu următoarele particularități: | - [**4p**] Implementaţi clasa template **Set** care să permită lucrul cu mulțimi de obiecte, cu următoarele particularități: | ||
- Constructorul va primi dimensiunea maximă de elemente care pot fi ținute în mulțime și va aloca spațiul necesar. | - Constructorul va primi dimensiunea maximă de elemente care pot fi ținute în mulțime și va aloca spațiul necesar. | ||
Line 91: | Line 207: | ||
- [**0.5p**] supraîncărcarea operatorului << (pentru scriere). | - [**0.5p**] supraîncărcarea operatorului << (pentru scriere). | ||
- [**0.5p**] supraîncărcarea operatorului >> (pentru citire). | - [**0.5p**] supraîncărcarea operatorului >> (pentru citire). | ||
+ | </hidden> |