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> | ||