Consideriamo le seguenti classi:
// ex12_1_1.h #include <iostream.h> enum Colore { ARANCIO, GIALLO, VERDE }; class Passero { int peso; public: Passero (int p) { peso = p; cout << "Passero::Passero()\n"; } ~Passero () { cout << "Passero::~Passero()\n"; } }; class Canarino { Colore colore; public: Canarino (Colore c) { colore = c; cout << "Canarino::Canarino()\n"; } ~Canarino () { cout << "Canarino::~Canarino()\n"; } }; class Merlo { bool parlante; public: Merlo (bool p) { parlante = p; cout << "Merlo::Merlo()\n"; } ~Merlo () { cout << "Merlo::~Merlo()\n"; } };
Supponiamo che esse siano delle classi fondamentali per una classe specifica
Voliera
, composta con esse:
// ex12_1_1.cpp #include <iostream.h> #include "ex12_1_1.h" class Voliera { Passero* passero; Canarino* canarino; Merlo* merlo; public: Voliera (int peso, // peso del passero Colore colore, // colore del canarino bool parla) { // attitudine del merlo cout << "Voliera::Voliera()\n"; passero = new Passero(peso); canarino = new Canarino(colore); merlo = new Merlo(parla); } ~Voliera () { cout << "Voliera::~Voliera()\n"; delete passero; delete canarino; delete merlo; } }; void main() { Voliera voliera (5, GIALLO, true); }
output:
Voliera::Voliera() Passero::Passero() Canarino::Canarino() Merlo::Merlo() Voliera::~Voliera() Passero::~Passero() Canarino::~Canarino() Merlo::~Merlo()
All'interno della classe Voliera
abbiamo dichiarato tre oggetti di tipo
Passero
, Canarino
e Merlo
tramite puntatori;
l'inizializzazione di essi avviene nel costruttore e la loro deallocazione del
distruttore ~Voliera()
. Tuttavia, visto che gli oggetti contenuti nella
classe Voliera
appartengono ad essa, possiamo dichiarare anche dei veri
e propri oggetti, come se si trattasse di oggetti di tipi primitivi. Dobbiamo
però fare risolvere il seguente problema: se definiamo un oggetto esso
necessita una chiamata al proprio costruttore, la quale deve avvenire
prima della chiamata del costruttore della classe composta
Voliera
. La seguente implementazione è dunque errata:
class Voliera { Passero passero; Canarino canarino; Merlo merlo; public: Voliera (int peso, // peso del passero Colore colore, // colore del canarino bool parla); // attitudine del merlo };in quanto non possiamo in nessun modo ``anticipare'' la costruzione degli oggetti privati della classe
Voliera
. Esiste naturalmente la soluzione
al nostro problema: per inizializzare un oggetto prima che il corpo del
costruttore venga eseguito è sufficiente utilizzare la seguente sintassi:
ClasseComposta ([argomenti_del_costruttore]) : Oggetto1([argomenti_oggetto_1]), Oggetto2([argomenti_oggetto_2]), , ..., OggettoN([argomenti_oggetto_N]); |
// ex12_1_2.cpp #include <iostream.h> #include "ex12_1_1.h" class Voliera { Passero passero; Canarino canarino; Merlo merlo; public: Voliera (int peso, // peso del passero Colore colore, // colore del canarino bool parla) // attitudine del merlo : passero(peso), canarino(colore), merlo(parla) { cout << "Voliera::Voliera()\n"; } ~Voliera () { cout << "Voliera::~Voliera()\n"; } }; void main() { Voliera voliera (5, GIALLO, true); }
output:
Passero::Passero() Canarino::Canarino() Merlo::Merlo() Voliera::Voliera() Voliera::~Voliera() Merlo::~Merlo() Canarino::~Canarino() Passero::~Passero()
Dall'output del programma è evidente che la costruzione degli oggetti di
tipo Passero
, Canarino
e Merlo
avviene prima della
chiamata al costruttore di Voliera
. Si noti inoltre che la loro
distruzione è automaticamente messa in atto dal compilatore dopo la
distruzione dell'oggetto di tipo Voliera
. Comporre classi è dunque
una operazione del tutto sicura. Per analogia con i costruttori degli oggetti
istanziati da classi programmate dall'utente, è possibile anche chiamare
degli pseudo-costruttori per oggetti di tipo primitivo:
// ex12_1_3.cpp #include <iostream.h> #include <math.h> class Prova { int n; double x; char* s; public: Prova (int N, double X) : n(N), x(X), s(0) { } operator double() { return pow(x, n); } }; void main() { Prova p(3, 4); cout << p << "\n"; }
output:
64
Abitacolo
e
Portabagagli
, aventi un costruttore con argomento un numero intero
rappresentante il loro volume; si definisca per entrambe l'operatore di
conversione ad intero, che restituisce il valore dell'unico campo dati di
esse contenente appunto il volume;
Automobile
; si ridefinisca
l'operatore di uscita sull'output standard, avente una sintassi del tipo:
abitacolo: XX litri portabagagli: XX litri |