This is an old revision of the document!


Laborator 03 - Notiuni avansate de C++

Obiective

În urma parcurgerii acestui laborator studentul va:

  • 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

Templates

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:

template <class identifier> declaratie;
template <typename identifier> declaratie;

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:

template<typename T>
T getMax(T a, T b) {
    return a > b ? a : b;
}

Funcția poate fi apelată astfel:

getMax<int>(2, 3);
getMax<double>(3.2, 4.6);

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:

KeyStorage.h
template<typename T>
class KeyStorage {
public:
    int key;
    T member;
};

În funcția main, să presupunem că vrem să folosim clasa cu membrul de tip long.

main.cpp
#include "KeyStorage.h"
 
int main() {
    KeyStorage<long> keyElement;
    return 0;
}

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

KeyStorage.h
template<typename T>
class KeyStorage {
public:
    KeyStorage(int k);
    ~KeyStorage();
 
    T getMember();
    T setMember(T element);
 
private:
    T member;
    int key;
};

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).
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>;
  1. [4p] Implementaţi clasa template Set care să permită lucrul cu mulțimi de obiecte, cu următoarele particularități:
    1. Constructorul va primi dimensiunea maximă de elemente care pot fi ținute în mulțime și va aloca spațiul necesar.
    2. [0.5p] Se va defini și un destructor, care va dezaloca memoria alocată dinamic.
    3. Se vor implementa funcţii membre pentru:
      1. [1p] supraîncărcarea operatorului += pentru adăugarea unui nou element în mulțime (dacă elementul există deja în mulțime atunci nu va mai fi adăugat).
      2. [0.5p] supraîncărcarea operatorului -= pentru eliminarea unui element din mulțime.
    4. Se vor implementa funcţii friend (nemembre) pentru:
      1. [1p] testul de egalitate a două mulțimi ( supraîncărcarea operatorului == ): două mulțimi sunt egale daca conțin aceleași elemente.
      2. [0.5p] supraîncărcarea operatorului « (pentru scriere).
      3. [0.5p] supraîncărcarea operatorului » (pentru citire).
sd-ca/laboratoare/laborator-03.1456076460.txt.gz · Last modified: 2016/02/21 19:41 by radu.stochitoiu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0