Newsgroup: it.comp.lang.c++
Titolo: Aiuto su uso distruttori virtuali

Salve amici,
sto progettando un programma di riconoscimento vocale e sto cercando,
nel contempo, di "wrappare" una serie di API appartenenti ad un
particolare SDK con una mia gerarchia di classi.
Vorrei studiare la cosa in modo che risultasse decente e quindi chiedo
a voi un grosso aiuto. Premetto che l'ambiente di sviluppo utilizzato
e' il C++ Builder 5.0 e premetto anche che non conosco bene il C++,
per cui sto andando molto ad intuito su certe cose, come l'uso dei
distruttori virtuali, perche' non mi sono ancora trovato in situazioni
che facessero risaltare l'importanza o meno di aspetti del linguaggio
come questo.
Il problema e' che il C++ Builder 5.0 consente di avere 4 livelli di
visibilita' dei membri delle classi (aggiunge, cioe', il livello
__published, che equivale, in fin dei conti, ad un public con qualcosa
in piu'. Leggendo o assegnando una proprieta' di questo tipo, si
scatena la funzione membro rispettivamente associata all'azione
(read/write)).
Il mio problema e' il seguente :
Ho costruito una classe base astratta di questo tipo (ditemi se ho
fatto bene) :
//
// classe astratta di interfaccia per le operazioni
// su oggetti ASR
//
class ASRObject {
      protected:
         bool   fActive ;
         virtual bool GetStatus() = 0 ;
         virtual void SetStatus( bool OpenOrClose ) = 0 ;
      public:
         ASRObject() ;
         virtual ~ASRObject() ;
         // indica se l'oggetto e' attivo o meno
         __property bool Active = { read    = GetStatus,
                                    write   = SetStatus } ;
} ;
e una classe derivata (ne costruiro' anche altre) di questo tipo :
//
// classe derivata per la gestione del DB dei fonemi
//
class ASRDatabase : public ASRObject {
      private:
         String fDatabaseName ;
         HDB    fHandle ;
      protected:
         bool GetStatus() ;
         void SetStatus( bool OpenOrClose ) ;
      public:
         ASRDatabase( const String DbName = "" ) ;
         ~ASRDatabase() ;
         // proprieta' pubbliche :
         // handle del database dei fonemi
         __property HDB  Handle = { read = fHandle } ;
} ;
L'uso dei costruttori che faccio nel modulo .cpp e' il seguente :
ASRObject::ASRObject()
{
    fActive = false ;
}
ASRDatabase::ASRDatabase( const String DbName = "" ) : ASRObject()
{
    fDatabaseName = DbName ;
}
Quello su cui mi sorgono dei dubbi e' l'uso dei distruttori.
Difatti, le funzioni protette di lettura/scrittura della proprieta'
"published" sono le seguenti :
bool ASRDatabase::GetStatus()
{
     return fActive ;
}
void ASRDatabase::SetStatus( bool OpenOrClose )
{
     switch ( OpenOrClose )
     {
            case true  : if ( !fActive )
                         {
                            fHandle = 
                            asrDbOpen( fDatabaseName.c_str()) ;
                            fActive = ( fHandle >= ASRERR_MAXERR ) ;
                         }
                         break ;
            case false : if ( fActive )
                         {
                            fActive = 
                            !( asrDbClose( fHandle ) == ASRERR_OK ) ;
                         }
                         break ;
     }
}
Ora, il distruttore, pur essendo stato definito virtuale (perche' gli
oggetti derivati vengono "chiusi" in modo diverso l'uno dall'altro),
ha comunque un comportamento unico per tutti, che e' quello di
impostare la proprieta' Active a false.
Io vorrei che si scatenasse la funzione associata all'assegnamento
(ovvero SetStatus( false )), ma che cio' avvenisse per tutti gli
oggetti.
ASRDatabase::~ASRDatabase()
{
     if ( fActive )
        Active = false ;
}
C'e' un modo in un distruttore virtuale di implementare, comunque, un
comportamento unico per tutte le classi derivate ?
Se ho detto una fesseria, fatemi capire perche' ! ;)
Grazie !!!
--
Andrea Laforgia (
andrea_...@bigfoot.com
)
-----------------------------------------------------
"There is nothing better for understanding a kangaroo
 than seeing it jump." [D.Grune, C.J.H. Jacobs]
-----------------------------------------------------


Se non mi sbaglio la parola "virtual" per il distruttore indica solo che in
una gerarchia di classi, con un puntatore alla classe base che in realtà
punta ad una derivata, il distruttore effettivamente chiamato sia quello
della classe derivata. Ma, a partire da quello, vengono chiamati anche i
distruttori di tutte le classi base.
Quindi, se vuoi un'azione comune per tutti i distruttori dei derivati,
scrivila nel costruttore della base (sempre virtuale, ovviamente).
Ciao
Domenico Barra

scrivila nel costruttore della base (sempre virtuale, ovviamente).
Grazie mille ;)


Grazie mille ;)
Smorzo subito l'entusiasmo : non va bene.
I problemi nascono quando imposto Active a false nel distruttore della
classe base, visto che il comportamento, in questo caso, dovrebbe
essere polimorfico, ma non lo e' :( (il compilatore denuncia difatti
che viene invocato un membro virtuale puro, cioe' tenta di chiamare
quello della classe base). Credo che il compilatore sia coerente con
la logica del linguaggio e non vedo altra strada che ripetere
l'esecuzione del codice comune in ciascuno dei distruttori delle
classi derivate.
--
Andrea Laforgia (
andrea_...@bigfoot.com
)
-----------------------------------------------
Dovro' prendere lezioni di Internet, 
se non sai di Internet non ti considera nessuno 
(Carmen di Pietro) 
-----------------------------------------------

classi derivate.
Si, se si tratta solo di SetStatus(false) passi pure, ma è la questione di
principio che non mi va giù.
Ciao
Domenico Barra

comportamento di un distruttore virtual _deve_ essere polimorfico.
E' una discussione che e' stata fatta recentemente.
Durante l'esecuzione del distruttore della base l'oggetto
non e' piu' un "derivato" e come tale non possono essere
eseguiti dei metodi "derivati".
Ogni metodo ha pro e contro... quello scelto dal C++ e'
di cercare di impedire che sia possibile eseguire un
metodo dopo che il corpo del distruttore e' stato eseguito.
HTH
Andrea
--
Andrea Griffini, Programmer
http://www.netservers.com/~agriff

metodo dopo che il corpo del distruttore e' stato eseguito.
Well done ;-)
Domenico Barra

base: è necessario definire SetStatus nella classe ASRObject, è chiaro.
Non puo' essere definita nella classe ASRObject, perche' SetStatus e'
diversa da classe derivata a classe derivata.


diversa da classe derivata a classe derivata.
Beh, allora questo è un altro paio di maniche... E non si tratta nemmeno
dello "stesso comportamento" per ogni classe derivata. In questo caso mi sa
che devi proprio scrivere a mano tutto il codice necessario, oppure:
1) delegare il codice "davvero" comune al distruttore della classe base, e
scrivere il codice che si comporta diversamente nei vari distruttori;
2) creare una funzione virtuale "pre-distruttrice", e ridefinirne il
comportamento in ogni derivazione. (ORRORE!) Es: 'My_obj.Destroy();'
Ma, ripeto, un buon uso dell'ereditarietà (vedi anche punto n.1) consente di
avere comportamenti corretti da parte del compilatore e del programma. Se
specifichi il comportamento che devono avere i vari distruttori, forse posso
darti una mano a scriverli :-)
Ciao
Domenico Barra