В Delphi 7, почему я могу присвоить значение const?
Я скопировал некоторый код Delphi из одного проекта в другой и обнаружил, что он не компилируется в новом проекте, хотя он и делался в старом. Код выглядит примерно так:
procedure TForm1.CalculateGP(..)
const
Price : money = 0;
begin
...
Price := 1.0;
...
end;
Итак, в новом проекте Delphi жалуется, что "левая сторона не может быть назначена" - понятно! Но этот код компилируется в старом проекте. Поэтому мой вопрос: почему? Есть ли переключатель компилятора, позволяющий переназначать константы? Как это работает? Я думал, что consts были заменены их значениями во время компиляции?
Ответы
Ответ 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;