В Delphi 7, почему я могу присвоить значение const? - PullRequest
21 голосов
/ 08 сентября 2008

Я скопировал некоторый код Delphi из одного проекта в другой и обнаружил, что он не компилируется в новом проекте, хотя в старом. Код выглядит примерно так:

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

Таким образом, в новом проекте Delphi жалуется, что «левая сторона не может быть назначена» - понятно! Но этот код компилируется в старом проекте. Итак, мой вопрос, почему ? Есть ли переключатель компилятора, позволяющий переназначать констант? Как это вообще работает? Я думал, что conts были заменены их значениями во время компиляции?

Ответы [ 4 ]

29 голосов
/ 08 сентября 2008

Вам необходимо включить назначаемые типизированные константы. Проект -> Параметры -> Компилятор -> Назначаемые типизированные константы

Также вы можете добавить {$J+} или {$WRITEABLECONST ON} в файл pas, что, вероятно, лучше, так как он будет работать, даже если вы переместите файл в другой проект.

27 голосов
/ 08 сентября 2008

Константы, определяемые по типу, могут быть только скалярными значениями - то есть такими вещами, как целые числа, числа с двойными числами и т. Д. Для этих типов констант компилятор действительно заменяет символ константы значением константы всякий раз, когда он встречает их в выражениях.

Типизированные константы, с другой стороны, могут быть структурированными значениями - массивами и записями. Этим парням нужно фактическое хранилище в исполняемом файле, т. Е. Им должно быть выделено хранилище для них таким образом, чтобы, когда ОС загружает исполняемый файл, значение типизированной константы физически содержалось в некотором месте в памяти.

Чтобы объяснить, почему исторически типизированные константы в раннем Delphi и его предшественнике Turbo Pascal доступны для записи (и, таким образом, по существу инициализировали глобальные переменные), нам нужно вернуться к временам DOS.

DOS работает в реальном режиме в терминах x86. Это означает, что программы имеют прямой доступ к физической памяти без каких-либо MMU виртуальных и физических отображений. Когда программы имеют прямой доступ к памяти, защита памяти не действует. Другими словами, если по какому-либо адресу есть память, в реальном режиме она доступна как для чтения, так и для записи.

Таким образом, в программе Turbo Pascal для DOS с типизированной константой, значение которой распределяется по адресу в памяти во время выполнения, эта типизированная константа будет доступна для записи. Нет никакого аппаратного MMU, мешающего и мешающего программе записать в него. Точно так же, поскольку Паскаль не имеет понятия «константности», которое есть в C ++, в системе типов нет ничего, что могло бы вас остановить. Многие люди воспользовались этим, поскольку Turbo Pascal и Delphi в то время еще не инициализировали глобальные переменные как функцию.

Переходя к Windows, существует слой между адресами памяти и физическими адресами: блок управления памятью. Этот чип берет индекс страницы (сдвинутую маску) адреса памяти, к которому вы пытаетесь получить доступ, и ищет атрибуты этой страницы в своей таблице страниц . Эти атрибуты включают в себя читаемые, записываемые и для современных чипов x86 неисполняемые флаги. Благодаря этой поддержке можно помечать разделы .EXE или .DLL такими атрибутами, чтобы при загрузке Windows загрузчик выполнил образ в память, он назначал соответствующие атрибуты страниц для страниц памяти, которые отображаются на страницы диска в этих разделах.

Когда появилась 32-битная версия компилятора Delphi для Windows, имело смысл сделать const-подобные вещи действительно const, поскольку ОС также имеет эту функцию.

11 голосов
/ 16 сентября 2008
  1. Почему: потому что в предыдущих версиях Delphi типизированные константы по умолчанию назначались для сохранения совместимости со старыми версиями, где они всегда были доступны для записи (от Delphi 1 до раннего Pascal).
    Теперь значение по умолчанию было изменено, чтобы сделать константы действительно постоянными…

  2. Переключатель компилятора: {$ J +} или {$ J-} ​​{$ WRITEABLECONST ON} или {$ WRITEABLECONST OFF}
    Или в опциях проекта для компилятора: отметьте назначаемые типизированные константы

  3. Как это работает: если компилятор может вычислить значение во время компиляции, он заменяет const своим значением повсюду в коде, в противном случае он содержит указатель на область памяти, содержащую значение, которое можно сделать доступным для записи или нет ,
  4. см. 3.
2 голосов
/ 19 сентября 2008

Как сказал Барри, люди воспользовались преимуществами; Один из способов, которым это было использовано, был для отслеживания единичных случаев. Если вы посмотрите на классическую одноэлементную реализацию, вы увидите следующее:

  // 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;
...