Newsgroup: it.comp.lang.c++
Titolo: dubbio su uso del polimorfismo

Saluti di nuovo.
Scrivendo del codice sono stato preso da un dubbio riguardo
la correttezza di quello che faccio.
Per illustrare la situazione posto il codice (mi scuso se e'
inutilmete lungo e spero non abbia bisogno di commenti):
#include <iostream>
class Receiver;
class Manager
{
    friend class Receiver;
  private:
    static Receiver* receiver_;
    static void register_receiver(Receiver* the_receiver)
    {
       receiver_ = the_receiver;
    }
  public:
    void raise_callback();
};
Receiver* Manager::receiver_ = 0;
class Receiver
{
   protected:
      friend class Manager;
      virtual void on_callback() = 0; // e' chiamata da Manager
   public:
      Receiver()
      {
         // si registra nel Manager
         Manager::register_receiver(this);
      }
};
void Manager::raise_callback()
{
   receiver_->on_callback();
}
class Test: public Receiver
{
   protected:
      void on_callback()
      {
         std::cout << "Test::on_callback\n";
      }
   public:
      Test()
      {
      }
};
int main()
{
   Manager the_manager;
   Test the_receiver;
   the_manager.raise_callback();
}
Quello di cui non sono sicuro e' se va bene chiamare
register_receiver nel costruttore di Receiver oppure
se vada fatto nel costruttore di Test.
Cosi' come e' funziona correttamente con il BCC 5.5.
Saluti e grazie  Manlio Perillo

 wrote in message
news:RmPy9.9243$XO.3...@news2.tin.it...

Il fatto e' che eseguendo la register_receiver, nel costruttore della classe
base, il manager lavorera' in quel punto,con un tipo base, e non con il tipo
derivato.
Nel tuo caso non corri rischi data la struttura semplice del tuo codice, ma
in casi un po piu' complessi la cosa non funziona piu' e saresti costretto
ad eseguire la registrazione nel costruttore della derivata come
nell'esempio sotto:
#include <iostream>
class Receiver;
typedef int (*fp)(Receiver*);
int fpFirst(Receiver* pRec);
class Manager
{
    friend class Receiver;
    friend class Test;
  private:
    static Receiver* receiver_;
    static void register_receiver(Receiver* the_receiver)
    {
  fp aFp = fpFirst;
       receiver_ = the_receiver;
  aFp(receiver_);
    }
  public:
    void raise_callback();
};
Receiver* Manager::receiver_ = 0;
class Receiver
{
   protected:
      friend class Manager;
      friend int fpFirst(Receiver*);
      virtual void on_callback() = 0; // e' chiamata da Manager
      virtual void initialize() = 0; // e' chiamata dal Manager in fase di
registrazione
   public:
      Receiver()
      {
         // si registra nel Manager
  Manager::register_receiver(this);
      }
};
void Manager::raise_callback()
{
   receiver_->on_callback();
}
class Test: public Receiver
{
 int foo;
   protected:
      void on_callback()
      {
  std::cout << "Test::on callback\n" << std::endl;
      }
      void initialize()
      {
  std::cout << "Test::initialize: " << foo  << std::endl;
      }
   public:
      Test()
      {
  foo = 10000;
  //Manager::register_receiver(this);
      }
};
int main()
{
   Manager the_manager;
   Test the_receiver;
   the_manager.raise_callback();
 return 0;
}
int fpFirst(Receiver *pRec){
 std::cout << "I'm fpFirst()" << std::endl;
 pRec->initialize();
}
La cosa che mi lascia un po perplesso e' che stando a come me la ricordavo
io, se un metodo virtuale viene chiamato all'interno del costruttore della
base, dovrebbe venire eseguito il metodo della base, dato si che l'oggetto
derivato ancora non risulta costruito completamente, ma invece compilando
con il g++ 3.0.4 e 2.95.3 viene comunque eseguito il metodo della
derivata(non completamente costruita).
Forse ricordavo male.
---
Ivan Molella
ICQ: 162760950

 ICQ: 162760950
