PROGRAMMIAMO
C++ - Costruttore e distruttore
Programma per la gestione di un conto corrente

Supponiamo ora di voler scrivere un programma a menu per la gestione di un conto corrente bancario. Possiamo dunque definire una classe conto così strutturata:

class conto {
    private:
        double somma_depositata,     //somma depositata sul conto
                tasso_interesse;          // tasso di interesse annuo 

    public:
        void set_deposito(double);    // assegna un valore alla somma depositata
        void set_tasso(double);        // assegna un valore al tasso di interesse
        double get_deposito();        // legge il valore della somma presente sul conto
        double get_tasso();            // legge il valore del tasso di interesse
        void versamento(double);    // effettua il versamento di una somma sul conto
        int prelievo(double);            // effettua un prelievo dal conto
        void aggiornamento();        // aggiorna annualmente il deposito calcolando l'interesse
};

I metodi della classe conto dovrebbero essere abbastanza esplicativi (lasciamo come utile esercizio al lettore la loro scrittura). Una sola precisazione è necessaria a proposito della funzione prelievo, la quale torna 1 se il prelievo è andato a buon fine (cioè se sul conto è presente la somma da prelevare), mentre torna 0 se non è stato possibile effettuare il prelievo (perché sul conto non è disponibile la somma richiesta).

Il programma principale, strutturato, come si è detto, a menu potrebbe essere il seguente:

int main(int argc, char *argv[])
{
conto cc;
int scelta;
double deposito, tasso, somma;

cout<<"Scegli l'operazione da effettuare:\n";
cout<<"1) assegna somma depositata\n2) assegna tasso di interesse\n";
cout<<"3) visualizza somma depositata\n4) visualizza tasso di interesse\n";
cout<<"5) effettua versamento\n6) effettua prelievo\n7) aggiorna conto\n";
cout<<"Scelta? ";
cin>> scelta;

switch (scelta)
    {
    case 1:
        cout<<"Somma depositata: ";
        cin>>deposito;
        cc.set_deposito(deposito);
        break;
    case 2:
        cout<<"Tasso di interesse: ";
        cin>>tasso;
        cc.set_tasso(tasso);
        break;
    case 3:
        cout<<"Somma sul conto: "<<cc.get_deposito()<<"\n";
        break;
    case 4:
        cout<<"Tasso di interesse: "<<cc.get_tasso()<<"\n";
        break;
    case 5:
        cout<<"Somma da versare: ";
        cin>>somma;
        cc.versamento(somma);
        break;
    case 6:
        cout<<"Somma da prelevare: ";
        cin>>somma;
        if (!cc.prelievo(somma))
            cout<<"Errore: somma non disponibile sul conto.\n";
        break;
    case 7:
        cc.aggiornamento();
        cout<<"Aggiornamento annuale effettuato.\n";
        break;
    }
}

Osserviamo che, affinché il programma funzioni correttamente, è necessario che, prima di eseguire qualsiasi altra operazione, venga depositata una somma iniziale sul conto (con set_deposito) e venga assegnato un valore al tasso di interesse (con set_interesse). In caso contrario le proprietà (i dati) somma_depositata e tasso_interesse non hanno alcun valore e i risultati di ogni altra operazione sono privi di senso.

Questo problema si verifica in C ogni qual volta si dichiara una variabile senza inizializzarla: il valore della variabile è indeterminato. Ciò può condurre potenzialmente a gravi errori, dei quali ci si accorge (talvolta) solo al momento dell'esecuzione del programma.

Costruttore di classe

Le classi prevedono in C++ uno strumento apposito per inizializzare le variabili della classe: il costruttore. Il costruttore (constructor) di una classe non è altro che un metodo speciale con le seguenti proprietà:

Vediamo in pratica come potrebbe essere definito il costruttore della classe conto:

class conto {
    private:
        double somma_depositata,     //somma depositata sul conto
                tasso_interesse;          // tasso di interesse annuo 

    public:
        conto() {
            somma_depositata = 0;
            tasso_interesse = 0;
            }

