Esempio:
class Complex { float i, r; public: Complex(); // Primo semplice costruttore Complex (float, float); // Secondo costruttore sovraccaricato }; Complex::Complex() { i = 0; r = 0; } Complex::Complex (float ii, float rr) { i = ii; r = rr; }Ma come facciamo a chiamare un costruttore? Questo dipende se stai usando l'operatore "new" o no. Senza "new":
Complex x(); // O per farla breve: Complex x; // Chiamata del primo costruttore Complex y (2, 2); // Chiamata del secondo costruttoreDevi solo mettere i parametri dopo il nome della variabile, e il corrispondente costruttore sarà chiamato. Con l'operatore "new", si fa così:
Complex *x = new Complex(); // O più brevemente Complex *y = new Complex; // Chiamata del primo costruttore Complex *z = new Complex (2, 2); // Chiamata del secondo costruttore ... delete x; delete y; delete z;Il compilatore genera del codice che fa le seguenti cose:
Esempio:
class Complex { float i, r; public: Complex(); // Primo semplice costruttore Complex (float, float); // Secondo costruttore "overloaded" ~Complex(); // Distruttore }; Complex::~Complex() { printf ("Destructor called.\n"); } ... { Complex *x = new Complex (5, 5); Complex y; ... delete x; // Chiamata del distruttore per x } // Chiamata implicita del distruttore per yQuando "cancelliamo" un oggetto con delete, ecco cosa facciamo:
C'è anche un costruttore di default, e anche questo diventa inutilizzabile quando un qualsiasi altro costruttore è dichiarato. Puoi allora usare solo i costruttori che hai fornito.
class Complex { public: Complex (float, float); // È dichiarato un solo costruttore };Dopo questa, l'unico modo per costruire un oggetto Complex è di dare 2 parametri al costruttore.
Complex x; // Il compilatore rifiuterà questa riga Complex y (2, 2); // è ok chiamare il giusto costruttore Complex z[10]; // Non permesso (continua a leggere)Con un array di oggetti, dovremmo fornire gli argomenti del costruttore per ciascun elemento del vettore. Ma a causa della sintassi che deve uniformarsi con quella dei tipi nativi, un array di oggetti può essere dichiarato soltanto se l'oggetto ha un costruttore che non prende nessun argomento, o un solo argomento, ma non più di uno:
class Complex { public: Complex (float = 0, float = 0); }; Complex v1[5] = { 0, 1, 2, 3, 4 }; // per ogni elemento il costruttore è // chiamato con gli argomenti (x, 0) void Create (int n) { Complex v2[n] = { 0, 1, 2}; // I primi tre elementi sono inizializzati // con (x, 0), e gli altri con (0,0) }Ndt: notare che nel caso del vettore v1 i valori di inizializzazione 0, 1, 2, 3, 4 sono le parti immaginarie dei numeri complessi, perchè il primo argomento del costruttore
Complex (float, float)
(vedi sopra)
era usato come parte immaginaria.
Se volete che accada il contrario, dovete definire il costruttore così,
invertendo l'ordine degli argomenti:
Complex::Complex (float rr, float ii) { r = rr; i = ii; }Fate come preferite: infatti anche in matematica alcuni preferiscono scrivere i numeri complessi come
2j+1
altri come 1+2j
(che è lo stesso). Altri in entrambe le forme,
senza adottare una sintassi precisa. Sopra quando è scritto (x, 0)
per il primo numero (x)
si intende la parte immaginaria e per il secondo (0) quella reale, perciò la convenzione
adottata è quella di passare i numeri complessi al costruttore nella forma (parte imm, parte real)
del tutto simile a (parte imm)j + parte real
.
class Vector { int n; float *v; public: Vector(); Vector (const Vector &); }; Vector::Vector() { v = new float[100]; n = 100; } Vector::Vector (const Vector &vector) { n = vector.n; // Copia del campo n v = new float[100]; // Crea un nuovo array for (int i = 0; i < 100; i++) v[i] = vector.v[i]; // Copia l'array }I costruttori di copia sono necessari se effettui allocazione di memoria all'interno di un oggetto, come nell'esempio visto. In questo caso, non puoi semplicemente limitarti a copiare il puntatore (come fa il costruttore di copia di default), ma hai davvero bisogno all'atto della copia di allocare una parte della memoria, in modo che i valori associati ai due oggetti possano essere differenti. Quindi i costruttori di copia risolvono il problema della condivisione di memoria indesiderata che si avrebbe all'atto dell'assegnamento di un oggetto con parti dinamiche (cioè con membri puntatori).
Questo costruttore di copia è chiamato ognivolta che dichiari un oggetto di una classe e gli assegni un valore con una sola istruzione.
Vector a; // Costruttore vuoto Vector b (a); // Costruttore di copia Vector c = a; // Costruttore di copia (equivalente alla precedente istruzione) Vector *d = new vector (a); // Costruttore di copiaPuoi utilizzare il costruttore di copia anche con i tipi nativi, come int o char:
int a = 2; // Assegna un valore int b (2); // Assegna un valore
class A { int a; public: A (int); }; class B { int b; A a_element; public: B (int, int); };In questo esempio, stiamo includendo un oggetto della classe A in un oggetto della classe B. La cosa interessante è quello che succede quando chiamiamo il costruttore di B. Dopo che la memoria è stata allocata, il linguaggio stabilisce che deve essere chiamato il costruttore per ogni oggetto incluso, e solo dopo di ciò il costruttore dell'oggetto contenitore o figlio (cioè B). Ma quale costruttore di A verrà chiamato (nel caso ve ne sia più di uno), e come specificare altrimenti? Quando definisci il costruttore di B, devi specificare quale costruttore di A deve essere chiamato, e con quali argomenti. Ecco come si fa:
B::B (int aa, int bb) : a_element (aa) { // Codice per il costruttore b = bb; }Ndt: in questo esempio non c'è un costruttore senza argomenti, perciò se non specifichiamo nella definizione di B quale costruttore chiamare, il compilatore tenterà di inserire una chiamata ad un costruttore senza argomenti per la classe A, cioè a
A::A ()
ma poichè questo non è definito ci darà un errore.
Ricordate infatti che abbiamo detto che il costruttore di default (che è senza argomenti)
viene interdetto quando viene definito un altro costruttore.
Note sintattiche: notare l'uso di ":" prima della chiamata al costruttore di A.
Se molti costruttori devono essere chiamati, basta separare le loro chiamate
con delle virgole (come nell'esempio sotto).
Nota che la sintassi per inizializzare l'oggetto contenuto nell'oggetto è
all'atto della chiamata di un costruttore è la stessa usata nelle normali
dichiarazioni (tipo int b (2);
); solo il tipo della variabile da
inizializzare viene omesso, in quanto già noto dalla definizione della classe.
Un altro modo più semplice per scrivere il costruttore di B è di usare
i costruttori dei tipi nativi:
B::B (int aa, int bb) : a_element (aa), b (bb) { // Codice per il costruttore }Quando viene chiamato il costruttore di copia di default di un oggetto contenente altri oggetti, sarà implicitamente chiamato anche il costruttore di copia di ciascuno degli oggetti contenuti. Nel caso abbiamo bisogno di sovrapporre il costruttore di copia dell'oggetto contenitore, dobbiamo chiamare noi esplicitamente il costruttore di copia per ognuno degli oggetti contenuti: la sintassi per chiamare questi costruttori è la stessa usata nell'esempio precedente.
Ndt: ok, ok eccovi un esempio chiarificatore:
#include <stdio.h> class A { public: A() { printf("costruttore vuoto di A chiamato\n"); } A (const A&) { printf("costruttore di copia A chiamato\n"); } }; class B { A aA; public: B() { printf("costruttore vuoto di B chiamato\n"); } B (const B& b) : aA(b.aA) // provare ad eliminare : aA(b.aA) { printf("costruttore di copia B chiamato\n"); } }; main() { B aB; B anotherB(aB); }senza
: aA(b.aA)
il costruttore di copia di A
non viene chiamato e nel membro aA dell'oggetto anotherB
non viene copiato l'oggetto membro aA dell'oggetto aB
.
Piuttosto viene richiamato il costruttore vuoto A() sul membro aA
di anotherB (provare per credere):
output con la riga B (const B& b) : aA(b.aA)
costruttore vuoto di A chiamato
costruttore vuoto di B chiamato
costruttore di copia A chiamato
costruttore di copia B chiamato
output con la riga B (const B& b)
costruttore vuoto di A chiamato
costruttore vuoto di B chiamato
costruttore vuoto di A chiamato
costruttore di copia B chiamato