======================================================
Rony Cesana
Sistemi Informativi srl - Gruppo Liberologico
via F. Goya 2 - 56010 loc. Ghezzano - Pisa Italy
tel.: 
+39 050 877088
 - fax: 
+39 050 8755498
pers.: 
+39 347 2582666
e-mail: 
r.ce...@liberologico.com
www.liberologico.com
======================================================

 
http://web.tiscali.it/fanelia/cpp-faq-en/ctors.html#faq-10.7
:
Cosa dice lo standard a tal proposito?
Questo comportamento e' da considerarsi implementation  dependent , o non
conforme allo standard?


Rispondo incautamente senza aver letto il codice :)
Pero' ricordi bene: la chiamata ad una funzione virtuale nel costruttore
della classe base provoca la chiamata alla funzione della classe base.
Forse sei riuscito mettere in piedi un meccanismo per cui riesci a
costruire completamente anche la derivata.
L'unico capace di tanto che io abbia mai visto e' Carlo Pescio
nell'articolo sotto citato:
http://www.eptacom.net/pubblicazioni/pub_it/nl_8.html

 
http://www.eptacom.net/pubblicazioni/pub_it/nl_8.html
Ho letto, l'articolo, veramente interessante, ma non ho architettato uno
stratagemma di quel tipo, penso invece che si tratti di un comportamento non
standard del compilatore di cui non mi sono mai accorto prima.....e forse
neppure gli sviluppatori.....:):)

 costruita). Forse ricordavo male.
Lo Standard dispone che le chiamate a funzioni virtuali durante
l'esecuzione di un costruttore o di un distruttore vangano risolte con le
funzioni definite nel contesto della classe che si sta costruendo o
distruggendo, o definite in una sua classe base.
Se le funzioni sono dichiarate *virtuali pure* il comportamento è
*indefinito*.
Max


Il tuo codice è corretto a norma di standard. Lo standard afferma che il
dispatching virtuale è disabilitato all'interno dei costruttori e dei
distruttori, e se tu provassi a chiamare on_callback il compilatore ti
segnalerebbe errore. Quello che fai è chiamare una funzione virtuale al di
fuori di un costruttore su un puntatore a classe base. Un problema potrebbe
sorgere se cercassi di usare tale puntatore prima che l'oggetto fosse
completamente costruito, ma non è questo il caso.
Ciao,
--
----------------------------
Fulvio Esposito
"La differenza tra teoria e pratica e', in pratica, un po' piu' che in
teoria"
----------------------------


Conoscevo questo comportamento, solo che mi era venuto il dubbio.
Grazie per la conferma  Manlio Perillo

 se vada fatto nel costruttore di Test.
dovrebbe essere corretto, anche se non ci metterei la mano sul fuoco.
Il problema di richiamare funzioni virtuali nel ctor della classe base 
non sussiste: infatti esse sono richiamata *fuori* dal costruttore, 
quando tutto l'oggetto e` gia` costruito e le funzioni virtuali sono 
gia` state ridefinite dalle derivate.
L'unica riga a cui bisogna stare un po' piu` attenti e` questa:
      Receiver()
      {
         // si registra nel Manager
         Manager::register_receiver(this);
      }
dovrebbe essere corretta, ma per una ragione un po' subdola. Nota che tu 
usi Receiver::this (spero si capisca cosa intendo, anche se non e` la 
notazione corretta), ma non e` affatto garantito che esso sia uguale a 
Test::this (basta fare qualche prova in caso di ereditarieta` multipla, 
ad esempio); fortunatamente e` garantito che Receiver::this punta alla 
componente Receiver di Test, anche in caso di ereditarieta` 
multipla/virtuale, quindi fin tanto che utilizzi solo puntatori a 
Receiver non dovrebbero esserci problemi.
In pratica, sempre approfittando dell'abuso di notazione di cui sopra, 
potrebbe capitare che:
   (void*)Receiver::this != (void*)Test::this
