PDA

Visualizza la versione completa : [C++] metodo distruttore


YuYevon
15-02-2009, 17:51
Ciao a tutti!

Mi ritrovo con un piccolo problema di natura più concettuale che pratica che riguarda i metodi distruttori relativamente al linguaggio C++; purtroppo studio il C++ da poco più di 3 mesi e in maniera "discontinua" - causa studio di tante altre cose - e a volte finisco col farmi venire dei dubbi apparentemente banali.

La mia domanda essenzialmente è: qual è, di preciso, lo scopo di un metodo distruttore all'interno di una classe?

Mi spiego meglio.

Da quello che so, un distruttore è un particolare metodo di una classe che occorre quando bisogna compiere delle operazioni esplicite per la distruzione di un oggetto come la deallocazione della memoria precedentemente allocata per un particolare campo dell'oggetto o la chiusura di uno stream aperto. Ma quello che mi chiedo è: a parte le istruzioni che vengono scritte esplicitamente nel corpo del distruttore stesso, questo esegue anche delle operazioni predefinite stabilite magari dallo standard del linguaggio?

In estrema sintesi, avrebbe senso dichiarare (e definire) un distruttore - in una classe - esattamente come questo:



~nome_classe() { };


o sarebbe inutile?

Nella fattispecie del mio problema, io ho implementato una classe per costruire un albero binario (per l'algoritmo di Huffman) che ha questi attributi (la classe si chiama "Nodo" e "Byte" è un carattere senza segno):



unsigned int peso;
Byte valore;
Nodo * figlio_sx;
Nodo * figlio_dx;


e ho definito un metodo dal nome pittoresco "distruggiAlbero()" col quale dealloco ricorsivamente lo spazio in memoria allocato per tutti i puntatori ai figli dei vari nodi dell'albero (figlio_sx e figlio_dx), dalle foglie alla radice.

Avendo definito questo metodo, mi serve a qualcosa definire anche un distruttore standard come quello che ho scritto prima?

Scusate se mi sono dilungato ma volevo essere quanto più chiaro possibile (e spero di esserci riuscito!)

shodan
15-02-2009, 18:26
Originariamente inviato da YuYevon
La mia domanda essenzialmente è: qual è, di preciso, lo scopo di un metodo distruttore all'interno di una classe?

Garantire il cleanup delle risorse.

Originariamente inviato da YuYevon
In estrema sintesi, avrebbe senso dichiarare (e definire) un distruttore - in una classe - esattamente come questo:


~nome_classe() { };



Come sempre, dipende. Se non si intende ereditare dalla classe, un distruttore vuoto ha poco senso. Se però si intende derivare dalla classe, il distruttore dev'essere virtual (pena comportamento indefinito.) anche se vuoto.

Nel tuo caso specifico, il fatto che tu abbia implementato una "distruggiAlbero()" non garantisce che all'uscita dello scope dell'oggetto, l'albero venga distrutto.
Inserendo il metodo nel distruttore (ovviamente con i controllo che l'albero esista ancora o no.) garantisce che "distruggiAlbero()" sia invocata almeno una volta.
Se questo sembra rindondante, occorre ricordare che l'introduzione delle eccezioni in C++ ha cambiato il modo di programmare. Se viene lanciata un'eccezione prima dell'invocazione di "distruggiAlbero()" e tale eccezione non è gestita, "distruggiAlbero()" non è mai invocata, mentre il distruttore si.
E il concetto base del paradigma RAAI.
Un esempio è fstream::~fstream() dove viene invocata la fstream::close()

KrOW
15-02-2009, 18:52
I distruttori non sono altro che metodi chiamati subito prima della distruzione di un oggetto ... Se tu non definisci un distruttore in una classe, il compilatore lo farà al posto tuo col corpo vuoto. I distruttori servono soprattutto quando uno dei membri della classe è un puntatore. In caso che, durante la vita dell' oggetto, venga allocata memoria tramita il puntatore, è compito del distruttore assicurare che alla distruzione dell' oggetto venga deallocata la memoria (ovviamente in questo caso è compito del programmatore implementare correttamente il distruttore). Ad esempio nel tuo caso, ti consiglierei di sostituire il metodo distruggiAlbero() con il distruttore (o al limite fare in modo che il distruttore richiami distruggiAlbero() ) . . . Perchè se ti dimentichi di richiamare il metodo distruggiAlbero() ed il tuo oggetto esce dal suo scope (e quindi, a meno che non sia statico , viene distrutto), l' oggetto in se stesso viene distrutto ma incorri in un errore di memory leak . . . Spero di essere stato chiaro . . . Ciao

YuYevon
15-02-2009, 19:37
Grazie per le risposte!

Comunque il punto è che il metodo "distruggiAlbero()" lo richiamo esplicitamente io (all'uscita del programma) sul nodo radice dell'albero stesso, e il metodo poi si "propaga" ricorsivamente su tutto l'albero, deallocando lo spazio ovviamente prima per i puntatori ai figli del penultimo livello dell'albero (cioè quello prima delle foglie) e poi continua verso i livelli sempre più alti fino alla radice.

Quindi il fatto che il metodo venga richiamato è sicuro dato che sono io a richiamarlo esplicitamente. Del resto, sostituirlo con il distruttore o fare in modo che venga richiamato dal distruttore mi risulta un po' troppo difficile; in realtà lo avevo anche fatto ma la cosa mi aveva richieto un po' troppi casini (in particolare una variabile booleana dichiarata come attributo della classe che valeva false per tutti i nodi tranne che per la radice, cosa che francamente non mi piaceva per nulla).

shodan
15-02-2009, 20:31
Quindi il fatto che il metodo venga richiamato è sicuro dato che sono io a richiamarlo esplicitamente.

Sicuro?


Albero albero;
// codice vario di cui una funzione di libreria lancia un'eccezione.
albero.distruggiAlbero();


distruggiAlbero() non sarà mai invocata.

Edit.
Orrori di ortografia.

YuYevon
15-02-2009, 20:45
Guarda avevo già fatto delle prove in tal senso e ora ho anche riprovato mettendo delle istruzioni di stampa nel corpo del metodo in questione ed effettivamente mi vengono stampati i messaggi a video, quindi il fatto che venga richiamato è sicuro...

XWolverineX
15-02-2009, 20:57
Prova questo codice



Albero albero;
throw;
albero.distruggiAlbero();

YuYevon
15-02-2009, 21:17
Originariamente inviato da XWolverineX
Prova questo codice



Albero albero;
throw;
albero.distruggiAlbero();



In questo caso il programma mi si interrompe proprio in corrispondenza dell'istruzione throw; diciamo che non è che pregiudica la chiamata al metodo, semplicemente si blocca proprio il programma, infatti se provo a mettere l'istruzione tra le prime righe si interrompe subito...

Comunque sulla gestione delle eccezioni non sono molto preparato... lessi qualcosa dal manuale qualche tempo fa autonomamente ma non ho mai fatto alcuno studio vero e proprio in merito. All'università non c'è un vero e proprio corso sul C++, viene incluso all'interno di un altro ben più vasto... ed è per questo che non sono ferratissimo sul linguaggio, pur avendo partorito un codice di 700 righe e più proprio per questo progetto... forse anche alla luce di questo i problemi che mi pongo esulano da quelle che sono le mie competenze in merito.

KrOW
15-02-2009, 23:55
Premetto che non sono un esperto in alber binari . . . Ma se allochi i nodi dinamicamente, dal distruttore del nodo/radice è possibile cancellare i nodi foglie richiamando solo delete:


~node()
{
if(figlio_sx)
delete figlio_sx;

if(figlio_dx)
delete figlio_dx;
}

Cosí otterresti l' effetto desiderato visto che viene richiamato il distruttore di ogni nodo . . .

MItaly
15-02-2009, 23:59
Originariamente inviato da YuYevon
In questo caso il programma mi si interrompe proprio in corrispondenza dell'istruzione throw;
Sì, ma se ci fosse un catch nella funzione chiamante il programma potrebbe continuare a funzionare e l'albero non verrebbe deallocato. È così che nascono i memory leaks.

Loading