Newsgroup: it.comp.lang.c++
Titolo: Costruttore

Ho necessità di costruire degli oggetti che si "autoinseriscano" in una
lista. Io ho pensato di mettere l'istruzione per l'inserimento alla fine del
costruttore, ad esempio:
class Oggetto
{
 ........
 public:
   Oggetto (........);
 .........
};
.................
Oggetto::Oggetto (.......)
{
 /* inizializzazione degli attributi */
.....................
 inserisciInLista (this);
}
Una cosa del genere funziona oppure la variabile "this" non è ancora del
tutto definita? Vorrei evitare di avere problemi con gli oggetti una volta
che sono nella lista. Grazie per l'aiuto
                                                    Marchisio Stefano

che sono nella lista. Grazie per l'aiuto
Il valore di "this" e' gia' definito all'inizio del
codice del costruttore (anche prima ancora, durante
l'inizializzazione dei membri), ma l'oggetto puntato
da "this" non e' considerato "costruito" sino al
completamento del costrutture, in particolare per
quello che riguarda le funzioni virtuali e
distruttore. Es:
------------------ Inizio
  #include <iostream>
  class Figura
  {
    public:
      virtual void disegna() = 0;
      static void disegna_tutto();
      Figura();
      virtual ~Figura();
 
    private:
      static Figura *first,*last; // Lista di tutte le figure
      Figura *prev,*next;         // Link di lista
      // taboo
      Figura(const Figura&);
      Figura& operator=(const Figura&);
  };
  Figura *Figura::first = 0;
  Figura *Figura::last = 0;
 
  void Figura::disegna_tutto()
  {
    std::cout << "Inizio ridisegno\n";
    for (Figura *f=first; f!=0; f=f->next)
      f->disegna();
    std::cout << "Fine ridisegno\n";
  }
 
  Figura::Figura() : prev(last),next(0)
  {
    // Inserisce in coda (ok usare "this")
    if (prev) prev->next=this;
         else first=this;
    last=this;
    // Ridisegna tutte le figure (ERRORE!)
    disegna_tutto();
  }
  Figura::~Figura()
  {
    // Toglie dalla lista
    if (prev) prev->next=next;
         else first=next;
    if (next) next->prev=prev;
         else last=prev;
    // Ridisegna tutte le figure (OK)
    disegna_tutto();
  }
  ///////////////////////
  class Triangolo : public Figura
  {
    void disegna()
    {
      std::cout << "Triangolo::disegna\n";
    }
  };
  class Quadrato : public Figura
  {
    void disegna()
    {
      std::cout << "Quadrato::disegna\n";
    }
  };
  ///////////////////////
  int main()
  {
    std::cout << "Inizio\n";
    Triangolo *t = new Triangolo;
    std::cout << "Dopo creazione triangolo\n";
    Quadrato *q = new Quadrato;
    std::cout << "Dopo creazione quadrato\n";
    delete t;
    std::cout << "Dopo eliminazione triangolo\n";
    delete q;
    std::cout << "Dopo eliminazione quadrato\n";
    return 0;
  }
------------------ Fine
Nel costruttore di Figura l'istanza viene messa in
una lista globale di tutte le figure e per fare
questo viene usato "this". Questa parte non comporta
problemi come non comporterebbe problemi passare
"this" ad altre classi o funzioni purche' queste
usino il valore di "this" ma aspettino prima di usare
l'oggetto puntato.
Infatti nel codice che ho scritto e' presente un
errore logico... dopo aver inserito l'oggetto viene
chiamata la funzione statica "disegna_tutto" che
usa tutti gli oggetti in lista, incluso il corrente
di cui pero' non e' ancora stato completato il
costruttore!
Per questo motivo alcuni compilatori avvisano
sull'uso di this se this viene utilizzato nella
lista di inizializzazione.
Se la chiamata a "disegna_tutto" viene fatta nel
main il problema scompare.
In generale io ti consiglierei di evitare di avere
parti "esterne" a cui tu passi puntatori ad oggetti
che ancora non sono stati costruiti completamente;
ci sono alcuni casi in cui pero' io ho trovato
comoda questa tecnica (in particolare passare "this"
ad una base della classe stessa nella lista di
inizializzazione).
Nota che questo comportamento di costruttore/funzioni
virtuali e' abbastanza caratteristico del C++ e non
si riscontra in altri linguaggi object-oriented.
Nota anche che l'errore logico del costruttore e'
doppio... se infatti per qualche ragione la funzione
disegna_tutto dovesse lanciare una exception il
distruttore della classe non verrebbe eseguito
(visto che non e' stato completato il costruttore)
e l'istanza non verrebbe rimossa dalla lista con
conseguenze disastrose per il programma.
HTH
Andrea

Grazie, sei stato molto chiaro. Ciao
                                            Marchisio Stefano