在Delphi 7中,为什么我可以为const赋值?

时间:2008-09-08 00:34:46

标签: delphi constants

我将一些Delphi代码从一个项目复制到另一个项目,发现它不能在新项目中编译,尽管它在旧项目中编译。代码看起来像这样:

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

所以在新项目中,Delphi抱怨“左侧无法分配” - 可以理解!但是这段代码在旧项目中编译。所以我的问题是,为什么?是否有编译器开关允许重新分配consts?这怎么样?我认为在编译时它们的值被替换了?

4 个答案:

答案 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)

  1. 为什么:因为在Delphi的早期版本中,默认情况下可以分配类型常量以保持与旧版本的兼容性,在旧版本中它们始终可写(Delphi 1直到早期的Pascal)。
    现在已经更改了默认值以使常量保持不变...

  2. 编译开关:{$ J +}或{$ J-}​​ {$ WRITEABLECONST ON}或{$ WRITEABLECONST OFF}
    或者在编译器的项目选项中:检查可分配的类型常量

  3. 如何工作:如果编译器可以在编译时计算该值,它会在代码中的任何位置用const值替换const,否则它会保存一个指向保存该值的内存区域的指针,该值可以写入或不可写。
  4. 见3。

答案 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;