2008-09-08 17 views
21

Copié algún código Delphi de un proyecto a otro, y encontré que no se compila en el nuevo proyecto, aunque sí en el anterior. El código es como la siguiente:En Delphi 7, ¿por qué puedo asignar un valor a un const?

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

Así que en el nuevo proyecto, Delphi se queja de que "el lado izquierdo no se puede asignar a" - comprensible! Pero este código se compila en el proyecto anterior. Entonces mi pregunta es ¿por qué? ¿Hay un cambio de compilador para permitir reasignar consts? ¿Cómo funciona eso? Pensé que las constelaciones fueron reemplazadas por sus valores en tiempo de compilación.

Respuesta

29

Debe activar las constantes tipeadas asignables. Proyecto -> Opciones -> Generador -> asignable mecanografiado Constantes

También puede agregar o {$J+}{$WRITEABLECONST ON} al archivo pas, que es probablemente mejor, ya que va a trabajar incluso si se mueve el archivo a otro proyecto.

+2

Que hizo el truco. Google revela que la directiva del compilador es {$ J +}. También está en las opciones del proyecto, probablemente debería haber buscado allí: P – Blorgbeard

+0

Sí, me ha impresionado antes. (editado para agregar detalles después de mi publicación inicial) – Ray

+1

IMHO '{$ WRITABLECONST OFF}' sería incluso mejor :) que '{J +}'. –

27

Las constantes de tipo inferido solo pueden ser valores escalares, es decir, enteros, dobles, etc. Para este tipo de constantes, el compilador reemplaza el símbolo de la constante con el valor de la constante siempre que se encuentre en expresiones.

Las constantes tipadas, por otro lado, pueden ser valores estructurados: matrices y registros. Estos tipos necesitan almacenamiento real en el ejecutable, es decir, necesitan tener el almacenamiento asignado para ellos de forma que, cuando el SO cargue el ejecutable, el valor de la constante tipeada esté físicamente contenido en alguna ubicación de la memoria.

Para explicar por qué, históricamente, las constantes tipadas en Delphi inicial y su predecesor, Turbo Pascal, son escribibles (y, por lo tanto, variables globales esencialmente inicializadas), tenemos que volver a los días de DOS.

DOS se ejecuta en modo real, en términos de x86. Esto significa que los programas tienen acceso directo a la memoria física sin ningún MMU realizando asignaciones virtual-físicas. Cuando los programas tienen acceso directo a la memoria, no hay protección de memoria en efecto. En otras palabras, si hay memoria en cualquier dirección dada, es legible y escribible en modo real.

Entonces, en un programa Turbo Pascal para DOS con una constante tipada, cuyo valor se asigna a una dirección en la memoria en tiempo de ejecución, esa constante tipeada será escribible. No hay una MMU de hardware que se interponga y que impida que el programa escriba en ella. De manera similar, debido a que Pascal no tiene noción de 'const'ness' que C++ tiene, no hay nada en el sistema de tipos que lo detenga. Mucha gente se aprovechó de esto, ya que Turbo Pascal y Delphi en ese momento no habían inicializado las variables globales como una característica.

Pasando a Windows, hay una capa entre las direcciones de memoria y físicas: la unidad de gestión de memoria. Este chip toma el índice de página (una máscara desplazada) de la dirección de memoria a la que intenta acceder y busca los atributos de esta página en page table. Estos atributos incluyen legibles, editables, y para chips modernos x86, indicadores no ejecutables. Con este soporte, es posible marcar secciones de .EXE o .DLL con atributos tales que cuando el cargador de Windows carga la imagen ejecutable en la memoria, asigna atributos de página apropiados para las páginas de memoria que asignan páginas de disco dentro de estas secciones.

Cuando apareció la versión de Windows de 32 bits del compilador Delphi, tenía sentido hacer que las cosas similares a realmente const, ya que el sistema operativo también tiene esta función.

+0

@Barry - ¿Tiene alguna información sobre * por qué * no se recomienda utilizar constantes asignables? Por mi parte, me gusta mucho el hecho de que las constantes grabables locales se comportan como variables globales, pero solo son visibles dentro del alcance local. –

+0

@LievenKeersmaekers porque se supone que las constantes son constantes. –

+0

@BarryKelly - lol, ese no era mi punto. Nombrales lo que sea que funcione para ti :). Me gusta la * funcionalidad * que proporcionan. Todas las alternativas a las constantes mecanografiadas son de menor consciencia, entonces, bien, constantes tipadas. –

11
  1. qué: Debido a que en las versiones anteriores de Delphi las constantes introducidas fueron asignable por defecto para mantener la compatibilidad con versiones anteriores donde siempre estaban grabable (Delphi 1 hasta principios de Pascal).
    El valor por defecto se ha cambiado ahora para hacer constantes muy constante ...

  2. modificador de compilador: {$ J +} o {$ J-} ​​{$ WRITEABLECONST on} o {$ WRITEABLECONST off}
    o en las opciones del proyecto para el compilador: verificar constantes escritas asignables

  3. Cómo funciona: Si el compilador puede calcular el valor en tiempo de compilación, reemplaza la const por su valor en todas partes en el código; de lo contrario, contiene un puntero a un área de memoria valor, que puede hacerse escribible o no.
  4. ver 3.
2

como Barry dijo, la gente tomó ventaja de consts; Una de las formas en que esto se usó fue para realizar un seguimiento de las instancias únicas. Si nos fijamos en una implementación Singleton clásico, que se vería esto:

// 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; 
Cuestiones relacionadas