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.
Sfida
FacileCostruiamo 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 chiamatoStorageche funge da semplice contenitore per un singolo valore, con alcune operazioni utili:Il tuo template
Storagedovrebbe 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 restituiscetruese il valore è uguale al valore predefinito costruito del tipo T,falsealtrimenti
Crea anche un template di classe chiamato
Paircon 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()egetSecond()per recuperare ciascun valore - Un metodo
display()che stampa:(<first>, <second>)
main.cpp: Leggi quattro input (ciascuno su una riga separata):- Un valore intero
- Un nuovo valore intero
- Un valore stringa
- Un valore double
Dimostra i tuoi template:
- Crea uno
Storage<int>con il primo intero e stampa:Int storage: <value> - Aggiornalo con il secondo intero usando
setValue(), quindi stampa:Updated: <value> - Crea uno
Storage<std::string>con la stringa in input e stampa:String storage: <value> - Controlla se lo storage della stringa è vuoto e stampa:
Is empty: <true/false>(stampatrueofalse) - Crea un
Pair<std::string, double>con i valori stringa e double, quindi chiamadisplay() - Crea un
Pair<int, int>con entrambi gli input interi, quindi chiamadisplay()
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;
}
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
1Fondamenti della programmazione orientata agli oggetti
File esterniBuild e compilazione in C++File header e file sorgenteNamespace e ambitoIntroduzione alla programmazione orientata agli oggetti in C++Classi e oggetti a confrontoIl puntatore 'this'Metodi (funzioni membro)Attributi (membri dati)Fondamenti di costruttori e distruttoriRiepilogo - Calcolatrice semplice4Proprietà delle classi
Membri di istanza e staticiGetter e setterFunzioni membro constParola chiave mutableMetodi e variabili staticiFunzioni e classi friendRiepilogo - Gestore di conti bancari7Ereditarietà
Ereditarietà di baseLivelli di accesso nell’ereditarietàOrdine di chiamata di costruttori e distruttoriRidefinizione dei metodiFunzioni virtuali e VTableEreditarietà multiplaEreditarietà virtualeRiepilogo - Gerarchia dei dipendenti2Gestione della memoria
Memoria Stack vs HeapPuntatori e riferimentiMemoria dinamica (new/delete)Puntatori intelligenti in C++RAII in C++Riepilogo - Gestore di array dinamico5Incapsulamento
Specificatori di accesso in C++Specificatori di accesso in dettaglioOccultamento delle informazioniStruct vs classClassi annidate e interneRiepilogo - Sistema di registrazione degli studenti8Polimorfismo
Polimorfismo a compile time e a runtimeOverload delle funzioniFunzioni virtuali: ripassoFunzioni virtuali pureClassi astratteProgettazione delle interfacce in C++Dynamic casting e RTTIRipasso: calcolatrice di forme11Concetti avanzati di OOP
Composizione vs ereditarietàMixin tramite CRTPIdiom PimplType ErasureEnum class e tipizzazione forteGestione delle eccezioni in OOPGerarchie personalizzate di eccezioni14Pattern di progettazione - Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeRAII come pattern3Costruttori e distruttori
Costruttore predefinitoCostruttore con parametriCostruttore di copiaCostruttore di spostamentoListe di inizializzazione dei costruttoriCostruttori delegantiApprofondimento sui distruttoriRegola del Tre / Cinque / ZeroRipasso - classe String6Sovraccarico degli operatori
Introduzione al sovraccarico degli operatoriSovraccarico degli operatori aritmeticiSovraccarico degli operatori di confrontoOperatori di flussoSovraccarico dell'operatore di assegnazioneSovraccarico degli operatori [] e ()Operatori di conversione di tipoRipasso - Classe Matrix9Template
Template di funzioneTemplate di classeSpecializzazione dei templateTemplate variadiciBasi di SFINAE e dei trait di tipoRiepilogo - Contenitore genericoEsercitati da solo: Compilatore C++ online