Menu
Coddy logo textTech

Template di classe

Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 65 di 104.

Proprio come i modelli di funzione ti permettono di scrivere funzioni indipendenti dal tipo, i modelli di classe ti permettono di creare classi indipendenti dal tipo. È così che funzionano contenitori come std::vector: definisci la struttura una volta e il compilatore genera versioni specifiche per ogni tipo che usi.

template <typename T>
class Box {
    T value;
public:
    Box(T v) : value(v) {}
    T getValue() const { return value; }
    void setValue(T v) { value = v; }
};

int main() {
    Box<int> intBox(42);
    Box<std::string> strBox("Hello");
    
    std::cout << intBox.getValue() << std::endl;  // 42
    std::cout << strBox.getValue() << std::endl;  // Hello
}

A differenza dei modelli di funzione, quando crei oggetti da modelli di classe devi specificare esplicitamente il tipo usando le parentesi angolari. Il compilatore genera quindi una definizione completa della classe per quel tipo specifico.

I modelli di classe possono avere più parametri di tipo e persino parametri non di tipo, come gli interi:

template <typename T, int Size>
class FixedArray {
    T data[Size];
public:
    T& operator[](int index) { return data[index]; }
    int size() const { return Size; }
};

FixedArray<double, 5> arr;  // Array di 5 double
arr[0] = 3.14;

Quando definisci funzioni membro al di fuori della classe, devi ripetere la dichiarazione del template:

template <typename T>
class Container {
    T* data;
public:
    Container();
    ~Container();
};

template <typename T>
Container<T>::Container() : data(nullptr) {}

template <typename T>
Container<T>::~Container() { delete data; }

I modelli di classe costituiscono le fondamenta della programmazione generica in C++, consentendoti di scrivere strutture dati riutilizzabili che funzionano con qualsiasi tipo.

challenge icon

Sfida

Facile

Costruiamo un sistema di archiviazione generico utilizzando i template di classe per creare un contenitore flessibile che possa contenere qualsiasi tipo di dato. Organizzerai la classe template in un file header e ne dimostrerai la versatilità con diversi tipi nel programma principale.

Creerai due file:

  • Storage.h: Definisci un template di classe chiamato Storage che funge da semplice contenitore per un singolo valore, con alcune operazioni utili:

    Il tuo template Storage dovrebbe avere:

    • Un membro privato per contenere il valore archiviato
    • Un costruttore che accetta un valore iniziale
    • Un metodo getValue() che restituisce il valore archiviato
    • Un metodo setValue() che aggiorna il valore archiviato
    • Un metodo isEmpty() che restituisce true se il valore è uguale al valore predefinito costruito del tipo T, false altrimenti

    Crea anche un template di classe chiamato Pair con due parametri di tipo, che memorizza due valori correlati:

    • Membri privati per il primo e il secondo valore (potenzialmente di tipi diversi)
    • Un costruttore che inizializza entrambi i valori
    • I metodi getFirst() e getSecond() per recuperare ciascun valore
    • Un metodo display() che stampa: (<first>, <second>)
  • main.cpp: Leggi quattro input (ciascuno su una riga separata):
    1. Un valore intero
    2. Un nuovo valore intero
    3. Un valore stringa
    4. Un valore double

    Dimostra i tuoi template:

    1. Crea uno Storage<int> con il primo intero e stampa: Int storage: <value>
    2. Aggiornalo con il secondo intero usando setValue(), quindi stampa: Updated: <value>
    3. Crea uno Storage<std::string> con la stringa in input e stampa: String storage: <value>
    4. Controlla se lo storage della stringa è vuoto e stampa: Is empty: <true/false> (stampa true o false)
    5. Crea un Pair<std::string, double> con i valori stringa e double, quindi chiama display()
    6. Crea un Pair<int, int> con entrambi gli input interi, quindi chiama display()

Ad esempio, con gli input 42, 100, Hello e 3.14:

Int storage: 42
Updated: 100
String storage: Hello
Is empty: false
(Hello, 3.14)
(42, 100)

Nota come lo stesso template Storage funzioni senza problemi con interi e stringhe e come Pair possa combinare tipi diversi. Il compilatore genera definizioni di classe separate per ogni combinazione di tipi utilizzata. Ricorda di usare la sintassi template <typename T> per i template con un solo tipo e template <typename T, typename U> per i template con più parametri di tipo.

Provalo tu

#include <iostream>
#include <string>
#include "Storage.h"

using namespace std;

int main() {
    // Leggi gli input
    int intValue1;
    int intValue2;
    string strValue;
    double doubleValue;
    
    cin >> intValue1;
    cin >> intValue2;
    cin >> strValue;
    cin >> doubleValue;
    
    // TODO: Crea Storage<int> con il primo intero e stampa: Int storage: <value>
    
    // TODO: Aggiorna con il secondo intero usando setValue(), stampa: Updated: <value>
    
    // TODO: Crea Storage<std::string> con la stringa in input e stampa: String storage: <value>
    
    // TODO: Controlla se la stringa memorizzata è vuota, stampa: Is empty: true/false
    
    // TODO: Crea Pair<std::string, double> con la stringa e il double, chiama display()
    
    // TODO: Crea Pair<int, int> con entrambi gli interi, chiama display()
    
    return 0;
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore C++ online