Newsgroup: it.comp.lang.c++
Titolo: Costruttori e notifica di fallimento

Un saluto a tutto il NG.
Per la realizzazione di un progetto per un esame ho la necessita'
di creare degli oggetti i cui costruttori possono fallire al verifi-
carsi di eventi anomali. Ogni costruttore dovrebbe avvisare colui
che ha creato l'istanza del fallimento avvenuto.
Il problema e' che non so quale alternativa scegliere (e soprattutto
se ne esistono altre):
- brutale: uso una variabile di stato accessibile dall'esterno dell'og-
getto mediante un metodo (tipo 'isOk()'). In questo modo colui che ha
creato l'istanza deve ricordarsi di controllare lo stato dell'oggetto
creato con il metodo opportuno;
- faccio in modo che il costruttore generi un'eccezione. Il mio docente
ha pero' detto che in questo modo potrebbero esserci dei problemi se
chi crea l'oggetto usa una 'new' (l'indirizzo restituito potrebbe essere
perduto);
- colto dalla disperazione metto una 'exit' nel costruttore (cosa orri-
pilante visto che le mie classi fanno parte di una libreria: chi la
usa rischia di ritrovarsi un programma che termina senza sapere perche').
Altro non mi e' venuto in mente, anche perche' non sono ferrato nella
programmazione in C++.
Ringrazio tutti coloro che vorranno darmi dei suggerimenti.
Federico
 

 usa rischia di ritrovarsi un programma che termina senza sapere perche').
Beh, al limite stampa un messaggio di errore significativo invece di
uscire tout court.
-- 
Maurizio Loreti                         
http://www.pd.infn.it/~loreti/mlo.html
Univ. of Padova, Dept. of Physics - Padova, Italy            
lor...@pd.infn.it

 cio'?
Ma se la new ha generato eccezione sicuramente non ha allocato memoria,
dico bene?
EMG

}
Penso che se lanci un'eccezione in un costruttore, il minimo che possa
succedere e' che new deallochi cio che ha allocato ... Vado a memoria ( e la
mia memoria e' pessima ! ) : se in un costruttore viene generata un
eccezione, tutte le variabili gia inizializzate (
implicitamente/esplicitamente ) vanno deinizializzate. E l'oggetto stesso (
se allocato dinamicamente da new ) dovrebbe essere deallocato ( non vedo
perche' non farlo ). Certo ci sono dei limiti:
class A {
	char	*s;
	A( char *s_) : s(s_ ) {
		throw "Sono un suicida !";
	}
};
class B {
	A	a;
	char	*ptr;
	B( ) : ptr(malloc(100)), s("pippo") { } 
}
B *b=new B;
Il funzionamento dovrebbe essere:
1) alloco spazio per B
2) inizializzo ptr( ad un indirizzo che punto ad uno spazio libero di 100
byte )
3) Inizializzo B
	3.bis B fallisce
4) richiamo il distruttore di ptr ( che essendo un puntatore NON ha
distruttore ! quindi in sostanza NON faccio nulla )
5) dealloco lo spazio per B
Insomma ho allocato spazio con malloc ma questo NON viene liberato.
Credo che sia buona norma non creare costruttori che possano
fallire. Se c'e' una qualche possibilita' di fallimento, delegare il tutto
ad una funzione membro, che verra chiamata dopo il costruttore.

creato con il metodo opportuno;
Tale soluzione non è valida.
Se viene chiamato il distruttore di un oggetto, non vi si può più
accedere.
Una soluzione che mi viene in mente è definire una classe tipo:
class Id { // ... };
la classe Id serve come 'reference' counter.
(ma va bene anche un int).
Ogni oggetto ha una propria Id (ex usando Id come membro statico)
in modo da essere univocamente determinato.
class FailureReport
{
  private:
    std::vector<Id> falliti;   // oggetti il cui distruttore ha fallito
  // ...
};
A questo punto basta che la tua classe usi un oggetto statico di tipo
FalilureReport come membro.
class TuaClasse
{
  private:
    static Id id;
    static FailureReport report;
    // ...
  public:
    static ...visualizza...
    // ...
};
Ogni volta che un distruttore fallisce la sua Id viene inserita in
FailureReport::falliti.
In questo modo il chiamante può esaminare l'oggetto FailureReport per
conoscere quali oggetti hanno fallito.
Ovviamente le cose vanno messe un pò a posto, spero che possa andare
bene.
Manlio Perillo

 Lanciare una eccezione in un distruttore è una cosa azzardata,
Guarda che si parlava di _costruttori_ (vedi anche il soggetto...)



Ho preso una svista!
Manlio Perillo

