Подтвердить что ты не робот

В Delphi 7, почему я могу присвоить значение const?

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

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

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

4b9b3361

Ответ 1

Вам нужно включить назначаемые типизированные константы. Проект → Параметры → Компилятор → Назначаемые типизированные константы

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

Ответ 2

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

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

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

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

Итак, в программе Turbo Pascal для DOS с типизированной константой, значение которой выделяется по адресу в памяти во время выполнения, типизированная константа будет доступна для записи. Металлический MMU не мешает и не позволяет программе писать на него. Точно так же, поскольку у Паскаля нет понятия "const'ness", который имеет С++, в системе типов нет ничего, чтобы остановить вас. Многие люди воспользовались этим, так как Turbo Pascal и Delphi в то время не инициализировали глобальные переменные как функцию.

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

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

Ответ 3

  • Почему: поскольку в предыдущих версиях Delphi типизированные константы были по умолчанию назначены для сохранения совместимости со старыми версиями, где они всегда были доступны для записи (Delphi 1 до раннего Pascal).
    Значение по умолчанию теперь изменено, чтобы константы были постоянными...

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

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

Ответ 4

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

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