Pergunta

Copiei alguns códigos Delphi de um projeto para outro e descobri que ele não compila no novo projeto, embora tenha sido compilado no antigo.O código é mais ou menos assim:

procedure TForm1.CalculateGP(..)
const
   Price : money = 0;
begin
   ...
   Price := 1.0;
   ...
end;

Portanto, no novo projeto, Delphi reclama que "o lado esquerdo não pode ser atribuído" - compreensível!Mas esse código é compilado no projeto antigo.Então minha pergunta é: por que?Existe uma opção de compilador para permitir que consts sejam reatribuídos?Como isso funciona?Achei que consts foram substituídos por seus valores em tempo de compilação?

Foi útil?

Solução

Você precisa ativar constantes digitadas atribuíveis.Projeto -> Opções -> Compilador -> Constantes digitadas atribuíveis

Além disso, você pode adicionar {$J+} ou {$WRITEABLECONST ON} para o arquivo pas, o que provavelmente é melhor, pois funcionará mesmo se você mover o arquivo para outro projeto.

Outras dicas

Constantes inferidas por tipo só podem ser valores escalares - ou seja,coisas como números inteiros, duplos, etc.Para esses tipos de constantes, o compilador de fato substitui o símbolo da constante pelo valor da constante sempre que os encontra nas expressões.

As constantes digitadas, por outro lado, podem ser valores estruturados – matrizes e registros.Esses caras precisam de armazenamento real no executável - ou seja,eles precisam ter armazenamento alocado para eles de forma que, quando o sistema operacional carrega o executável, o valor da constante digitada esteja fisicamente contido em algum local da memória.

Para explicar por que, historicamente, as constantes digitadas no início do Delphi e em seu antecessor, Turbo Pascal, são graváveis ​​(e, portanto, essencialmente variáveis ​​globais inicializadas), precisamos voltar aos dias do DOS.

O DOS roda em modo real, em termos x86.Isto significa que os programas têm acesso direto à memória física sem qualquer MMU fazendo mapeamentos físicos virtuais.Quando os programas têm acesso direto à memória, nenhuma proteção de memória está em vigor.Em outras palavras, se houver memória em qualquer endereço, ela será legível e gravável em modo real.

Assim, em um programa Turbo Pascal para DOS com uma constante digitada, cujo valor é alocado em um endereço na memória em tempo de execução, essa constante digitada será gravável.Não há nenhum MMU de hardware atrapalhando e impedindo que o programa grave nele.Da mesma forma, como Pascal não tem a noção de 'constância' que o C++ possui, não há nada no sistema de tipos que possa impedi-lo.Muita gente se aproveitou disso, já que o Turbo Pascal e o Delphi ainda não tinham inicializado variáveis ​​globais como um recurso.

Passando para o Windows, existe uma camada entre endereços de memória e endereços físicos:a unidade de gerenciamento de memória.Este chip pega o índice da página (uma máscara deslocada) do endereço de memória que você está tentando acessar e procura os atributos desta página em seu tabela de páginas.Esses atributos incluem sinalizadores legíveis, graváveis ​​e, para chips x86 modernos, sinalizadores não executáveis.Com esse suporte, é possível marcar seções do .EXE ou .DLL com atributos de forma que, quando o carregador do Windows carregar a imagem executável na memória, ele atribua atributos de página apropriados para páginas de memória que mapeiam para páginas de disco dentro dessas seções.

Quando a versão Windows de 32 bits do compilador Delphi surgiu, fazia sentido criar coisas do tipo const realmente const, já que o sistema operacional também possui esse recurso.

  1. Por que:Porque nas versões anteriores do Delphi as constantes digitadas eram atribuíveis por padrão para preservar a compatibilidade com versões mais antigas, onde sempre eram graváveis ​​(Delphi 1 até o início do Pascal).
    O padrão agora foi alterado para tornar as constantes realmente constantes…

  2. Chave do compilador:{$J+} ou {$J-} {$WRITEABLECONST ON} ou {$WRITEABLECONST OFF}
    Ou nas opções de projeto do compilador:verifique constantes digitadas atribuíveis

  3. Como funciona:Se o compilador puder calcular o valor em tempo de compilação, ele substituirá const por seu valor em todo o código; caso contrário, ele manterá um ponteiro para uma área de memória que contém o valor, que pode ser gravável ou não.
  4. veja 3.

Como Barry disse, as pessoas aproveitavam as consts;Uma das maneiras pelas quais isso foi usado foi para monitorar instâncias singleton.Se você observar uma implementação singleton clássica, verá o seguinte:

  // Example implementation of the Singleton pattern.
  TSingleton = class(TObject)
  protected
    constructor CreateInstance; virtual;
    class function AccessInstance(Request: Integer): TSingleton;
  public
    constructor Create; virtual;
    destructor Destroy; override;
    class function Instance: TSingleton;
    class procedure ReleaseInstance;
  end;

constructor TSingleton.Create;
begin
  inherited Create;

  raise Exception.CreateFmt('Access class %s through Instance only', [ClassName]);
end;

constructor TSingleton.CreateInstance;
begin
  inherited Create;

  // Do whatever you would normally place in Create, here.
end;

destructor TSingleton.Destroy;
begin
  // Do normal destruction here

  if AccessInstance(0) = Self then
    AccessInstance(2);

  inherited Destroy;
end;

{$WRITEABLECONST ON}
class function TSingleton.AccessInstance(Request: Integer): TSingleton;
const
  FInstance: TSingleton = nil;
begin
  case Request of
    0 : ;
    1 : if not Assigned(FInstance) then
          FInstance := CreateInstance;
    2 : FInstance := nil;
  else
    raise Exception.CreateFmt('Illegal request %d in AccessInstance', [Request]);
  end;
  Result := FInstance;
end;
{$IFNDEF WRITEABLECONST_ON}
  {$WRITEABLECONST OFF}
{$ENDIF}

class function TSingleton.Instance: TSingleton;
begin
  Result := AccessInstance(1);
end;

class procedure TSingleton.ReleaseInstance;
begin
  AccessInstance(0).Free;
end;
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top