        void set_deposito(double);    // assegna un valore alla somma depositata
        void set_tasso(double);        // assegna un valore al tasso di interesse
        double get_deposito();        // legge il valore della somma presente sul conto
        double get_tasso();            // legge il valore del tasso di interesse
        void versamento(double);    // effettua il versamento di una somma sul conto
        int prelievo(double);            // effettua un prelievo dal conto
        void aggiorna_conto();        // aggiorna annualmente il deposito calcolando l'interesse
};

Nell'esempio qui sopra il costruttore è stato definito all'interno della definizione della classe conto. Come per qualsiasi altra funzione, sarebbe stato anche possibile definirlo all'esterno della classe, lasciando nella classe il solo prototipo della funzione, in questo modo:

class conto {
    private:
        double somma_depositata,     //somma depositata sul conto
                tasso_interesse;          // tasso di interesse annuo 

    public:
        conto() ;
        void set_deposito(double);    // assegna un valore alla somma depositata
        void set_tasso(double);        // assegna un valore al tasso di interesse
        double get_deposito();        // legge il valore della somma presente sul conto
        double get_tasso();            // legge il valore del tasso di interesse
        void versamento(double);    // effettua il versamento di una somma sul conto
        int prelievo(double);            // effettua un prelievo dal conto
        void aggiorna_conto();        // aggiorna annualmente il deposito calcolando l'interesse
};

conto::conto() // Si noti la ripetizione: il primo "conto" indica la classe, il secondo il costruttore
    {
    somma_depositata = 0;
    tasso_interesse = 0;
    }

Il costruttore, a differenza degli altri metodi, non viene mai chiamato esplicitamente in un programma. Esso viene invece invocato automaticamente al momento della dichiarazione:

conto cc;

In questo modo non appena viene dichiarato l'oggetto cc, automaticamente vengono azzerate le sue proprietà somma_depositata e tasso_interesse. Si osservi che non sarebbe stato possibile inizializzare tali variabili all'interno della dichiarazione della classe conto, in questo modo:

class conto {
    private:
        double somma_depositata=0,     // ERRORE!
                tasso_interesse=0;          // Inizializzazioni non consentite!
....

D'altra parte non sarebbe neppure stato possibile inizializzare somma_depositata e tasso_interesse all'inizio del main, dal momento che si tratta di membri private della classe conto, che non possono essere usati dall'esterno.

Costruttore con parametri e overloading di costruttori

Il costruttore usato nell'esempio precedente è molto semplice, poiché prevede solo la possibilità che i dati vengano inizializzati a zero. Una definizione di costruttore più interessante potrebbe essere la seguente:

conto::conto(double som, double dep)    
    {
    somma_depositata = som;
    tasso_interesse = dep;
    }

Questo costruttore ha due parametri, som e dep, che servono per inizializzare con valori a piacere le proprietà della classe. Questo costruttore viene invocato al momento della dichiarazione di un oggetto di tipo conto in questo modo:

conto cc(12500 , 0.05); //inizializza somma_depositata a 12500 e tasso_interesse a 0.05

Vi è anche la possibilità di definire più costruttori per la stessa classe, in questo modo:

class conto {
    private:
        double somma_depositata,     //somma depositata sul conto
                tasso_interesse;          // tasso di interesse annuo 

