Ir para conteúdo
  • Cadastre-se

dev botao

dev botao

Objetos: Saber se é a mesma instância - Chupa essa manga


Ver Solução Respondido por Daniel Simoes,
  • Este tópico foi criado há 1975 dias atrás.
  • Talvez seja melhor você criar um NOVO TÓPICO do que postar uma resposta aqui.

Recommended Posts

Postado (editado)

Olá estou quebrando a cabeça.

Preciso armazenar um objeto e depois comparar se é ele mesmo. parece simples, mas tem umas pegadinhas, veja abaixo:

var
  A, B: TObject;
begin
  A := TObject.Create;
  B := A;

  A.Free;
  A := TObject.Create;
  // Agora A é outro objeto

  ShowMessage(             
    BoolToStr(A = B, True)
    );
 
//Exibe "True"

end;

Como assim?  Eu sei... O operador "=" apenas compara se os endereços na memória são iguais, e como  variáveis são ponteiros pra endereços de memória, deve ser porque o mesmo endereço onde  A estava armazenado é "aproveitado" para armazenar o novo objeto.

Mais uns testes:

var
  A, B, C: TObject;
begin

  A := TObject.Create;
  B := A;

  A.Free;

  C := TObject.Create;
  //C é outro objeto

  ShowMessage(             
    BoolToStr(A = C, True) 
    ); //Exibe "True"

  ShowMessage(             
    BoolToStr(Pointer(A) = Pointer(C), True) 
    ); //Exibe "True"

  ShowMessage(             
    BoolToStr(A.Equals(C), True) 
    );  //Exibe "True"

end;

Mas e agora? Usei variáveis diferentes(A e C), continua sendo o mesmo endereço.  O objeto C é criado no mesmo endereço onde estava armazenado A.

Os objetos são iguais, mas não são a mesma instância.

Preciso saber se é a mesma instância. 

Tem alguma maneira sem ter que dar um B:=nil, visto que onde destruo A não tenho acesso a B, e se possível sem ter que alterar a classe original, adicionando uma referencia ou algo do tipo.

Editado por Delcio
  • Consultores
Postado
  Em 12/03/2020 at 13:28, Delcio disse:

Como assim?  Eu sei... O operador "=" apenas compara se os endereços na memória são iguais, e como  variáveis são ponteiros pra endereços de memória, deve ser porque o mesmo endereço onde  A estava armazenado é "aproveitado" para armazenar o novo objeto.

Expandir  

Nessa comparação os objetos são os mesmos. Não entendi sua dúvida. A e B são o mesmo objeto no mesmo endereço. O resultado True está correto.

  Em 12/03/2020 at 13:28, Delcio disse:

.  O objeto C é criado no mesmo endereço onde estava armazenado A.

Os objetos são iguais, mas não são a mesma instância.

Expandir  

Você deu Free no objeto A. Liberou a memória indicando que ela pode ser utilizada por outro objeto. Como o objeto C é instanciado depois da liberação da memória, ele pode usar o mesmo endereço. O resultado vai ser igual.

Usar FreeAndNil vai ser a solução mais simples pro seu problema.

Mas sinceramente, acho que você está indo num caminho incorreto. Me parece que há violação de princípios de orientação a objetos. Seu código não deveria precisar desse tipo de comparação.

O que você está realmente tentando fazer? Que problema você está tentando resolver?

 

 

  • Curtir 2

[]'s

Consultor SAC ACBr

Elton
Profissionalize o ACBr na sua empresa, conheça o ACBr Pro.

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Um engenheiro de Controle de Qualidade(QA) entra num bar. Pede uma cerveja. Pede zero cervejas.
Pede 99999999 cervejas. Pede -1 cervejas. Pede um jacaré. Pede asdfdhklçkh.
Postado
  Em 12/03/2020 at 14:45, EMBarbosa disse:

Nessa comparação os objetos são os mesmos. Não entendi sua dúvida. A e B são o mesmo objeto no mesmo endereço. O resultado True está correto.

Expandir  

Os objetos são "iguais", mas não deveriam ser o "mesmo", pois são instancias diferentes. Isso causa confusão e foi motivo de um bug em nosso sistema.

  Em 12/03/2020 at 14:31, Daniel Simoes disse:

FreeAndNil(A);

Expandir  

Também pensei em FreeAndNil, mas tem que ser em B, o problema é que não tenho acesso a B onde libero A. Veja:

var
  A, B: TObject;
begin
  A := TObject.Create;
  B := A;

  FreeAndNil(A);
  A := TObject.Create;
  // Agora A é outro objeto


  ShowMessage(             
    BoolToStr(A = B, True) 
    ); //True
end;
  Em 12/03/2020 at 14:45, EMBarbosa disse:

O que você está realmente tentando fazer? Que problema você está tentando resolver?

Expandir  

Preciso armazenar um determinado objeto e depois, em outro local da aplicação preciso verificar se ele continua sendo o mesmo, não se é igual, mas se é a mesma instancia anterior.

