2008-09-08 40 views
21

我复制了一些Delphi代码从一个项目到另一个,并发现它并没有在新项目中编译,虽然它在旧的一样。该代码看起来是这样的:在Delphi 7中,为什么我可以给const赋值?

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

所以在新项目中,德尔福抱怨说,“左侧不能被分配到” - 可以理解的!但是这个代码在旧项目中编译。所以我的问题是,为什么?是否有编译器开关允许重新分配const?这甚至如何工作?我认为consts在编译时被它们的值所取代?

回答

29

您需要打开分配类型的常量上。 项目 - >选项 - >编译器 - >可分配类型的常量

你也可以添加{$J+}{$WRITEABLECONST ON}到PAS文件,这可能是更好的,因为它会即使您将文件移动到另一个项目。

+2

这样做的伎俩。 Google显示编译器指令为{$ J +}。 它也在项目选项中,可能应该看过那里:P – Blorgbeard 2008-09-08 00:53:46

+0

是的,我以前曾经被这个打击过。 (编辑后添加细节后我的第一篇文章) – Ray 2008-09-08 00:58:22

27

类型推断的常量只能是标量值 - 即类似整数,双精度等的东西。对于这些类型的常量,只要编译器在表达式中满足它们,就确实将常量的符号替换为常量的值。

类型的常量,在另一方面,可以构成值 - 阵列和记录。这些人需要在可执行文件中存储实际的内容 - 即他们需要为他们分配存储空间,以便当操作系统加载可执行文件时,类型常量的值被物理地包含在内存中的某个位置。

为了解释为什么历史上早期的Delphi及其前身Turbo Pascal中的键入常量是可写的(因此基本上初始化全局变量),我们需要回到DOS的日子。

DOS运行在实模式,在86项。这意味着程序可以直接访问物理内存而不需要任何虚拟物理映射。当程序直接访问内存时,内存保护不起作用。换句话说,如果在任何给定地址上都有内存,则它在实模式下都是可读写的。

所以,在Turbo Pascal的程序DOS与类型化常数,其值在运行时内存在地址分配,这类型的常数将是可写的。没有硬件MMU妨碍程序写入。同样,由于Pascal没有C++所具有的“常量”的概念,类型系统中没有任何东西可以阻止你。很多人都利用了这一点,因为当时Turbo Pascal和Delphi并没有将全局变量初始化为特征。

移动到Windows,有存储器地址与物理地址之间的层:存储器管理单元。该芯片将获取您试图访问的内存地址的页面索引(移动掩码),并在其page table中查找该页面的属性。这些属性包括可读,可写,以及用于现代x86芯片的非可执行标志。有了这种支持,就可以用属性标记.EXE或.DLL的各个部分,以便Windows加载器将可执行映像加载到内存中时,它会为映射到这些节中的磁盘页的内存页分配适当的页面属性。

当Delphi编译器的32位Windows版本出现时,因此有意义的做const常量的东西真的是常量,因为OS也具有此功能。

11
  1. 为什么:因为在Delphi的早期版本中的类型的常量是自动分配,以保持与旧版本,他们总是写(德尔福1到帕斯卡早期)的兼容性。
    默认现在已经改变,使常数真正不变...

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

  3. 工作原理:如果编译器可以在编译时计算该值,它会在代码中的任何位置将const替换为它的值,否则它将持有指向存储区的指针价值,可以写或不写。
  4. 看到3.
2

像巴里说,人们趁着consts的;其中一种方法是用于跟踪单例实例。 如果你看看一个经典的单例实现,你会看到这样的:

// 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; 
相关问题