2008-09-08 74 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; 
相關問題