Fórum Variavel de tipo ponteiro dá erro ao Liberar? #243639

19/07/2004

0

Olá

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

Paulo_amorim

Responder

Posts

19/07/2004

Allen74

Olá Paulo!

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.


Responder

Gostei + 0

19/07/2004

Paulo_amorim

Olá

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é+


Responder

Gostei + 0

19/07/2004

Allen74

Olá Paulo!

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...?

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 eu atribuir algo ja criado à minha variável ela não precisa ser criada, nem liberada...?

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.


Responder

Gostei + 0

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar