PDA

Visualizza la versione completa : [DELPHI] Accesso a oggetto con riferimento contenuto in array


Whitecrowsrain
14-02-2012, 10:48
Salve,
vorrei sapere un vostro parere in merito ad una questione .

Ho creato due classi del tipo



type
TIndirizzo = class
constructor
create;
private
nome:string;
cognome:string;
citta:string;
via:string;
cap:string;
protected
procedure leggi(info:string);
public
property getnome: string read nome;
property getcognome: string read cognome;
property getcitta: string read citta;
property getvia: string read citta;
property getcap: string read citta;
end;

TlistaIndirizzi = class
constructor create(nf:string);
private
indirizzi: array of TIniridzzo;
public
procedure get_indirizzo(nome:string;cognome:string):TIndiriz zo;
end;

....
implementation
procedure Tindirizzi.leggi(info:string);
begin
nome:=copy(info,1,10);
nome:=copy(info,20,10);
cognome:=copy(info,30,10);
citta:=copy(info,40,10);
via:=copy(info,50,10);
cap:=copy(info,60,10);
end;
..
procedure TListaIndirizzi.create(nf);
var
t:textFile;
i:integer;
riga:string;
begin
i:=0;
assignFile(t,nf);
reset(t);
while not eof(t) do
begin
readln(t,riga);
SetLength(indirizzi,i+1);
indirizzi[i]:=TIndirizzo.create;
indirizzi[i].leggi(riga);
inc(i);
end;
closeFile(t);
end;
...
var
ListaInd:TlistaIndirizzi;
indirizzo:Tindirizzo;



Se io eseguo il codice sottostante



...
indirizzo:=Tindirizzo.create;
ListaInd:=TlistaIndirizzi.create('indirizzi.txt');
indirizzo:=ListaInd.get_indirizzo('Mario','Rossi') ;
showmessage(indirizzo.getcitta);
indirizzo.free;
...


Ottengo che il metodo free non solo cancella la variabile "indirizzo" ma anche "ListaInd.indirizzi[i]"!
Questo mi fa pensare che ad "indirizzo" non venga passato il valore di ListaInd.indirizzi[i], ma l'indirizzo di memoria come se fosse un puntatore.. ma perchè? è normale?

P.S.
Il codice è un esempio semplificato di quello che fa la mia classe, non è il codice reale!

alka
14-02-2012, 12:20
Originariamente inviato da Whitecrowsrain
Ottengo che il metodo free non solo cancella la variabile "indirizzo" ma anche "ListaInd.indirizzi[i]"!


Cosa intendi con "cancellare"? Il metodo Free distrugge l'oggetto, pertanto qualsiasi riferimento a esso non sarà più valido perché questo non esiste più in memoria (ciò si tradurrà quindi in una probabile Access Violation).



Originariamente inviato da Whitecrowsrain
Questo mi fa pensare che ad "indirizzo" non venga passato il valore di ListaInd.indirizzi[i], ma l'indirizzo di memoria come se fosse un puntatore.. ma perchè? è normale?

Il codice mi sembra abbastanza caotico, ad ogni modo è sufficiente sapere che quando si crea una istanza di una classe (oggetto), cioè di cui si dispone è il "riferimento" a quell'oggetto, cioè un "puntatore". Andando a richiamare il metodo Free, si distrugge l'oggetto in questione attraverso il suo riferimento, pertanto qualsiasi altro riferimento presente in variabili, campi, array o altre strutture dati non sarà più "valido" (avrà cioè un puntatore valido - non nil - ma che si riferisce a un oggetto che non esiste più).

P.S.: suggerisco di usare titoli più significativi per i problemi (tralasciando il fatto che non è affatto un bug ciò che è alla base della programmazione a oggetti). :stordita:

Whitecrowsrain
14-02-2012, 12:42
Originariamente inviato da alka
Cosa intendi con "cancellare"? Il metodo Free distrugge l'oggetto, pertanto qualsiasi riferimento a esso non sarà più valido perché questo non esiste più in memoria (ciò si tradurrà quindi in una probabile Access Violation).



Il codice mi sembra abbastanza caotico, ad ogni modo è sufficiente sapere che quando si crea una istanza di una classe (oggetto), cioè di cui si dispone è il "riferimento" a quell'oggetto, cioè un "puntatore". Andando a richiamare il metodo Free, si distrugge l'oggetto in questione attraverso il suo riferimento, pertanto qualsiasi altro riferimento presente in variabili, campi, array o altre strutture dati non sarà più "valido" (avrà cioè un puntatore valido - non nil - ma che si riferisce a un oggetto che non esiste più).

