21

あるプロジェクトから別のプロジェクトにDelphiコードをコピーしたところ、古いプロジェクトではコンパイルされたものの、新しいプロジェクトではコンパイルされないことがわかりました。コードは次のようになります。

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

したがって、新しいプロジェクトでは、Delphiは「左側を割り当てることができない」と文句を言います-理解できます!しかし、このコードは古いプロジェクトでコンパイルされます。だから私の質問は、なぜですか?constsを再割り当てできるようにするコンパイラスイッチはありますか?それはどのように機能しますか?constsはコンパイル時にそれらの値に置き換えられたと思いましたか?

4

4 に答える 4

30

割り当て可能な型付き定数をオンにする必要があります。プロジェクト->オプション->コンパイラ->割り当て可能な型付き定数

{$J+}また、pasファイルにまたはを追加することもでき{$WRITEABLECONST ON}ます。これは、ファイルを別のプロジェクトに移動しても機能するため、おそらくより優れています。

于 2008-09-08T00:46:42.083 に答える
27

型推論された定数は、スカラー値(つまり、整数、倍精度浮動小数点数など)のみにすることができます。これらの種類の定数の場合、コンパイラーは、式で定数と出会うたびに、定数の記号を定数の値に置き換えます。

一方、型付き定数は、構造化された値(配列とレコード)にすることができます。これらの人は、実行可能ファイルに実際のストレージを必要とします。つまり、OSが実行可能ファイルをロードするときに、型付き定数の値がメモリ内のある場所に物理的に含まれるように、ストレージを割り当てる必要があります。

歴史的に、初期のDelphiとその前身であるTurbo Pascalの型付き定数が書き込み可能である(したがって、本質的に初期化されたグローバル変数である)理由を説明するには、DOSの時代に戻る必要があります。

DOSは、x86用語でリアルモードで実行されます。これは、 MMUが仮想物理マッピングを行うことなく、プログラムが物理メモリに直接アクセスできることを意味します。プログラムがメモリに直接アクセスできる場合、メモリ保護は有効になりません。つまり、任意のアドレスにメモリがある場合、リアルモードで読み取りと書き込みの両方が可能です。

そのため、実行時にメモリ内のアドレスに値が割り当てられる、型付き定数を使用するDOS用のTurbo Pascalプログラムでは、その型付き定数は書き込み可能になります。ハードウェアMMUが邪魔になり、プログラムがそれに書き込むのを妨げることはありません。同様に、PascalにはC ++のような「定数」の概念がないため、型システムにはあなたを止めるものは何もありません。Turbo PascalとDelphiは当時、グローバル変数を機能として初期化していないため、多くの人がこれを利用していました。

Windowsに移ると、メモリアドレスと物理アドレスの間にメモリ管理ユニットという層があります。このチップは、アクセスしようとしているメモリアドレスのページインデックス(シフトされたマスク)を取得し、そのページテーブルでこのページの属性を検索します。これらの属性には、読み取り可能、書き込み可能、​​および最新のx86チップの場合は実行不可能なフラグが含まれます。このサポートにより、.EXEまたは.DLLのセクションに属性をマークして、Windowsローダーが実行可能イメージをメモリにロードするときに、これらのセクション内のディスクページにマップされるメモリページに適切なページ属性を割り当てることができます。

32ビットのWindowsバージョンのDelphiコンパイラが登場したとき、OSにもこの機能があるため、constのようなものを実際にconstにすることは理にかなっています。

于 2008-09-08T02:41:20.467 に答える
11
  1. 理由:以前のバージョンのDelphiでは、型付き定数はデフォルトで割り当て可能であり、常に書き込み可能であった古いバージョン(Delphi 1から初期のPascalまで)との互換性を維持していました。
    デフォルトが変更され、定数が実際に一定になるようになりました…</ p>

  2. コンパイラスイッチ:{$J+}または{$J-}{$WRITEABLECONSTON}または{$WRITEABLECONSTOFF}
    またはコンパイラのプロジェクトオプション:割り当て可能な型付き定数を確認します

  3. 仕組み:コンパイラがコンパイル時に値を計算できる場合は、コード内のすべての場所でconstをその値に置き換えます。それ以外の場合は、値を保持するメモリ領域へのポインタを保持します。これにより、書き込み可能かどうかを指定できます。
  4. 3を参照してください。
于 2008-09-16T00:46:20.457 に答える
2

Barry が言ったように、人々は const を利用しました。これが使用された方法の 1 つは、シングルトン インスタンスを追跡することでした。古典的なシングルトンの実装を見ると、次のようになります。

  // 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;
于 2008-09-19T06:33:42.983 に答える