pero` siamo anche sicuri che
   (Base*)Receiver::this == (Base*)Test::this
nota che se chiamassi Manager::register_reciver() all'interno di Test() 
avresti una conversione implicita da Test* a Base*, quindi passeresti 
alla funzione lo stesso indirizzo che gli passi da Receiver().
Ripeto: non ci metto la mano sul fuoco ma sono ragionevolmente sicuro di 
quello che ho scritto; forse non sono stato chiarissimo, ma non riesco a 
partorire nulla di meglio... ;-)
ciao Anto
-- 
"Computer science is not about computers any more than astronomy
is about telescopes." -- EW Dijkstra

 costruita). Forse ricordavo male.
ti ricordi bene, pero` non consideri una cosa: il metodo raise_callback() 
e di conseguenza on_callback() sono chiamati all'interno di main(), 
quando l'oggetto e` completamente costruito. Nel costruttore di Receiver 
viene soltanto utilizzato this, senza effettuare alcuna chiamata, quindi 
questo problema non sussiste.


 e di conseguenza on_callback() sono chiamati all'interno di main(),
Si, questo lo so, ma nel mio contro esempio, invocavo nella "rigister" del
Manager una initialize, allo scopo di far notare esplicitamente che al
momento della register si stava lavorando sull' oggetto base e non sulla
derivata(anche se l'inivocazione di tale metodo in base allo standard
avrebbe sortito lo stesso effetto sia sulla base che sulla derivata,ma non
con il gcc....:) )
Nel suo caso la differenza e' nulla, ma in un eventuale caso di
inizializzazioni piu' complesse, ci poterebbero essere dei subdoli effetti
indesiderati, se non si sta abbastanza attenti, come nell' esempio di
sotto(che ovviamente e' un esmpio di pessima  programmazione)
#include <iostream>
class Receiver;
typedef int (*fp)(Receiver*);
int fpFirst(Receiver* pRec);
class Manager
{
    friend class Receiver;
    friend class Test;
    friend int fpFirst(Receiver*);
  private:
    static Receiver* receiver_;
  static int current;
    static void register_receiver(Receiver* the_receiver)
    {
    fp aFp = fpFirst;
      receiver_ = the_receiver;
   aFp(receiver_);
    }
  public:
    void raise_callback();
};
Receiver* Manager::receiver_ = 0;
int Manager::current = 0;
class Receiver
{
   protected:
      friend class Manager;
      friend int fpFirst(Receiver*);
      virtual void on_callback() = 0; // e' chiamata da Manager
   int current;
   public:
      Receiver():current(0)
      {
         // si registra nel Manager
     Manager::register_receiver(this);
      }
};
void Manager::raise_callback()
{
   receiver_->on_callback();
}
class Test: public Receiver
{
   protected:
      void on_callback()
      {
    std::cout << "Test::on callback on current: <" << current << ">\n" <<
std::endl;
      }
   public:
      Test()
      {
    current = 0; // Errore current e' gestito dalla base e dal Manager
     //Manager::register_receiver(this);
      }
};
int main()
{
   Manager the_manager;
   Test the_receiver;
   the_manager.raise_callback();
 return 0;
}
int fpFirst(Receiver *pRec){
 std::cout << "I'm fpFirst()" << std::endl;
  Manager::current++;
 pRec->current = Manager::current;
}


Sei stato chiarissimo ed il dubbio mi era venuto proprio facendo un discorso
del genere.


 a chiamare on_callback il compilatore ti segnalerebbe errore.
Questo non e' del tutto esatto: all interno di costruttori e
distruttori di classi base il dispatching virtuale e' effettuato come
se il tipo effettivo dell'oggetto fosse la classe di cui e' in
esecuzione il costruttore/distruttore.
Ciao,
Nicola