我将一些Delphi代码从一个项目复制到另一个项目,发现它不能在新项目中编译,尽管它在旧项目中编译。代码看起来像这样:
procedure TForm1.CalculateGP(..)
const
Price : money = 0;
begin
...
Price := 1.0;
...
end;
所以在新项目中,Delphi抱怨“左侧无法分配” - 可以理解!但是这段代码在旧项目中编译。所以我的问题是,为什么?是否有编译器开关允许重新分配consts?这怎么样?我认为在编译时它们的值被替换了?
答案 0 :(得分:29)
您需要打开可分配的类型常量。 项目 - >选项 - >编译器 - >可分配的类型常量
此外,您可以将{$J+}
或{$WRITEABLECONST ON}
添加到pas文件中,这可能更好,因为即使您将文件移动到另一个项目它也能正常工作。
答案 1 :(得分:27)
类型推断的常量只能是标量值 - 即整数,双精度等等。对于这些常量,编译器确实用常量的值替换常量的符号,只要它在表达式中遇到它们。
另一方面,类型常量可以是结构化值 - 数组和记录。这些人需要在可执行文件中存储实际存储 - 即他们需要为他们分配存储空间,这样当操作系统加载可执行文件时,类型化常量的值实际上包含在内存中的某个位置。
为了解释为什么历史上早期Delphi及其前身Turbo Pascal中的类型常量是可写的(因而基本上是初始化的全局变量),我们需要回到DOS的时代。
DOS以实时模式运行,用x86术语表示。这意味着程序可以直接访问物理内存,而无需MMU进行虚拟物理映射。当程序可以直接访问内存时,没有内存保护功能生效。换句话说,如果任何给定地址都有内存,则它在实模式下都是可读写的。
因此,在具有类型常量的DOS的Turbo Pascal程序中,其值在运行时在内存中的地址处分配,该类型常量将是可写的。没有硬件MMU阻碍并阻止程序写入它。同样,因为Pascal没有C ++所具有的'const'ness概念,所以类型系统中没有任何东西可以阻止你。很多人都利用了这一点,因为Turbo Pascal和Delphi当时没有将全局变量初始化为一个特征。
转到Windows,内存地址和物理地址之间有一层:内存管理单元。该芯片获取您尝试访问的内存地址的页面索引(移位掩码),并在其page table中查找此页面的属性。这些属性包括可读,可写和现代x86芯片,非可执行标志。有了这种支持,就可以使用以下属性标记.EXE或.DLL的各个部分:当Windows加载程序将可执行映像加载到内存中时,它会为映射到这些部分中的磁盘页的内存页分配适当的页面属性。
当32位Windows版本的Delphi编译器出现时,因此操作系统也具有此功能,因此使const类似的东西真正 const是有意义的。
答案 2 :(得分:11)
为什么:因为在Delphi的早期版本中,默认情况下可以分配类型常量以保持与旧版本的兼容性,在旧版本中它们始终可写(Delphi 1直到早期的Pascal)。
现在已经更改了默认值以使常量保持不变...
编译开关:{$ J +}或{$ J-} {$ WRITEABLECONST ON}或{$ WRITEABLECONST OFF}
或者在编译器的项目选项中:检查可分配的类型常量
答案 3 :(得分:2)
// 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;