P.S.: suggerisco di usare titoli più significativi per i problemi (tralasciando il fatto che non è affatto un bug ciò che è alla base della programmazione a oggetti). :stordita:

Forse non mi sono spiegato bene, probabilmente perchè manca la funzione "get_indirizzo":



function TlistaIndirizzi.get_indirizzo(nome,cognome:string) :Tindirizzo;
var
i:integer;
begin
result:=nil;
for i:=0 to count(indirizzi)-1 do
begin
if (indirizz[i].nome=nome) and (indirizz[i].cognome=cognome) then
begin
result:=indirizz[i];
break;
end;
end;

end;


Tornando al codice in esame:



indirizzo:=Tindirizzo.create;
ListaInd:=TlistaIndirizzi.create('indirizzi.txt');
indirizzo:=ListaInd.get_indirizzo('Mario','Rossi') ;
showmessage(indirizzo.getcitta);
indirizzo.free;


quando faccio il free non solo mi libera la variabile "indirizzo" ma anche quella dal quale ha copiato il valore cioè quella di "ListaInd.indirizzi" (risultato della funzione get_indirizzo), come se ad "indirizzo" non gli avesse assegnato solo il valore della variabile "ListaInd.indirizzi[i]", ma proprio l'indirizzo di memoria.. ma io non ho usato un puntatore.. ecco dov'è la mia perplessità e chiedevo se fosse normale o se fosse un bug!!
Siccome mi sono confrontato con i miei colleghi qui in ufficio su questo argomento e mi dicevano che nella variabile "indirizzo" dovrebbe esser copiato solo il valore del risultato della funzione "get_indirizzo" e non l'indirizzo chiedevo se fossimo tutti in errore o se a sbagliare fosse il compilatore..

Tu dici bene
quando si crea una istanza di una classe ([I]oggetto), cioè di cui si dispone è il "riferimento" a quell'oggetto, cioè un "puntatore"

Ma qui io ho due istanze diverse, "indirizzo" ed il risultato di "ListaInd.get_indirizzo('Mario','Rossi')".. perché se libero la prima istanza mi dovrebbe liberare anche la seconda? E' normale? e se io volessi che in indirizzo venisse copiato solo il valore risultato della funzione?

p.s: ottimo titolo, sinceramente non sapevo cosa scriverci!!

ESSE-EFFE
14-02-2012, 13:04
Originariamente inviato da Whitecrowsrain


indirizzo:=Tindirizzo.create;
ListaInd:=TlistaIndirizzi.create('indirizzi.txt');
indirizzo:=ListaInd.get_indirizzo('Mario','Rossi') ;
showmessage(indirizzo.getcitta);
indirizzo.free;


Scusa, ma non stai usando la stessa variabile?

Whitecrowsrain
14-02-2012, 13:10
Originariamente inviato da ESSE-EFFE
Scusa, ma non stai usando la stessa variabile?

dopo il free della variabile "indirizzo" se faccio di nuovo :



ListaInd.get_indirizzo('Mario','Rossi');


ottengo come risultato nil, cioè non solo mi ha liberato la variabile "indirizzo" ma anche il risultato della funzione get_indirizzo, ovvero ListaInd.indirizzi[i] (che è di tipo TIndirizzo e dal quale ho copiato il valore su "indirizzo") !!

alka
14-02-2012, 13:43
Originariamente inviato da Whitecrowsrain
Forse non mi sono spiegato bene, probabilmente perchè manca la funzione "get_indirizzo"

Forse dovresti postare tutto il codice, perché sicuramente lì c'è l'errore.

Se guardiamo codice esemplificativo, magari quello non riconduce al problema.

