Newsgroup: it.comp.lang.c++
Titolo: costruttori, distruttore e oggetti dinamici

Ciao a tutti, ho questa classe con costruttore overloaded. Il 
costruttore base non ha parametri, e viene chiamato dal mio programma in 
fase di "training". Questa fase produce un file. Quando richiamo il 
programma (in fase di "testing") e gli passo il file creato prima, mi 
servo del secondo costruttore della classe.
Il mio problema. Il secondo costruttore č parametrizzato da alcuni 
oggetti dinamici (che mi servono per contenere i dati del file).
Ora, non sono un esperto di C++, ma mi sembra di capire che quegli 
oggetti li devo esplicitamente cancellare nel distruttore.
Visto che posso avere un solo distruttore, come faccio a fargli capire 
che, nel caso stia usando il primo costruttore, non ha nulla da 
distruggere, mentre quando chiamo il secondo deve farmi alcuni delete?
Nel concreto:
class Model {
public:
	Model(){}
 
	Model(RandLMFile* fin):
		info_(NULL), struct_(NULL), order_(0),
		main_event_(randlm::RandLMInfo::kNullEvent), 			 
aux_event_(randlm::RandLMInfo::kNullEvent),
		max_codes_(0),
		probCache_(NULL),
		log_quantiser_(NULL),
	        countCodeCache_(NULL) {
		//serie di chiamate a new del tipo
		//struct_ = new struct.....
		}
 
//...
private:
	RandLMInfo* info_;
     	RandLMStruct* struct_;
	//eccetera eccetera
}
se scrivo qualcosa del tipo:
     ~Model() {
     	delete info_;
     	delete struct_;
     	delete[] max_codes_;
     	delete probCache_;
     	delete log_quantiser_;
     	delete countCodeCache_;
     }
mi si pianta tutto nella fase di training, perche cerca di cancellare 
oggetti non inizializzati (giusto?), perň funziona bene nella fase di 
testing, col secondo.
Questo non aiuta nemmeno:
     ~Model() {
     	if(info_)
     		delete info_;
     	if(struct_)
     		delete struct_;
     	if(max_codes_)
     		delete[] max_codes_;
     	if(probCache_)
     		delete probCache_;
     	if(log_quantiser_)
     		delete log_quantiser_;
     	if(countCodeCache_)
     		delete countCodeCache_;
     }
Cosa si fa in questi casi?O cosa suggerite?
Grazie
F.

 distruggere, mentre quando chiamo il secondo deve farmi alcuni delete?
[CUT]
Cosě mi viene in mente di dichiarare una variabile
privata che identifichi la "fase" (testing
o altro) e farla testare da dentro
il distruttore per fare le varie delete.

mi si pianta tutto nella fase di training,
E perché mai? se quei puntatori sono inizializzati a 0 (cosě com'č
quando si dichiarano membri di una classe), non c'č alcun problema nel
chiamare la delete.

 Cosa si fa in questi casi?O cosa suggerite?
Devi inizializzare a zero quei puntatori, quando usi il costruttore 
senza argomenti. A quel punto puoi tranquillamente farci delete, che sul 
puntatore 0 non ha effetto.
E.

 distruggere, mentre quando chiamo il secondo deve farmi alcuni delete?
Possibilità 1:
metti nella sezione private un «const bool deleteStuff», che inizializzi
nel costruttore Model() a false e nel Model(RandLMFile*) a true; nel
distruttore, in base a quel valore, chiami delete o meno.
Nota che ho messo «const bool» per essere sicuri che nessuno possa
cambiare le carte in tavola.
In generale potresti usare un flag o un'enumerazione se dovessi avere
più casi che si possono combinare.
Possibilità 2:
Inizializzi a zero i puntatori quando chiami Model() (come peraltro fai
già per Model(RandLMFile*)), come ti hanno già fatto notare altri, e
chiami delete sul puntatore.
Anche qui suggerisco di usare const: scrivi, nella sezione privata:
	RandLMInfo* const info_;
	RandLMStruct* const struct_;
	// [...]
così ti pari le terga nel caso qualche parte della classe provasse a
cambiarne il valore, il che ti regalerebbe subito delle perdite di
memoria o dei segfault.
Possibilità 3:
Usare auto_ptr: auto_ptr è un template standard e molto comodo proprio
per questo genere di casi. Usa:
	#include <memory>
	// [...]
	const std::auto_ptr<RandLMInfo> info_;
	const std::auto_ptr<RandLMStruct> struct_;
In questo modo te ne puoi fregare del tutto del distruttore (così hai
una cosa in meno a cui pensare), perché gli auto_ptr procederanno ad
eliminare l'oggetto puntato automaticamente quando loro stessi vengono
distrutti. Usando const insieme ad auto_ptr, inoltre, impedisci che
qualcuno tolga la proprietà agli auto_ptr o li reinizializzi.
Unico problema è che per istanziare un auto_ptr<pippo> la classe pippo
deve essere definita; non puoi usare una classe con dichiarazione in
avanti, come:
class pippo;
// [...]
auto_ptr<pippo> puntaPippo;
Io ci sono rimasto malissimo quando l'ho scoperto... :-( Anche se forse
dipende dall'implementazione.
- ------
In conclusione, secondo me la soluzione migliore è 3 (perché non devi
usare nessun distruttore), in alternativa la 2 se non puoi usare
auto_ptr perché le classi puntate non sono definite. La 1 è una
soluzione tenuta su col fil di ferro, di cui io non mi fiderei.
Ciao,
- -Federico
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (GNU/Linux)
Comment: Using GnuPG with SUSE - 
http://enigmail.mozdev.org
iD8DBQFIjbiWBIpu+y7DlLcRAnrTAJ4ivQ22awPapHQlDNjPmgF0nMsl8QCgti7H
9aqyWHWsX+U2ue4iy9NsAik=
=UCCo
-----END PGP SIGNATURE-----

... 


 dichiarazione in avanti"?
Probabilmente riconosci meglio l'inglese «forward declaration» :-)
Il punto è che se provi il seguente codice:
#include <memory>
int main()
{
        class Pippo;
        Pippo* puntaPippo;
        std::auto_ptr<Pippo> autoPuntaPippo;
        return 0;
}
questo compila solo se commenti la riga con auto_ptr, cioè puntaPippo
non dà problemi, mentre autoPuntaPippo invece vuole che la classe sia
già definita.
Credo che il motivo fondamentale sia che auto_ptr richiede che
l'operatore delete della classe puntata sia definito prima di istanziare
un suo oggetto.
Ciao,
- -Federico
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (GNU/Linux)
Comment: Using GnuPG with SUSE - 
http://enigmail.mozdev.org
iD8DBQFIjsv6BIpu+y7DlLcRAngmAJ45uZo/crNDvYznipNRUwgpI2QnCACfY1wz
53n7D74sbb2rIQLJB9puyTM=
=YWYC
-----END PGP SIGNATURE-----