Newsgroup: it.comp.lang.delphi
Titolo: Metodo Create per classi definite nella unit Types

Ciao,
ho un po' di confusione su molti argomenti riguardanti la
programmazione ad oggetti e sto cercando di chiarirmeli studiando e
praticando. Penso mi aiuterebbe moltissimo sapere il perchè non si
crea un'istanza delle classi definite nella unit Types, mentre nelle
classi definite da me, invece, è necessario "invocare il constructor".
Stesso dubbio sul metodo Free.
Grazie
Francesco

 Stesso dubbio sul metodo Free.
Che vuoi dire? spiegati meglio...
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 Che vuoi dire? spiegati meglio...
Ad esempio se definisco
var
  punto : TPoint;
posso, nel codice, accedere immediatamente alle proprietà di questo
oggetto scrivendo immediatamente:
  punto.X := 1;
Invece se definisco una classe in questo modo:
type
  TForo = class
  protected
    id_foro : Integer;
    posx_foro, posy_foro : Double;
    utensile_foro : Integer;
  [... + lenco di metodi e proprietà pubbliche]
per utilizzarlo all'interno di una Unit, devo dichiarare:
var
  unforo : TForo;
e poi, prima di poterlo utilizzare devo:
  unforo := TForo.create;
etc.
Eppure sia TPoint che TForo sono due classi ... o mi sbaglio?

   punto : TPoint;
TPoint non � una classe, � un record. I record vengono allocati
staticamente, non c'� bisogno di allocazione dinamica. Questa � una delle
grosse incongruenze del modello ad oggetti di Delphi: record e classi sono
cose molto diverse. Questo differisce moltissimo, per esempio, dal C++, in
cui le classi possono essere allocate staticamente sullo stack e "struct"
e "class" sono praticamente la stessa cosa (cambia solo lo scopo
predefinito di visibilit�, che per le struct � public e per le classi �
private). In Delphi devi sempre allocare dinamicamente l'istanza di una
classe e quindi ricordarti di deallocarla (un aiuto te lo d� la clausola
try...finally). Questa necessit� obbligata porta a non poter implementare
in maniera puntuale e precisa un idioma per me fondamentale che � RAII. 


[...]
Grazie Andrea, sei stato chiarissimo, almeno su questo punto mi è
tutto chiaro. Per quanto riguarda Rail, lascio da parte ciò che hai
detto perchè non posso approfondire: lo riprenderò quando avrò fatto
un po' di strada.
La mia domanda nasce, in realtà, da altre difficoltà.
Ho capito che riesco a spiegarmi meglio prendendo l'esempio al quale
sto "lavorando" anzichè cercare di fare un ragionamento più astratto.
Ho un programma di foratura che deve eseguire una macchina. Il
programma non è niente altro che una lista di coordinate x e y
associate ad un numero intero che rappresenta un "id" di un utensile.
Ho bisogno di ordinare questi punti (problema del commesso
viaggiatore) e di manipolarli per raggrupparli per utensile e altre
cose.
So adesso che sarebbe meglio utilizzare le liste anzichè gli array
dinamici e che sarebbe bene utilizzare TPoint anzichè definire una
classe apposta per le coordinate, comunque, l'applicazione funziona e
fa il proprio dovere. Solo che, sia nel mentre la scrivevo che adesso
che è terminata nella sostanza, mi rimangono dei dubbi su come viene o
non viene liberata la memoria destinata allocata dai miei oggetti.
In particolare ho definito la classe TFileForatura:
type
  TFileForatura = class
    protected
      ...
      Programma_file : Array of TForo;
      ...
dove la classe TForo è definita come:
type
  TForo = class
  protected
    //La sua posizione tra gli altri fori dello stesso utensile
    id_foro : Integer;
    //La posizione sul piano: una coppia di coordinate X e Y
    posx_foro, posy_foro : Double;
    //L'utensile al quale appartiene, un oggetto della classe
TUtensile
    utensile_foro : Integer;
    ...
Nel metodo create della classe TFileForatura, per ogni riga del file,
vado a leggere la coppia di coordinate e l'id utensile per poi
metterli nell'array Programma_File.
var
  ...
  foro : TForo;
  ...
begin
  ...
    foro := TForo.create;
    for i=0 to lunghezza_file -1 do
    begin
      foro.posx_foro := posx_lettadafile;
      foro.posy_foro := posy_lettadafile;
      foro.utensile_foro := utensile_foro_lettodafile;
      Programma_File[i] := foro;
    end;
  ...