Whitecrowsrain
14-02-2012, 14:04
//Definizione delle classi
TFormato = class
constructor create;
private
nome:string;
codice:string;
codice_man:string;
codice_cd:string;
alt:double;
larg:double;
quality:double;
rapW:double;
rapH:double;
codici: array of string;
limiti: array of string;
variabile:boolean;
protected
procedure azzera;
procedure leggi(nomeFormato,nf:string);
public
property getnome: string read nome;
function getcodice: string;
property getcodice_man: string read codice_man;
property getcodice_cd: string read codice_cd;
property getAlt: double read alt;
property getLarg: double read larg;
property getQuality: double read quality;
property getRapW: double read rapW;
property getRapH: double read rapH;
function trovacodice(cod,formato:string):boolean;
function getCodicePerSviluppo(sviluppo:double):string;
property isVariabile: boolean read variabile;
end;
TListaFormati = class
constructor create(nf:string);
private
nf_config:string;
numero_formati:integer;
formati: array of TFormato;
public
function getFormatoByName(nome:string):TFormato;
function getFormatoByID(id:integer):TFormato;
function getFormatoName(id:integer):string;
property getnumeroFormati: integer read numero_formati;
end;
..
//funzioni
procedure TFormato.azzera;
begin
nome:='';
codice:='';
codice_man:='';
codice_cd:='';
alt:=0;
larg:=0;
quality:=0;
rapW:=0;
rapH:=0;
SetLength(limiti,0);
SetLength(codici,0);
variabile:=false;
end;
constructor TFormato.create;
begin
azzera;
end;
function TFormato.getcodice: string;
begin
result:='';
try
if variabile then
result:=codici[0]
else
result:=codice;
except
end;
end;
procedure TFormato.leggi(nomeFormato,nf:string);
var
ini:TiniFile;
l,i:integer;
lim,cod:string;
begin
azzera;
if nomeFormato<>'' then
begin
ini:=TIniFile.Create(nf);
nome:=nomeFormato;
quality:=ini.ReadInteger(nomeFormato,'Quality',0);
rapW:=ini.readFloat(nomeFormato,'Rapporto W',0);
rapH:=ini.readFloat(nomeFormato,'Rapporto H',0);
alt:=ini.readFloat(nomeFormato,'H',0);
larg:=ini.readFloat(nomeFormato,'W',0);
codice:=ini.readstring(nomeFormato,'codice','');
codice_man:=ini.readstring(nomeFormato,'codice manuale','');
codice_cd:=ini.readstring(nomeFormato,'codice automatico','');
variabile:=ini.readstring(nomeFormato,'Tipo','F')= 'V';
l:=ini.readInteger(nomeFormato,'Limiti',0);
SetLength(limiti,l);
SetLength(codici,l);
if variabile then
begin
for i:=1 to l do
begin
limiti[i-1]:=ini.readstring(nomeFormato,'Limite'+inttoStr(i), '');
codici[i-1]:=ini.readstring(nomeFormato,'Codice'+inttoStr(i), '');
end;
end;

ini.free;
end;
end;
function TFormato.trovacodice(cod,formato:string):boolean;
var
i:integer;
begin
result:=false;
if formato=nome then
begin
if (cod=codice) or (cod=codice_man) or (cod=codice_cd) then
result:=true;
if not result then
begin
for i:=0 to length(codici)-1 do
begin
if codici[i]= cod then
begin
result:=true;
break;
end;
end;
end;
end;
end;
function TFormato.getCodicePerSviluppo(sviluppo:double):str ing;
var
i:integer;
l:double;
begin
result:='';
for i:=0 to length(limiti)-1 do
begin
l:=strToFloatDef(limiti[i],-1);
if l=-1 then
begin
result:=codici[i];
break;
end
else begin
if sviluppo<=l then
begin
result:=codici[i];
break;
end;
end;
end;
end;
constructor TListaFormati.create(nf:String);
var
ini:TiniFile;
lf:Tstringlist;
i:integer;
begin
nf_config:=nf;
lf:=TStringList.create;
ini:=TIniFile.Create(nf);
ini.ReadSections(lf);
ini.Free;
numero_formati:=lf.Count;
SetLength(formati,numero_formati);
for i:=0 to numero_formati-1 do
begin
formati[i]:=TFormato.create;
formati[i].leggi(lf[i],nf);
end;
lf.free;
end;
function TListaFormati.getFormatoByName(nome:string):TForma to;
var
i:integer;
ris:TFormato;
begin
ris:=TFormato.create;

for i:=0 to numero_formati-1 do
begin
if nome=formati[i].getnome then
begin
ris:=formati[i];
break;
end;
end;
Result:=ris;
end;
function TListaFormati.getFormatoName(id:integer):string;
begin
try
result:=Formati[id].getnome;
except
result:='';
end;
end;
function TListaFormati.getFormatoByID(id:integer):TFormato;
var
ris:TFormato;
begin
ris:=TFormato.create;
if numero_formati<id then
ris:=Formati[id];
result:=ris;
end;



Ok ora se provo a fare questo:



var
lista:TlistaFormati;
formato1,formato2:TFormato;
begin
lista:=TlistaFormati.create('formati.ini');
formato1:=TFormato.create;
formato1:=lista.getFormatoByName('PICCOLO');
showmessage(formato1.getnome);
formato1.free;
formato2:=TFormato.create;
formato2:=lista.getFormatoByName('PICCOLO');
showmessage(formato2.getnome);
formato2.free;

end;


Il risultato che ottengo del primo showmessage è "PICCOLO" mentre del secondo è ""... l'elemento "formati" dell'oggetto "lista" si è cancellato insieme alla variabile "formato1"!
Inpratica quando ho assegnato a fomato1 il risultato della funzinoe "lista.getFormatoByName" gli ha passato l'indirizzo a quella variabile e non il valore!
mi chiedo è normale? perché accade ciò?

