Почему в Delphi 7 я могу присвоить значение константе?
Вопрос
Я скопировал некоторый код Delphi из одного проекта в другой и обнаружил, что он не компилируется в новом проекте, хотя в старом он компилировался.Код выглядит примерно так:
procedure TForm1.CalculateGP(..)
const
Price : money = 0;
begin
...
Price := 1.0;
...
end;
Так вот в новом проекте Delphi жалуется, что "левая часть не может быть назначена" - понятно!Но этот код компилируется в старом проекте.Итак, мой вопрос в том, почему?Есть ли переключатель компилятора, позволяющий переназначать константы?Как это вообще работает?Я думал, что константы были заменены их значениями во время компиляции?
Решение
Вам необходимо включить назначаемые типизированные константы.Проект -> Параметры -> Компилятор -> Назначаемые типизированные константы
Также вы можете добавить {$J+}
или {$WRITEABLECONST ON}
в файл pas, что, вероятно, лучше, поскольку оно будет работать, даже если вы переместите файл в другой проект.
Другие советы
Константы, выведенные по типу, могут быть только скалярными значениями, т.е.такие вещи, как целые числа, двойные числа и т. д.Для констант такого типа компилятор действительно заменяет символ константы значением константы всякий раз, когда он встречает их в выражениях.
С другой стороны, типизированные константы могут быть структурированными значениями — массивами и записями.Этим ребятам нужно реальное хранилище в исполняемом файле, т.е.им необходимо выделить для них хранилище, чтобы, когда ОС загружает исполняемый файл, значение типизированной константы физически содержалось в некотором месте памяти.
Чтобы объяснить, почему исторически типизированные константы в ранних версиях Delphi и его предшественнике Turbo Pascal были доступны для записи (и, таким образом, по сути, инициализировали глобальные переменные), нам нужно вернуться во времена DOS.
DOS работает в реальном режиме, в терминах x86.Это означает, что программы имеют прямой доступ к физической памяти без каких-либо ММУ выполнение виртуально-физических отображений.Когда программы имеют прямой доступ к памяти, защита памяти не действует.Другими словами, если по любому заданному адресу имеется память, она доступна как для чтения, так и для записи в реальном режиме.
Таким образом, в программе Turbo Pascal для DOS с типизированной константой, значение которой выделяется по адресу в памяти во время выполнения, эта типизированная константа будет доступна для записи.Никакой аппаратный MMU не мешает программе писать в него.Точно так же, поскольку в Паскале нет понятия «константности», которое есть в C++, в системе типов нет ничего, что могло бы вас остановить.Многие этим воспользовались, поскольку в Turbo Pascal и Delphi в то время не было возможности инициализировать глобальные переменные.
Переходя к Windows, между адресами памяти и физическими адресами существует прослойка:блок управления памятью.Этот чип берет индекс страницы (смещенную маску) адреса памяти, к которому вы пытаетесь получить доступ, и ищет атрибуты этой страницы в ее памяти. таблица страниц.Эти атрибуты включают доступные для чтения, записи, а для современных чипов x86 — неисполняемые флаги.Благодаря этой поддержке можно пометить разделы .EXE или .DLL такими атрибутами, что, когда загрузчик Windows загружает исполняемый образ в память, он назначает соответствующие атрибуты страниц для страниц памяти, которые сопоставляются со страницами диска в этих разделах.
Когда появилась 32-битная версия компилятора Delphi для Windows, имело смысл создавать константно-подобные вещи. Действительно const, поскольку операционная система также имеет эту функцию.
Почему:Потому что в предыдущих версиях Delphi типизированные константы можно было назначать по умолчанию, чтобы сохранить совместимость со старыми версиями, где они всегда были доступны для записи (от Delphi 1 до раннего Паскаля).
Значение по умолчанию теперь изменено, чтобы сделать константы действительно постоянными…Переключатель компилятора:{$J+} или {$J-} {$WRITEABLECONST ON} или {$WRITEABLECONST OFF}
Или в опциях проекта для компилятора:проверить назначаемые типизированные константы- Как это работает:Если компилятор может вычислить значение во время компиляции, он заменяет const его значением повсюду в коде, в противном случае он сохраняет указатель на область памяти, содержащую значение, которую можно сделать доступной для записи или нет.
- см. 3.
Как сказал Барри, люди воспользовались константами;Одним из способов его использования было отслеживание одноэлементных экземпляров.Если вы посмотрите на классическую реализацию синглтона, вы увидите следующее:
// 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;