Cosa viene memorizzato in tutte le "i" posizioni dell'array? Degli
indirizzi di memoria che puntano all'oggetto "foro"? Eppure io vedo
popolato perfettamente l'array Programma_file, con tutte le coordinate
e l'id utensile. E quante istanze di TForo (oggetti foro) esistono in
memoria?
Se, alla fine del metodo scrivo foro.free, in alcuni casi il contenuto
dell'ultimo elemento del vettore Programma_File è un numero "senza
senso", immagino il contenuto della memoria ad un certo indirizzo e
per una certa lunghezza.
Ciao
Francesco

 memoria?
Il tuo codice � sbagliato.
In Delphi, quando scrivo:
var
  MioOggetto: TMiaClasse;
supponendo sempre che TMiaClasse sia effettivamente una classe (e non un
record o un tipo built-in, intero, double, string, ecc...), sto
semplicemente riservando memoria per una variabile puntatore (un reference
in termini formali). MioOggetto altro non � che un puntatore non
inizializzato. Per inizializzarlo:
MioOggetto := TMiaClasse.Create;
Questa istruzione alloca memoria (nello spazio heap del sistema) per un
oggetto di tipo TMiaClasse e ne assegna l'indirizzo al riferimento
MioOggetto. 
A quel punto su MioOggetto faccio tutte le mie operazioni e alla fine lo
dealloco, cio� rilascio la memoria occupata dall'istanza di TMiaClasse
creata in precedenza. Siccome la deallocazione, in Delphi, � un'operazione
fondamentale, conviene mettersi a riparo da eventuali eccezioni che
potrebbero scatenarsi con un bel try... finally:
MioOggetto := TMiaClasse.Create;
try
   // faccio le mie operazioni
finally
   MioOggetto.Free; // non importa cosa succede nel blocco try, io
dealloco comunque, anche se scoppia un'eccezione
end;
Se hai bisogno di allocare due istanze, non fare MAI l'errore di scrivere:
MioOggetto1 := TMiaClasse1.Create;
MioOggetto2 := TMiaClasse2.Create;
try
  // ....
finally
   MioOggetto1.Free; 
   MioOggetto2.Free;
end;
ma scrivi sempre qualcosa come:
MioOggetto1 := TMiaClasse1.Create;
try
  MioOggetto2 := TMiaClasse2.Create;
  try
    // ...
  finally
     MioOggetto2.Free;
  end;
finally
   MioOggetto1.Free; 
end;
Ora: per utilizzare un array dinamico ("array of T" con T qualsiasi), come
prima cosa devi dimensionarlo, cio� allocare sufficiente memoria (con
SetLength) perch� possa contenere tutti i riferimenti necessari alle
istanze delle classi T. Tu hai bisogno di memorizzare non 1, ma N
elementi, quindi dimensiona l'array ad N come prima cosa. 
A questo punto dovresti capire da solo in cosa � sbagliato il tuo codice:
tu crei una sola istanza di TForo. Devi crearne una per ciclo. Se devi
aggiungere 5 fori, il tuo ciclo deve andare da 1 a 5 e devi creare, ad
ogni ciclo, un TForo. Risultato: 5 istanze di TForo in memoria.
Ovvio che devi ricordarti di deallocarle una per una, una volta che la tua
elaborazione sull'array � terminata.
A questo punto ritorno al mio solito consiglio: abbasso gli array
dinamici, viva le liste. In particolare, nel tuo caso, viene comodo la
TObjectList.
Con la TObjectList, non hai bisogno di preallocare (e quindi non rischi di
sbagliare a farlo), non hai bisogno esplicito di deallocare tutti gli
oggetti (e quindi di sbagliare a farlo) perch� puoi impostare un flag a
true in fase di creazione della TObjectList (consulta l'help) e delegare
ad essa la distruzione degli oggetti contenuti.
Ovvio che devi sempre ricordarti di allocare e deallocare esplicitamente
la TObjectList, ma questo, per il discorso fatto su, � scontato per tutte
le classi in Delphi.


Caro Andrea,
lessi il tuo messaggio il 24 Dicembre e ti confermo che mi hai
chiarito molti dubbi: ti sono debitore.
Ho ricominciato oggi a lavorare e quindi ho appena incominciato a
documentarmi sulla TObjectList. Il "flag" al quale fai riferimento è
la proprietà OwnsObjects, il quale, se settato a True, permette alla
ObjectList di liberare gli oggetti quando vengono cancellati dalla
lista o quando la lista viene distrutta (cito dal manuale). Direi che
è LA SOLUZIONE ai miei problemi.
Devo lavorare per capire bene la gestione delle eccezioni con try ...
finally e l'uso della ObjectList ma ho capito come devo impostare il
software.
Posterò il nuovo codice appena pronto.
W le liste!