Newsgroup: it.comp.lang.c++
Titolo: Costruttori due volte... È questo corretto?

Costruttori due volte... È questo corretto?      
 
Mi piacerebbe sapere se sia consigliabile o non usare costruttori sullo
stesso
oggetto.  
Il codi ce sotto è un esempio piccolo di un grande programma dove sto
usando due costruttori diversi per inizializzare lo stesso oggetto. Sto
ottenendo un problema di un oggetto che cambia i contenuti di altri
oggetti.
Inoltre, compilatori diversi danno risultati diversi. g++ non dà errori
mentre SparcWorksC++ 4.0.1 dà.
Humberto.
#include <malloc.h>
class X
{
   public:
      X(void);
      X(const int);
   private:
     int *v;
};
X::X(void)
{
  ;
};
X::X(const int i)
{
   this->v = malloc(i*sizeof(int));
};
X::~X(void)
{
   free(this->v);
};
class Y
{
   public:
      Y(void);
      Y(const int);
   private:
      X objX;
};
Y::Y(void)
{
   ;
};
Y::Y(const int i)
{
   objX = X(i); // È questo corretto?
};

};
No, quest'ultima operazione non e' corretta perche' il C++ eseguira'
operator=() associato alla classe X. Siccome tu non hai definito
operator=() per la classe X, questo verra' generato di default dal
compilatore che eseguira' una shallow copy (in parole povere, verra'
copiato il puntatore contenuto in X ma non l'area puntata). In poche
parole, per far funzionare il tutto dovresti aggiungere alla classe X
il seguente codice:
class X
    {
    // ...
 
    X& operator=(const X& rhs);
    // ...
   private:
       int dimensione; // da aggiungere e impostare
    };
X& X::operator=(const X& rhs)
    {
    if (this != &rhs)
    for (int i = 0; i < dimensione; i++)
        v[i] = rhs.v[i];
    return (*this);
    }
Nota che sopra DIM non appare come data member della classe X e quindi
te lo devi impostare tu nel momento in cui allochi "v". Senza questa
informazione e' impossibile far funzionare il codice sopra.
I sintomi che hai dei diversi compilatori e' il fatto che parte della
memoria viene corrotta o persa ma alcuni compilatori possono essere
piu' sensibili di altri in determinate situazioni e piattaforme. In
ogni caso, resta il fatto che il problema e' nel tuo codice e non nel
compilatore.
Spero di esserti stato di aiuto.
Buona fortuna!
------------------------------------------------------------------------
Mario Graziosi
Professional Developer's Journal (PDJ) - 
http://www.edizioni-globali.com
FG&A srl - 
http://www.fga-software.com/it

Cominciamo dalla classe X.
Non =E8 chiaro cosa tu voglia fare, ma credo sia questo:
=20
	class X
	{
	   public:
	      X(void) { v=3DNULL; }
	      X(int i) { v =3D new int[i]; }
		~X() { if ( v!=3DNULL ) delete [] v; }
	   private:
	     int *v;
	};
Rispetto al tuo codice nota: ti eri dimenticato di dichiarare il
distruttore. L'accesso a v dalle funzioni membro non richiede la
sintassi this->v. Nel distruttore devi verificare che v sia
inizializzato prima di chiamare il delete. Quindi nel costruttore di
default devi inizializzare v a NULL. In C++ =E8 meglio usare new e =
delete
invece che malloc e free. Nel parametro del secondo costruttore non
serve la const dato che non si tratta di un puntatore o di una
reference.
Passiamo a Y e riscriviamo la tua versione:
	class Y
	{
	   public:
	      Y(void) { }
	      Y(int i) { objX=3DX(i); }
	   private:
	      X objX;
	};
Questo codice non =E8 in se sbagliato. Pero' nota che non puo' =
funzionare
perche' il compilatore non sa come eseguire l'operatore =3D su oggetti =
di
tipo X. Se nella classe X tu avessi definito un operator =3D del tipo:
	X& operator=3D (const X&);=20