 lanciare l'eccezione; o usi un auto_ptr.
il problema non era questo, bensi' quando un "utente" usa la mia classe
mediante new. Il mio professore ha posto l'attenzione su questo caso:
try{
    classe c = new classe();
catch(...){
    /*  saltando qui rischio di perdere l'indirizzo ritornato da
        new e quindi non poter fare la delete. ma puo' succedere? e' vero
cio'?
    */
}
Federico

mi e' venuto in mente un'altro modo:
un metodo statico della mia classe, CreateIstance, che effettua la new.
la new non lancia eccezioni ma setta un bool (isOk). CreateInstance se e'
tutto a posto restituisce il puntatore (se isOk=true) oppure genero
un'eccezione (chiamando anche delete). che ne dite?
e' confuso... cerco di fare un esempio:
class A{
public:
    inline static A* createIstance(){
        A* pippo = new A();
        if isOk return pippo;
        delete pippo;
        throw qualcosa; /*o return null?? qual'e' meglio?*/
    }
private:
    inline A(){
        /*fai qualcosa*/
        if(/* tutto bene*/)
            isOk=true;
        else
            isOk=false;
    }
};
FEDERICO

 wrote in message
news:MSLv5.137376$Z05.1...@telenews.teleline.es...

Se generi un'eccezione in un costruttore lo spazio allocato dal new viene
automaticamente disallocato.
L'unico problema e' se all'interno del costruttore tu cerchi di allocare
oggetti con degli altri new.
Come diceva Maurizio Loreti usa degli auto_ptr che si disallocano da soli e
sei posto.
Visto che sei all'universita' (e quindi hai internet gratis) fatti un giro
sul sito della microsoft nella parte di MSDN
e cerca la stringa "Handling Exceptions in C and C++, Part 1"  (tra apici)
c'e' tutta una discussione proprio su questo.
Ciao Giacomo
Handling Exceptions in C and C++, Part 1

 
Penso esista una pena per chi usa questa tecnica ma non ricordo bene... mi sembra anche si perda il diritto al titolo di Programmatore :)))).
Ciao!
_______________________________________
 Luca Minudel   Consulente Informatico
 
luca.m...@nline.it
 Conegliano (TV)
 WWW    
http://www.nline.it/consulting


Non sono d'accordo.
Non c'è motivo per delegare ad una funzione membro la
costruzione di un oggetto.
Comunque non mi è molto chiaro cosa intendi, forse
class A
{
  public:
    A(//...);
    void init();
};
A* a = new A(..);
a->init();
?
Se è così, questo non è il modo migliore per procedere,
è un residuo della pre-exception-handling.
La cosa migliore è la "resource acquisition is allocation".
Ti consiglio di leggere l'appendice E del libro
"The C++ Programming Language" disponibile anche in
formato pdf alla home-page dello Stroustrup.
Manlio Perillo

sei posto.
Altra possibilita`...
Paolo
-------------------------------------------
  Paolo Marotta
 
http://web.tiscalinet.it/paolo_marotta/
-------------------------------------------

}
Se il costruttore classe() fallisce sollevando una eccezione, e`
garantito che il blocco allocato da new sia automaticamente
deallocato.
D'altronde senza questa accortezza le eccezioni sarebbero assai
meno utili.


 meno utili.
A quanto dice Eckel in "Thinking in C++" volume 2, capitolo 16, pg.384
riguardo alle eccezioni ed ai costruttori, se viene lanciata un'eccezione
nel costruttore non viene chiamato il ditruttore e questo e' il problema.
Quindi se la mia classe non alloca risorse che necessitano un distruttore
esplicito non dovrei avere problemi, giusto?
--
Ciao by Beppe


Non credo questa sia la soluzione migliore, perchč complicarsi
la vita?
Meglio fare:
MyClass::MyClass(...) : MemberPtr1(new T(...)), ... , {
  // se arriva qui tutto č andato bene
}
E' tutto piů semplice e coerente (a mio avviso).
Manlio Perillo

formato pdf alla home-page dello Stroustrup.
Mi sai indicare l'url ?

 Mi sai indicare l'url ?
L'home page dello Stroustrup è:
http://www.research.att.com/~bs/homepage.html
Cerca l'appendice nella sezione riguardante il libro
"The C++ Programming Language" third edition.
Manlio Perillo

}
Se i membri puntatore sono piu` di uno e fallisce il secondo,
il primo puntatore non sarebbe liberato...



Già, mi era sfuggito, scusa.
Comunque credo che si possa sempre delegare la acquisizione
ed inizializzazione in altro luogo
(ad esempio usare auto_ptr -- come già suggerito da altri --).
Questa mi sembra una soluzione più elegante.
Manlio Perillo

(ad esempio usare auto_ptr -- come già suggerito da altri --).
E` vero... in effetti non ho mai studiato gli auto_prt...
Grave pecca... lo so!
Solo che sono impegnato con quella sottospecie di
modello ad oggetti (che dovrebbe semplificare la vita)
di COM & C.   :(