Já contornei o problema original, mas não achei a solução ideal, deveria haver algo como TObject.SameInstance, já que o operador "=" só verifica se  o ponteiro das duas variáveis é o mesmo, e sim ele é o mesmo, mas os objetos não,  já que o primeiro foi destruído e  e criado novamente. 

Mas eu intendi, o operador é de igual,  e não de "mesmo": Dois carros iguais não significa que são o mesmo carro.

Mas achei interessante deixar este problema exposto para que outros não caiam nesta "arapuca".

Resolvi mais ou menos assim:

type
  TTest = class
  private
    FVar: Pointer;
  public
    constructor Create(var CheckInstanceVar); virtual;
    destructor Destroy; override;
  end;

constructor TTest.Create(var CheckInstanceVar);
begin
  inherited Create;
  FVar := @CheckInstanceVar;
end;

destructor TTest.Destroy;
begin
  inherited;
  if FVar = Self then
    FVar := nil;
end;

...

var
  A, B, C: TTest;
begin
  A := TTest.Create(B);

  A.Free;
  A := TTest.Create(C);
  // Agora A é outro objeto

  ShowMessage(             //
    BoolToStr(A = B, True) //
    );

end;

 

  • Fundadores
Postado
  Em 12/03/2020 at 15:29, Delcio disse:

Também pensei em FreeAndNil, mas tem que ser em B, o problema é que não tenho acesso a B onde libero A. Veja:

var
  A, B: TObject;
begin
  A := TObject.Create;
  B := A;

  FreeAndNil(A);
  A := TObject.Create;
  // Agora A é outro objeto


  ShowMessage(             
    BoolToStr(A = B, True) 
    ); //True
end;
  Em 12/03/2020 at 14:45, EMBarbosa disse:
Expandir  

Isso parece errado... você não deveria reaproveitar variáveis, dessa maneira...

Me parece que o seu modelo de codificação não está definindo muito bem as responsabilidade de cada método... O método que cria o objeto, deveria ser o mesmo responsável por liberá-lo ...

Consultor SAC ACBr

Daniel Simões de Almeida
O melhor TEF, é com o Projeto ACBr - Clique e Conheça
Ajude o Projeto ACBr crescer - Assine o SAC

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Postado
  Em 12/03/2020 at 15:41, Daniel Simoes disse:

Isso parece errado... você não deveria reaproveitar variáveis, dessa maneira..

Expandir  

Usei esse código apenas como exemplo, no meu caso nem reaproveito a variável, mas o erro acontece de forma semelhante ao exposto, pois o endereço da memoria do objeto que foi armazenado para posterior comparação é ocupado pelo novo objeto, e quando comparo usando "=" retorna True, mesmo sendo uma nova instância, pois aponta para o mesmo endereço da instancia anterior. 

No meu caso  a destruição/criação do objeto depende de fatores que não posso controlar, como a interação do usuário.

Resolvi armazenando uma referência para comparação no próprio objeto; 

  • Fundadores
Postado
  Em 12/03/2020 at 16:17, Delcio disse:

No meu caso  a destruição/criação do objeto depende de fatores que não posso controlar, como a interação do usuário.

Expandir  

Se você fala de Threads... é comum usar FreeOnTerminate, para que ela mesma faça a limpeza... nesse caso, o endereço de memória onde ela está não é importante...

Consultor SAC ACBr

Daniel Simões de Almeida
O melhor TEF, é com o Projeto ACBr - Clique e Conheça
Ajude o Projeto ACBr crescer - Assine o SAC

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

  • Consultores
Postado
  Em 12/03/2020 at 15:29, Delcio disse:

", pois são instancias diferentes. Isso causa confusão e foi motivo de um bug em nosso sistema.

Expandir  

Acho que você não entendeu essa parte ainda. A e B são a mesma instância. São variáveis diferentes apontando pro mesmo objeto, mesmo endereço de referência.

  Em 12/03/2020 at 15:29, Delcio disse:

", pois são instancias diferentes. Isso causa confusão e foi motivo de um bug em nosso sistema.

Expandir  

Nesse caso B pode continuar apontando para A  dependendo das opções do compilador. Veja:

type
  TMyClass = class(TObject)
  private
    { private declarations }
    Fs: string;
  end;

var
  A, B: TMyClass;
begin
    A := TMyClass.Create;
    B := A;

    A.Fs := 'Sou A';

    ShowMessage(B.Fs);

    FreeAndNil(A);

     A := TMyClass.Create;
      A.Fs := 'Sou NOVO A';
  // Agora A é outro objeto


  ShowMessage(
    BoolToStr(A = B, True)
    ); //True

    ShowMessage(B.Fs);
end.

 

  Em 12/03/2020 at 15:29, Delcio disse:

Preciso armazenar um determinado objeto e depois, em outro local da aplicação preciso verificar se ele continua sendo o mesmo, não se é igual, mas se é a mesma instancia anterior.