Allora il codice di Y sarebbe a posto. Non avendolo fatto il =
compilatore
genera un operatore di assegnamento di default il quale assegna ciascun
data member. Questo =E8 molto rischioso. Guarda infatti che cosa =
succede
nel secondo costruttore di Y.
Fase1: Vengono costruiti tutti gli oggetti di Y e le sue classi base.
Quindi objX viene costruito usando il costruttore X().
Fase2: Viene eseguito il corpo del costruttore. Quello che avviene =E8
l'assegnamento di objX tramite una variabile temporanea.=20
La riga:
	objX=3DX(i);
=E8 equivalenete a:
	X temp=3Di;
	objX=3Dtemp;
In ordine vengono eseguiti: il secondo costruttore di X per creare la
temporanea. L'operatore di assegnamento per assegnare la temporanea a
objX. Il distruttore di X per distruggere la temporanea.
Quindi quello che succede =E8 che il membro v della temporanea viene
inizializzato allocando la memoria.
Poi viene eseguito un operatore di assegnamento che non essendo stato =
da
te definito =E8 quello di default. Ovvero al membro v di objX viene
assegnato il valore del membro v della temporanea. Il precedente valore
viene perso (per fortuna era NULL, ma se fosse stato un puntatore ora
avresti un memory leak).
Viene distrutta la temporanea e di conseguenza viene liberata la =
memoria
allocata. Nota che il membro v di objX ora punta ad un indirizzo di
memoria che non =E8 pi=F9 valido.
Ora le possibilit=E0 sono 2: hai problemi quando accedi a objX oppure =
hai
problemi quando esegui il distruttore di objX liberando della memoria
che era gi=E0 stata liberata.
Io ti consiglio di provare questo codice:=20
	class X
	{
	   public:
	      X(void) { v=3DNULL; }
	      X(int i) { v =3D new int[i]; }
		~X() { if ( v!=3DNULL ) delete [] v; }
	   private:
		X& operator=3D (const X&); // dichiarato privato e non
implementato. Genera errore se usato per sbaglio.=20
	    	int *v;
	};
	class Y
	{
	   public:
	      Y(void) { }
	      Y(int i) objX(i) { }
	   private:
	      X objX;
	};
Andrea
	-----Original Message-----
	From:	Humberto Jose Bortolossi
[
SMTP:hjbo...@saci.mat.puc-rio.br
]
	Posted At:	Tuesday, July 22, 1997 2:59 PM
	Posted To:	c++
	Conversation:	Costruttori due volte... =C8 questo corretto?
	Subject:	Costruttori due volte... =C8 questo corretto?
	Costruttori due volte... =C8 questo corretto?     =20
	    =20
	Mi piacerebbe sapere se sia consigliabile o non usare
costruttori sullo
	stesso
	oggetto. =20
	Il codi ce sotto =E8 un esempio piccolo di un grande programma
dove sto
	usando due costruttori diversi per inizializzare lo stesso
oggetto. Sto
	ottenendo un problema di un oggetto che cambia i contenuti di
altri
	oggetti.
	Inoltre, compilatori diversi danno risultati diversi. g++ non d=E0
errori
	mentre SparcWorksC++ 4.0.1 d=E0.
	Humberto.
	#include <malloc.h>
	class X
	{
	   public:
	      X(void);
	      X(const int);
	   private:
	     int *v;
	};
	X::X(void)
	{
	  ;
	};
	X::X(const int i)
	{
	   this->v =3D malloc(i*sizeof(int));
	};
	X::~X(void)
	{
	   free(this->v);
	};
	class Y
	{
	   public:
	      Y(void);
	      Y(const int);
	   private:
	      X objX;
	};
	Y::Y(void)
	{
	   ;
	};
	Y::Y(const int i)
	{
	   objX =3D X(i); // =C8 questo corretto?
	};