    public:
        conto() ;
        conto(double, double);
        void set_deposito(double);    // assegna un valore alla somma depositata
        void set_tasso(double);        // assegna un valore al tasso di interesse
        double get_deposito();        // legge il valore della somma presente sul conto
        double get_tasso();            // legge il valore del tasso di interesse
        void versamento(double);    // effettua il versamento di una somma sul conto
        int prelievo(double);            // effettua un prelievo dal conto
        void aggiorna_conto();        // aggiorna annualmente il deposito calcolando l'interesse
};

Si noti che in questo modo abbiamo due funzioni con lo stesso nome conto. In C++ è possibile definire due funzioni con lo stesso nome, purché esse differiscano per il numero e/o per il tipo dei parametri (non è invece possibile differenziare due funzioni con lo stesso nome in base al solo valore di ritorno). Il compilatore è in grado di stabilire in base alla chiamata a quale delle funzioni ci si sta riferendo. Questo meccanismo, su cui torneremo nel seguito, viene detto overloading di funzioni.

Nel nostro caso, se dichiariamo un oggetto in questo modo:

conto cc;

viene automaticamente invocato il costruttore senza parametri (quello che inizializza i valori a zero). Invece dichiarando l'oggetto così:

conto cc(12500 , 0.05);

viene invocato il costruttore con parametri.

Scrittura compatta di un costruttore

Il C++ consente anche di scrivere in modo più compatto il codice di un costruttore, come mette in evidenza il seguente confronto:

Scrittura estesa Scrittura compatta
conto::conto()
    {
    somma_depositata = 0;
    tasso_interesse = 0;
    }
conto::conto() : somma_depositata(0), tasso_interesse(0) {}
conto::conto(double som, double dep)    
    {
    somma_depositata = som;
    tasso_interesse = dep;
    }
conto::conto(double som, double dep) : somma_depositata(som), tasso_interesse(dep) {}

L'esempio seguente si riferisce al caso in cui il costruttore venga definito fuori dalla classe (lasciando nella classe solo il prototipo). Volendo invece dichiarare il costruttore all'interno della classe, la scrittura compatta è la seguente:

class conto {
    private:
        double somma_depositata,     //somma depositata sul conto
                tasso_interesse;          // tasso di interesse annuo 

    public:
        conto() : somma_depositata(0), tasso_interesse(0) {}
        conto(double som, double dep) : somma_depositata(som), tasso_interesse(dep) {}
        void set_deposito(double);    // assegna un valore alla somma depositata
        void set_tasso(double);        // assegna un valore al tasso di interesse
        double get_deposito();        // legge il valore della somma presente sul conto
        double get_tasso();            // legge il valore del tasso di interesse
        void versamento(double);    // effettua il versamento di una somma sul conto
        int prelievo(double);            // effettua un prelievo dal conto
        void aggiorna_conto();        // aggiorna annualmente il deposito calcolando l'interesse
};

 

Distruttore

Il C++ permette di definire un altro metodo particolare con funzioni opposte a quelle del costruttore, detto distruttore (destructor). Il distruttore ha lo stesso nome della classe a cui appartiene, preceduto però dal carattere ~ (detto tilde, combinazione di tasti ALT+126 sui PC Windows).

Il distruttore di una classe viene invocato automaticamente quando l'oggetto di appartenenza perde visibilità (di solito quando termina il programma) e la memoria occupata dai suoi attributi viene liberata. A differenza del costruttore, il distruttore non trova un largo impiego, tranne nelle classi che gestiscono la memoria dinamicamente: in questi casi il distruttore consente di rilasciare lo spazio di memoria allocato per l'oggetto.

Il distruttore non può mai avere né argomenti né valore di ritorno. Per esempio nel caso della nostra classe conto potremmo aggiungere un distruttore ~conto() nel seguente modo:

class conto {
    private:
        double somma_depositata,     //somma depositata sul conto
                tasso_interesse;          // tasso di interesse annuo 

    public:
        conto() ; //costruttore della classe senza parametri
        conto(double, double); // costruttore con parametri
        ~conto(); // distruttore
        void set_deposito(double);    // assegna un valore alla somma depositata
        void set_tasso(double);        // assegna un valore al tasso di interesse
        double get_deposito();        // legge il valore della somma presente sul conto
        double get_tasso();            // legge il valore del tasso di interesse
        void versamento(double);    // effettua il versamento di una somma sul conto
        int prelievo(double);            // effettua un prelievo dal conto
        void aggiorna_conto();        // aggiorna annualmente il deposito calcolando l'interesse
};


conto::~conto()    
    {
    cout<<"Oggetto eliminato\n";
    }

Al termine dell'esecuzione del programma sul video compare il messaggio "Oggetto eliminato".

 

link precedente - successiva

Sito realizzato in base al template offerto da

http://www.graphixmania.it