Expandir  

Continuo sem saber o que você quer fazer. Você está explicando qual a implementação quer alcançar e não o objetivo...

[]'s

Consultor SAC ACBr

Elton
Profissionalize o ACBr na sua empresa, conheça o ACBr Pro.

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Um engenheiro de Controle de Qualidade(QA) entra num bar. Pede uma cerveja. Pede zero cervejas.
Pede 99999999 cervejas. Pede -1 cervejas. Pede um jacaré. Pede asdfdhklçkh.
Postado
  Em 12/03/2020 at 17:02, EMBarbosa disse:

Acho que você não entendeu essa parte ainda. A e B são a mesma instância. São variáveis diferentes apontando pro mesmo objeto, mesmo endereço de referência.

Expandir  

Entendi sim,  as duas variáveis estão apontando para o mesmo endereço, e é justamente isso que causa a confusão, no final do código as duas variáveis estão armazenando o mesmo objeto.

Exatamente isso que impede eu comparar se "A" é igual a instância que eu tinha anteriormente e referenciei em "B".

O que eu precisava era uma forma de saber se meu "A" é a mesma instância de antes. Não me atentei a esse detalhe de endereçamento, e isso causou o problema;

Resumindo: Uma variável armazena uma referência a um endereço, e não o objeto em si.

 

  • Consultores
Postado
  Em 12/03/2020 at 18:32, Delcio disse:

Resumindo: Uma variável armazena uma referência a um endereço, e não o objeto em si.

Expandir  

Bem, isso sempre foi assim... mas se você não sabia, posso entender sua confusão...

  Em 12/03/2020 at 15:29, Delcio disse:

Resolvi mais ou menos assim:

Expandir  

Esse código me parece incorreto...  Você não colocou o "B:= A" e por isso o código vai dar false...

Ainda não consegui entender o que você quer fazer, mas não me parece que esse código realmente vai resolver seu problema...

[]'s

Consultor SAC ACBr

Elton
Profissionalize o ACBr na sua empresa, conheça o ACBr Pro.

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Um engenheiro de Controle de Qualidade(QA) entra num bar. Pede uma cerveja. Pede zero cervejas.
Pede 99999999 cervejas. Pede -1 cervejas. Pede um jacaré. Pede asdfdhklçkh.
Postado
  Em 12/03/2020 at 21:50, EMBarbosa disse:

Esse código me parece incorreto...  Você não colocou o "B:= A" e por isso o código vai dar false...

Expandir  

Verdade, acabei não testando o código de exemplo antes de postar e esqueci de umas coisas;

O correto é esse:

type
  TTest = class
  private
    FVar: Pointer;
  public
    constructor Create(var CheckInstanceVar); virtual;
    destructor Destroy; override;
  end;

constructor TTest.Create(var CheckInstanceVar);
begin
  inherited Create;
  FVar := @CheckInstanceVar;
end;

destructor TTest.Destroy;
begin
  if TTest(FVar^) = Self then
    Pointer(FVar^) := nil;
  inherited;
end;

...
var
  A, B: TTest;
begin
  A := TTest.Create(B);

  B:= A;

  A.Free;
  A := TTest.Create(B);
  // Agora A é outro objeto

  ShowMessage(             //
    BoolToStr(A = B, True) //
    );

end;

Basicamente armazeno um ponteiro para a variável no objeto para que possa setar a variável para nil ao destruir o objeto;

Lembrando que esse código é somente um exemplo, usei esse artifício em uma implementação bastante complexa, onde A pode ser destruído e criado indiscriminadamente e depois preciso saber se A continua sendo a mesma instância do início;

  • Consultores
Postado
  Em 13/03/2020 at 01:08, Delcio disse:

armazeno um ponteiro para a variável no objeto para que possa setar a variável para nil ao destruir o objeto;

Expandir  

Pra mim continua com problemas de design.

  • O que você fez foi um FreeAndNil em A;
  • O Create de A tem acesso a B que nem foi criado ainda;
  • B continua podendo gerar Access Violations depois de A ser destruído;

Mas se resolveu seu problema...

 

[]'s

Consultor SAC ACBr

Elton
Profissionalize o ACBr na sua empresa, conheça o ACBr Pro.

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Um engenheiro de Controle de Qualidade(QA) entra num bar. Pede uma cerveja. Pede zero cervejas.
Pede 99999999 cervejas. Pede -1 cervejas. Pede um jacaré. Pede asdfdhklçkh.
  • Este tópico foi criado há 1975 dias atrás.
  • Talvez seja melhor você criar um NOVO TÓPICO do que postar uma resposta aqui.

Crie uma conta ou entre para comentar

Você precisar ser um membro para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar Agora
×
×
  • Criar Novo...

Informação Importante

Colocamos cookies em seu dispositivo para ajudar a tornar este site melhor. Você pode ajustar suas configurações de cookies, caso contrário, assumiremos que você está bem para continuar.

The popup will be closed in 5 segundos...