Fórum Variavel de tipo ponteiro dá erro ao Liberar? #243639
19/07/2004
0
Tive um pequeno problema que gostaria de discutir e tentar achar explicação aqui.
Fiz uma procedure para pegar o conteudo de um TQuery e trabalhar com ele normalmentel...sempre fiz isso, mas nunca tinha me ocorrido o que aconteceu:
var s: TStrings; begin s := TStringList.Create; try s := Query1.SQL; for i := 0 to s.Count -1 do //aqui ele faz uma rotina "inocente"... finally s.Free; // <-- Deu erro. end; end;
Ele deu erro na linha indicada. Não soh erro, mas uma ´falha catastrófica´ (catastrofica eh essa mensagem... :? ) ...debugando eu vi que foi na hora de liberar a variavel S.
Me ocorreu que pode ter sido devido ao fato que S apontava pro SQL da TQuery, e na hora que eu fui liberar eu ´destrui´ a propriedade da TQuery...isso eh meio estranho na minha opinião, mas eh isso mesmo que ocorre?
Gostaria de ver se tem algo diferente que eu nao vi
Ah, claro. como a procedure termina aí eu tirei o Free, já que a variavel eh local e deveria ser destruida junto com a Procedure. Estou certo?
Até+
Paulo_amorim
Curtir tópico
+ 0Posts
19/07/2004
Allen74
Conforme você poderá confirmar no Help do Delphi, a propriedade SQL de uma query, é um objeto da classe TStrings. No Delphi, as variáveis de um determinado tipo de classe são nada mais que ponteiros para a estrutura de uma instância (objeto) destas classes na memória.
Quando você fez a atribuição: [b:4bae6d9aae]s := Query1.SQL;[/b:4bae6d9aae] não foi feita uma cópia do objeto, mas na verdade foi feita uma referência para este objeto (assim como passar uma variável por referência nos parâmetros de procedures e funções). Dessa forma, quando você chamou [b:4bae6d9aae]s.Free[/b:4bae6d9aae] você estava tentando destruir na verdade um objeto agregado à classe TQuery que é a responsável pela criação e destruição do objeto do tipo TStrings associado à propriedade SQL.
Além deste problema, tal procedimento está causando um outro, um memory leak, porque o objeto do tipo TStringList que você criou na linha: [b:4bae6d9aae]s := TStringList.Create;[/b:4bae6d9aae] não foi destruído já que você mudou a referência para o objeto na memória quando refez a atribuição para Query1.SQL dentro do bloco [b:4bae6d9aae]try ... finally[/b:4bae6d9aae]. Se esta rotina for chamada repetidas vezes, a memória continuará sendo alocada e somente será liberada pelo sistema operacional quando seu programa for fechado (isto se não ocorrer antes erro por falta de memória).
A forma correta de se trabalhar, seria a seguinte:
- Se você não pretende modificar o conteúdo original da propriedade SQL de TQuery, você deve utilizar o método Assign para copiar as propriedades e de TQuery.SQL para a stringlist que você criou. Desta forma, se você alterar qualquer propriedade de [b:4bae6d9aae]s[/b:4bae6d9aae] as alterções não serão refletidas em TQuery.SQL. Seu código então ficaria assim:
var s: TStrings; begin s := TStringList.Create; try s.Assign (Query1.SQL); for i := 0 to s.Count -1 do //aqui ele faz uma rotina "inocente"... finally s.Free; // Agora não mais ocorrerá erro aqui e também não teremos memory leak end; end;
- Por outro lado, se sua inteção é modificar as propriedades de Query1.SQL então você poderia continuar fazendo referência a Query1.SQL sem utilizar uma variável [b:4bae6d9aae]s[/b:4bae6d9aae] ou se utilizar [b:4bae6d9aae]s[/b:4bae6d9aae] de forma que ele esteja apontando para o mesmo objeto que Query1.SQL você deve remover a linha que contém [b:4bae6d9aae]s := TStringList.Create;[/b:4bae6d9aae] e a outra linha onde temos [b:4bae6d9aae]s.Free;[/b:4bae6d9aae]
Espero que tenha ajudado a esclarecer a questão e aproveito para lembrar que o compilador somente realiza uma cópia dos dados primitivos do Delphi como os tipos integer, string, char, etc. Os tipos de dados de ponteiro, incluindo-se aí as variáveis de um determinado tipo de classe semprerão tratados como referência numa atribuição.
Gostei + 0
19/07/2004
Paulo_amorim
Obrigado Allen! :D
Eu sabia que ele era uma referencia para o mesmo local, mas não imaginei que fosse refletir na propriedade.
Eu criei uma variável S pois nao era um simples TQuery.SQL...eu tive que fazer vários typecasts pois era uma aplicação 3 camadas...mas isso não tem nada a ver
Então para que vc copie de fato os dados de um objeto para outro vc pode usar Assign? Daí serão 2 coisas totalmente diferentes...?
Se eu atribuir algo ja criado à minha variável ela não precisa ser criada, nem liberada...?
Obrigado outra vez. Me tirou várias dúvidas
Até+
Gostei + 0
19/07/2004
Allen74
Você pode utilizar Assign para todos as classes descendentes de TPersistent, desde que os dois objetos já estejam criados, pois o que o método Assign faz é apenas copiar os dados das propriedades. Consulte o help do método Assign para a classe TPersistent para saber um pouco mais.
Se você tiver uma variável de tipo compatível (ou seja, da mesma classe) e se sua intenção é fazer apenas uma referência ao invés de criar uma nova instância (novo objeto) então não precisa (e não deve) criar e nem liberar a memória.
Você até pode liberar a memória através da variável de referência, desde que tome o cuidado de não fazê-lo novamente pelo objeto original (que agora já estaria apontando para lixo na memória). De qualquer forma, a melhor prática é destruir através da mesma referência (variável) que criou o objeto, para deixar seu código compreensível.
Gostei + 0
Clique aqui para fazer login e interagir na Comunidade :)