ESSE-EFFE
14-02-2012, 14:33
Originariamente inviato da Whitecrowsrain


formato1:=TFormato.create;
formato1:=lista.getFormatoByName('PICCOLO');


Non so se c'entra direttamente con il problema che hai, però a me pare che stai chiamando TFormato.create una volta di troppo, sia prima della getFormatoByName, che all'interno della stessa funzione (ris:=TFormato.create).

alka
14-02-2012, 14:52
Concentriamoci su questo punto:



lista:=TlistaFormati.create('formati.ini');
formato1:=TFormato.create;
formato1:=lista.getFormatoByName('PICCOLO');
showmessage(formato1.getnome);
formato1.free;
formato2:=TFormato.create;
formato2:=lista.getFormatoByName('PICCOLO');
showmessage(formato2.getnome);
formato2.free;


Innanzitutto, ci sono delle chiamate a TFormato.Create che non servono a nulla, perché la variabile viene riassegnata subito dopo usando la getFormatoByName.

Poi, chiamando la getFormatoByName, si ottiene il riferimento a un oggetto che viene memorizzato in "formato1", di cui si fa la Free; pertanto, quell'oggetto viene distrutto, e il riferimento contenuto in "formato1" non sarà più valido.

Successivamente, sempre tramite la getFormatoByName, si ottiene lo stesso formato (PICCOLO) che non esisterà più perché distrutto poco prima.

Whitecrowsrain
14-02-2012, 16:38
Originariamente inviato da alka
Concentriamoci su questo punto:



lista:=TlistaFormati.create('formati.ini');
formato1:=TFormato.create;
formato1:=lista.getFormatoByName('PICCOLO');
showmessage(formato1.getnome);
formato1.free;
formato2:=TFormato.create;
formato2:=lista.getFormatoByName('PICCOLO');
showmessage(formato2.getnome);
formato2.free;


Innanzitutto, ci sono delle chiamate a TFormato.Create che non servono a nulla, perché la variabile viene riassegnata subito dopo usando la getFormatoByName.

Poi, chiamando la getFormatoByName, si ottiene il riferimento a un oggetto che viene memorizzato in "formato1", di cui si fa la Free; pertanto, quell'oggetto viene distrutto, e il riferimento contenuto in "formato1" non sarà più valido.

Successivamente, sempre tramite la getFormatoByName, si ottiene lo stesso formato (PICCOLO) che non esisterà più perché distrutto poco prima.

Ora ho capito, pero:
non capisco perché si ottiene il riferimento all'oggetto e non il valore dell'oggetto, io non gli passo un puntatore, ma faccio una assegnazione.
Di conseguenza mi aspettavo che a formato1 venisse copiato il valore dell'oggetto risultato della funzione e non che invece venisse assegnato il riferimento all'oggetto stesso.
E' ovvio che in quest'ottica diventa superfluo creare gli oggetti formato1 e formato2..

Per ottenere il risultato da me sperato dovevo fare una cosa del genere:



//Aggiungere una funzione assign a TFormati
Prodecdure TFormati.assign(Var FormatoInCuiCopiare:TFormato);
var
i:integer;
begin
if FormatoInCuiCopiare<>nil then
begin
FormatoInCuiCopiare.nome:=nome;
FormatoInCuiCopiare.codice:=codice;
FormatoInCuiCopiare.codice_man:=codice_man;
FormatoInCuiCopiare.codice_cd:=codice_cd;
FormatoInCuiCopiare.alt:=alt;
FormatoInCuiCopiare.larg:=larg;
FormatoInCuiCopiare.quality:=quality;
FormatoInCuiCopiare.rapW:=rapW;
FormatoInCuiCopiare.rapH:=rapH;
setLength(FormatoInCuiCopiare.codici,length(codici ));
setLength(FormatoInCuiCopiare.limiti,length(limiti ));
for i:=0 to count(codici)-1 do
FormatoInCuiCopiare.codici[i]:=codici[i];
for i:=0 to count(limiti)-1 do
FormatoInCuiCopiare.limiti[i]:=limiti[i];

limiti: array of string;
FormatoInCuiCopiare.variabile:variabile;
end;
end;


A quel punto avrebbe avuto senso scrivere così:



lista:=TlistaFormati.create('formati.ini');
formato1:=TFormato.create;
lista.getFormatoByName('PICCOLO').assign(formato1) ;
showmessage(formato1.getnome);
formato1.free;
formato2:=TFormato.create;
lista.getFormatoByName('PICCOLO').assign(formato2) ;
showmessage(formato2.getnome);
formato2.free;


ed avrei raggiunto il risultato sperato, giusto?

Loading