13
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

DelphiAdvent Calendar 2019

Day 10

<6> Dephi のオブジェクト指向拡張 (Pascal へのオブジェクト指向拡張の歴史と Delphi)

Last updated at Posted at 2019-12-09

6. Dephi のオブジェクト指向拡張

1995 年に発売された Delphi の言語は Object Pascal と呼ばれました。
image.png
Delphi のオブジェクト指向拡張を細かいトコまで説明していると大変なのでざっくりとした説明に留めます。

なお、サンプルコード中で // による行コメントを多用していますが、ご了承ください 1

See also:

6.1. Delphi のオブジェクト型

Delphi には Turbo Pascal 由来のオブジェクト型が下位互換性 2 のために残されています。非推奨でドキュメントも非常に少ないのですが、今でも使う事ができます。

ObjTest.dpr
program ObjTest;
{$APPTYPE CONSOLE}

type
  TObj = object
  private
    FValue: Integer;
  public
    constructor Init;
    destructor Done; virtual;
    function Multiply(n: Integer): Integer;
    property Value: Integer read FValue write FValue;
  end;
  PObj = ^TObj;

  { TObj }
  constructor TObj.Init;
  begin
    FValue := 1;
  end; { TObj.Init }

  destructor TObj.Done;
  begin
    ;
  end; { TObj.Done }

  function TObj.Multiply(n: Integer): Integer;
  begin
    result := FValue * n
  end; { TObj.Multiply }

var
  sObj: TObj;
  dObj: PObj;
begin
  // Static
  sObj.Init;
  sObj.Value := 3;
  Writeln(sObj.Multiply(5));

  // Dynamic
  dObj := New(PObj, Init);
  try
    dObj^.Value := 7;
    Writeln(dObj^.Multiply(9));
  finally
    Dispose(dObj, Done);
  end;

  Readln;
end. { Main }

Delphi では、ポインタの逆参照を自動的に想定する参照モデルを使うため、動的なオブジェクトを指すための逆参照演算子 (^) を省略する事ができます。上記コードだと、メインブロック中の ^ を取り除いても動作します 3。定義部の ^TObj の前にある ^ は逆参照演算子ではないので省略できません。

See also:

6.2. Delphi のクラス型

クラス という用語は Delphi 1 において、次のように定義されています。

データと関連コードを表す機能を一つの単位に集めたリスト。クラスには定義にある機能だけではなく、上位オブジェクトから継承した機能も含まれます。「クラス」という用語は「オブジェクト型」と同義です。

Turbo Pascal では クラス という用語を使っていなかったので混乱しそうになりますが、Delphi ではオブジェクト型もクラス型も (広義の) クラス型です。クラス型はレコード型と同様、構造化型の一種です。
image.png
クラス型は型宣言部 (type) にて "型名 = class (親クラス) ~ end;" として定義します。親クラスは省略できますが、省略した場合にはルートクラスである TObject を継承します。

クラスには次に挙げるようなメンバーがあります。

フィールド

フィールドはオブジェクトに属する変数のようなものです。他言語での "メンバ変数" や "インスタンス変数" に相当します。通常は可視性を private で定義します。

なお、クラスのフィールドを "クラスフィールド" とか "クラス変数" と呼んではいけません。そちらには別の意味があります。

メソッド

メソッドとはクラス型宣言内で宣言された、オブジェクトに対して操作を行う手続き (procedure) または関数 (function) を指します。他言語の "メンバ関数" に相当します。

コンストラクタとデストラクタはオブジェクトの作成と廃棄を制御する特殊なメソッドです。

なお、クラスのメソッドを "クラスメソッド" とか "クラス関数" と呼んではいけません。そちらには別の意味があります。

プロパティ

プロパティはオブジェクトの属性を定義するものです。フィールドの値を読み書きするメソッド (ゲッター/セッター) をカプセル化したりできます。プロパティの例には、フォームのキャプション、フォントのサイズ、データベーステーブルの名前などがあります。

See also:

6.2.1. クラス型の定義

次のようにしてクラスを定義できます。

type
  TCls = class(TObject) { TObject を継承したクラス型 TCls の定義 }
  private
    FValue: Integer;
  end;
  • ルートクラスは TObject です。
  • 親クラスを指定しなかった場合の親クラスは TObject です。
  • 多重継承はできません。
  • 可視性指定子があります。
  • プロパティがあります。
  • クラスメソッドがあります。
  • 静的なオブジェクトは作れません。

6.2.2. クラス型の定義

クラスの作成と破棄は次のようにして行います。次のコードは ObjTest.dpr のクラス型版です。

ClsTest.dpr
program ClsTest;
{$APPTYPE CONSOLE}

type
  TCls = class
  private
    FValue: Integer;
  public
    constructor Create;
    destructor Destroy; override;
    function Multiply(n: Integer): Integer;
    property Value: Integer read FValue write FValue;
  end;

  { TCls }
  constructor TCls.Create;
  begin
    FValue := 1;
  end; { TCls.Create }

  destructor TCls.Destroy;
  begin
    inherited;
  end; { TCls.Destroy }

  function TCls.Multiply(n: Integer): Integer;
  begin
    result := FValue * n
  end; { TCls.Multiply }

var
  Cls: TCls;
begin
  Cls := TCls.Create;
  try
    Cls.Value := 7;
    Writeln(Cls.Multiply(9));
  finally
    Cls.Free;
  end;

  Readln;
end. { Main }

コンストラクタ Create() と Free() メソッドですね。自動参照カウント (ARC) 4 が有効な場合には DisposeOf() メソッドを使って破棄します。

コンストラクタには結果型の定義はありませんが、コンストラクタが生成したオブジェクトまたはコンストラクタを呼び出したオブジェクトへの参照が返されます。with 文を使って、変数の宣言なしにクラスを使う事もできます。

  with TStringList.Create do
    begin
      Add('Hello,');
      Add('world.');
      Writeln(Text);
      Free;
    end;

クラスではクラス型とポインタ型を別々に宣言したり、オブジェクトポインタを明示的に逆参照する必要はありません。

See also:

6.2.3. 可視性指定子 (Visibility Specifiers)

Delphi の可視性指定子とその範囲は次の通りです。

同一ユニット 別ユニット 下位クラス 実行時
型情報
オートメーション
型情報
strict private 5 × × × × ×
private × × × ×
strict protected 5 × × × ×
protected × × ×
public × ×
published ×
automated (Win32) ×

published を使うには {$M+} コンパイラ指令を付ける必要があります。また、published に指定できるフィールドはクラス型またはインターフェイス型に限定されます。

Unit1.pas
unit Unit1;

interface

type
  TValue = class
  private
    FValue: Integer;
  public
    property Value: Integer read FValue write FValue;
  end;

  TClsA = class
    procedure Dummy; virtual;
  strict private
    FStrictPrivate: TValue;
  private
    FPrivate: TValue;
  strict protected
    FStrictProtected: TValue;
  protected
    FProtected: TValue;
  public
    FPublic: TValue;
  published
    FPublished: TValue;
  end;

  TClsB = class(TClsA)
    procedure Dummy; override;
  end;

  procedure DummyU1;

implementation

procedure DummyU1;
var
  V: Integer;
  ClsA: TClsA;
  ClsB: TClsB;
begin
  // --------------------------------------
  // Unit1.TClsA
  // Unit1.ClsA <- アクセス
  // --------------------------------------
  ClsA := TClsA.Create;
  try
    try
//    V := ClsA.FStrictPrivate.Value;   // NG
      V := ClsA.FPrivate.Value;
//    V := ClsA.FStrictProtected.Value; // NG
      V := ClsA.FProtected.Value;
      V := ClsA.FPublic.Value;
      V := ClsA.FPublished.Value;
    except
    end;  
  finally
    ClsA.Free;
  end;

  // --------------------------------------
  // Unit1.TClsA, Unit1.TClsB(継承)
  // Unit1.ClsB <- アクセス
  // --------------------------------------
  ClsB := TClsB.Create;
  try
    try
  //  V := ClsB.FStrictPrivate.Value;   // NG
      V := ClsB.FPrivate.Value;
  //  V := ClsB.FStrictProtected.Value; // NG
      V := ClsB.FProtected.Value;
      V := ClsB.FPublic.Value;
      V := ClsB.FPublished.Value;
    except
    end;  
  finally
    ClsB.Free;
  end;
end; { Dummy }

{ TClsA }

procedure TClsA.Dummy;
var
  V: Integer;
begin
// --------------------------------------
// Unit1.TClsA
// Unit1.TClsA.Self <- アクセス
// --------------------------------------
  V := Self.FStrictPrivate.Value;
  V := Self.FPrivate.Value;
  V := Self.FStrictProtected.Value;
  V := Self.FProtected.Value;
  V := Self.FPublic.Value;
  V := Self.FPublished.Value;
end;

{ TClsB }

procedure TClsB.Dummy;
var
  V: Integer;
begin
// --------------------------------------
// Unit1.TClsA, Unit1.TClsB(継承)
// Unit1.TClsB.Self <- アクセス
// --------------------------------------
//V := Self.FStrictPrivate.Value;
  V := Self.FPrivate.Value;
  V := Self.FStrictProtected.Value;
  V := Self.FProtected.Value;
  V := Self.FPublic.Value;
  V := Self.FPublished.Value;
end;

end.
Unit2.pas
unit Unit2;

interface

uses
  Unit1;

type
  TClsC = class(TClsA)
    procedure Dummy; override;
  end;

  procedure DummyU2;

implementation

procedure DummyU2;
var
  V: Integer;
  ClsA: TClsA;
  ClsB: TClsB;
  ClsC: TClsC;
begin
  // --------------------------------------
  // Unit1.TClsA
  // Unit2.ClsA <- アクセス
  // --------------------------------------
  ClsA := TClsA.Create;
  try
    try
//    V := ClsA.FStrictPrivate.Value;   // NG
//    V := ClsA.FPrivate.Value;         // NG
//    V := ClsA.FStrictProtected.Value; // NG
//    V := ClsA.FProtected.Value;       // NG
      V := ClsA.FPublic.Value;
      V := ClsA.FPublished.Value;
    except
    end;
  finally
    ClsA.Free;
  end;

  // --------------------------------------
  // Unit1.TClsA, Unit1.TClsB
  // Unit2.ClsB <- アクセス
  // --------------------------------------
  ClsB := TClsB.Create;
  try
    try
//    V := ClsB.FStrictPrivate.Value;   // NG
//    V := ClsB.FPrivate.Value;         // NG
//    V := ClsB.FStrictProtected.Value; // NG
//    V := ClsB.FProtected.Value;       // NG
      V := ClsB.FPublic.Value;
      V := ClsB.FPublished.Value;
    except
    end;
  finally
    ClsB.Free;
  end;

  // --------------------------------------
  // Unit1.TClsA, Unit2.TClsC
  // Unit2.ClsC <- アクセス
  // --------------------------------------
  ClsC := TClsC.Create;
  try
    try
//    V := ClsC.FStrictPrivate.Value;   // NG
//    V := ClsC.FPrivate.Value;         // NG
//    V := ClsC.FStrictProtected.Value; // NG
      V := ClsC.FProtected.Value;
      V := ClsC.FPublic.Value;
      V := ClsC.FPublished.Value;
    except
    end;
  finally
    ClsC.Free;
  end;

end; { DummyU2 }

{ TClsC }
procedure TClsC.Dummy;
var
  V: Integer;
begin
// --------------------------------------
// Unit1.TClsA, Unit2.TClsC(継承)
// Unit2.TClsC.Self <- アクセス
// --------------------------------------
//V := Self.FStrictPrivate.Value;   // NG
//V := Self.FPrivate.Value;         // NG
  V := Self.FStrictProtected.Value;
  V := Self.FProtected.Value;
  V := Self.FPublic.Value;
  V := Self.FPublished.Value;
end;

end.

6.3. Delphi のクラス参照型 (メタクラス)

クラスのインスタンス (オブジェクト) へのポインタはクラス型の変数です。クラス参照型 (メタクラス) 6 はクラス型そのものに対するポインタです。

クラス参照型は型宣言部 (type) にて "型名 = class of 対象クラス型;" として定義します。次のようにしてクラス参照型を定義できます。

type
  TObjectClass = class of TObject;

考え方はポインタ型と同じです。

type
  TMyClass = class
    function FuncA: Integer;
    class function FuncB: Integer;
  end;
  MyC = class of TMyClass; // TMyClass のクラス参照型

  function TMyClass.FuncA: Integer;
  begin
    ...
  end;

  class function TMyClass.FuncB: Integer;
  begin
    ...
  end;

var
  MyClass: TMyClass;
  v: Integer;
begin
  MyClass := TMyClass.Create; // <- クラス参照でコンストラクタが呼び出されている
  v := MyClass.FuncA;         // オブジェクト参照
  v := TMyClass.FuncB;        // クラス参照
  v := MyC.FuncB;             // クラス参照型を使ったクラス参照
  ...

MyClass は TMyClass のオブジェクトです。正確には TMyClass のインスタンスへのポインタです。 FuncA はオブジェクト参照で呼び出されています。コード中の FuncB は TMyClass のクラスメソッドなのでクラス参照で呼び出されています。

クラスメソッドはクラス参照だけでなく、オブジェクト参照でも呼び出す事ができます。

コンストラクタは通常、クラス参照で呼び出されます。コンストラクタをオブジェクト参照で呼び出した場合、オブジェクト (インスタンス) は生成されず、通常のメソッドとして実行されます。

See also:

6.4. Delphi のインターフェイス型

(オブジェクト)インターフェイスはクラスに実装させることができるメソッドを定義します。インターフェイスはクラスと同じように宣言しますが、直接インスタンス化することはできません。

インターフェイス型は Delphi 3 で実装され、Delphi 6 で汎用的な IInterface (以前は IUnknown) が使えるようになりました。また、インターフェイスを使うと多重継承に似た事ができます。インターフェイス型もまた構造化型です。

See also:

6.4.1. インターフェイス型の定義

インターフェイス型は型宣言部 (type) にて "型名 = interface (親インターフェイス) ['GUID'] ~ end;" として定義します。

次のようにしてインターフェイス型を定義できます。GUID は Delphi のコードエディタで〔Ctrl〕+〔Shift〕+〔G〕で生成できます。インターフェイス型の親インターフェイスを省略するとルートインターフェイス IInterface から派生します。

type
  IItfA = interface
  ['{xxxxxxxx-6953-4C6B-B1E3-DBB2E7419162}']
    function FuncA: string;
  end;

  IItfB = interface(IItfA)
  ['{xxxxxxxx-261D-4742-BC7E-8D05B22D8016}']
    function FuncB: string;
  end;

6.4.2. インターフェイス型を使ったクラス

インターフェイスを使ったクラスは基本的に TInterfacedObject (またはその下位クラス) から派生します。IItfB を使ったクラスの実装例を次に示します。

type
  TCls = class (TInterfacedObject, IItfB)
    function FuncA: string;
    function FuncB: string;
  end;

  function TCls.FuncA: string;
  begin
    result := 'FuncA';
  end;

  function TCls.FuncB: string;
  begin
    result := 'FuncB';
  end;

このクラスを使ってみます。

var
  Cls: TCls;
begin
  Cls := TCls.Create;
  try
    Writeln(Cls.FuncA);
    Writeln(Cls.FuncB);
  finally
    Cls.Free;
  end;
  Readln;
end.

実行結果は次の通りです。

FuncA
FuncB

クラス型変数ではなくインターフェイス型変数を使うと参照カウントが行われるようになり、Free での破棄が不要になります。

var
  Cls: IItfB; // <- インターフェイス型変数を使う
begin
  ReportMemoryReakOnShutdown := True; // アプリケーション終了時にメモリリークを報告する
  Cls := TCls.Create;
  Writeln(Cls.FuncA);
  Writeln(Cls.FuncB);
  Readln;
end.

See also:

6.5. Delphi の抽象クラス型

Delphi 2005 以降では次のようにして抽象クラス型を定義できます。

type
  TCls = class abstract (TObject) { TObject を継承した抽象クラス型 TCls の定義 }
  end;

...なのですが、何故か抽象クラス型をインスタンス化できてしまい、エラーも警告も出ません。つまり abstract の指定はマーカーとしての意味しかありません。

abstract を指定していなくても抽象メソッドが含まれていれば抽象クラスになります。こちらもインスタンス化できてしまうのですが、コンパイラが警告 W1020 を吐きます。

次のようなコンパイラ指令を書いておけば警告をエラーに昇格できるため、抽象メソッドを含むクラスのインスタンス化は阻止する事ができます。

{$WARN CONSTRUCTING_ABSTRACT ERROR}

なお、abstract の代わりに sealed を指定すると継承できないクラス (シールドクラス/ファイナルクラス) を作る事ができます。こちらの方は継承しようとするとちゃんとエラーになります...謎です。

6.6. Delphi の高度なレコード型 (Advanced Record)

Delphi 2006 からレコード型にメソッドやプロパティ、コンストラクタを持たせられるようになりました。次のコードは ObjTest.dpr の高度なレコード型版です。

RecTest.dpr
program RecTest;
{$APPTYPE CONSOLE}

type
  TRec = record
  private
    FValue: Integer;
  public
    constructor Create(InitialValue: Integer);
    procedure Destroy;
    function Multiply(n: Integer): Integer;
    property Value: Integer read FValue write FValue;
  end;
  PRec = ^TRec;

  { TRec }
  constructor TRec.Create(InitialValue: Integer);
  begin
    FValue := InitialValue;
  end; { TRec.Init }

  procedure TRec.Destroy;
  begin
    ;
  end; { TRec.Destroy }

  function TRec.Multiply(n: Integer): Integer;
  begin
    result := FValue * n
  end; { TRec.Multiply }

var
  sRec: TRec;
  dRec: PRec;
begin
  // Static (1)
  sRec.Value := 3;
  Writeln(sRec.Multiply(5));

  // Static (2)
  sRec := sRec.Create(3);
  Writeln(sRec.Multiply(5));

  // Static (3)
  sRec.Create(3);
  Writeln(sRec.Multiply(5));

  // Dynamic
  dRec := New(PRec, Create(0));
  try
    dRec^.Value := 7;
    Writeln(dRec^.Multiply(9));
    dRec^.Destroy;
  finally
    Dispose(dRec);
  end;

  Readln;
end. { Main }

オブジェクト型とほぼ同じような使い方ができるようになっています。メインブロック中の ^ を取り除いても動作します 3。Destroy() がデストラクタではなくただのメソッドになっている事に注意してください。

  • コンストラクタには最低でも一つのパラメータが必要で、初期値を持つパラメータは初期値が指定されない可能性がある (内部で使っている引数なしのコンストラクタとカブる) ため、使用する事ができません。
  • デストラクタはありません。
  • レコードなので継承はできません。
  • レコードなので可変部は普通に使えます。

サンプルコードの (2) の所は、クラスのように書いてはいけないのでしょうか?

  // Static (2)
  sRec := TRec.Create(3);
  Writeln(sRec.Multiply(5));

書けるのは書けるのですが、この処理は、

  1. TRec 型の (静的な) 変数 sRec が作られる。
  2. 無名の TRec 型動的変数が作られる。
  3. sRec に動的変数がコピーされる

となってしまいます (一時的に TRec 型の変数が 2 つ存在する) のでオススメしません。

See also:

6.7. カスタム管理レコード型 (Custom Managed Records)

Delphi 10.4 Sydney からコンストラクタやデストラクタのような働きをする演算子によって、初期化時と終了時の処理を行う事ができるようになりました。例を挙げます。

CustomManagegRecordTest.dproj
program CustomManagegRecordTest;

{$APPTYPE CONSOLE}

type
  TMyRecord = record
    Value: Integer;
    class operator Initialize(out Dest: TMyRecord);
    class operator Finalize(var Dest: TMyRecord);
  end;

{ TMyRecord }

class operator TMyRecord.Initialize(out Dest: TMyRecord);
begin
  Dest.Value := 10;
  Writeln('Start!');
end;

class operator TMyRecord.Finalize(var Dest: TMyRecord);
begin
  Writeln('Done.');
end;

begin
   begin
     var Rec: TMyRecord;
     Writeln(Rec.Value);
   end;
  Readln;
end.

カスタム管理レコード型 (TMyRecord) である Rec を空ブロックの中で使っています 7。これの実行結果は次のようになります。

Start!
10
Done.

コンストラクタやデストラクタ (ありませんけど) を明示的に呼び出さなくても、初期化コードと終了時コードが実行されます。

演算子 説明
Initialize 初期化処理を行います
Finalize 終了時処理を行います

See also:

6.7.1. 代入とコピー

カスタム管理レコード型は 継承できない (静的な) オブジェクト型のようなもの という表現がしっくりくるでしょうか。

それでも、レコード型には変わりないので、代入はコピーとなります。ポインタや参照の代入ではありません。

CustomManagegRecordTest.dproj

...

begin
  begin
    var Rec1: TMyRecord;
    var Rec2: TMyRecord;

    Rec1.Value := 11;
    Rec2 := Rec1;
    Rec1.Value := 10;

    Writeln(Rec1.Value);
    Writeln(Rec2.Value);
  end;
  Readln;
end.

結果は次のようになります。

Start!
Start!
10
11
Done.
Done.

初期化処理と終了時処理が 2 つ分行われています。

6.7.2. Assign 演算子

Assign 演算子を使うと、代入時の挙動もカスタマイズできます。

演算子 説明
Assign 代入時の処理を行います

次に例を挙げます。

CustomManagegRecordTest.dproj
program CustomManagegRecordTest;

{$APPTYPE CONSOLE}

type
  TMyRecord = record
    Value: Integer;
    class operator Initialize(out Dest: TMyRecord);
    class operator Finalize(var Dest: TMyRecord);
    class operator Assign (var Dest: TMyRecord; const [ref] Src: TMyRecord);
  end;

{ TMyRecord }

class operator TMyRecord.Initialize(out Dest: TMyRecord);
begin
  Dest.Value := 10;
  Writeln('Start!');
end;

class operator TMyRecord.Finalize(var Dest: TMyRecord);
begin
  Writeln('Done.');
end;

class operator TMyRecord.Assign (var Dest: TMyRecord; const [ref] Src: TMyRecord);
begin
  Dest.Value := Src.Value * 2; // ニヴァイ
  Writeln('Multiply!');
end;

begin
  var Rec1: TMyRecord;
  var Rec2: TMyRecord;

  Rec1.Value := 11;
  Rec2 := Rec1;
  Rec1.Value := 10;

  Writeln(Rec1.Value);
  Writeln(Rec2.Value);

  Readln;
end.

実行結果は次のようになります。

Start!
Start!
Multiply!
10
22
Done.
Done.

6.7.3. コンストラクタと Intialize 演算子の併用

コンストラクタと Initialize 演算子を併用するとどうなるのでしょう?

CustomManagegRecordTest.dproj
program CustomManagegRecordTest;

{$APPTYPE CONSOLE}

type
  TMyRecord = record
    Value: Integer;
    constructor Create(InitialValue: Integer);
    class operator Initialize(out Dest: TMyRecord);
    class operator Finalize(var Dest: TMyRecord);
  end;

{ TMyRecord }

constructor TMyRecord.Create(InitialValue: Integer);
begin
  Self.Value := InitialValue;
  Writeln('Begin!');
end;

class operator TMyRecord.Initialize(out Dest: TMyRecord);
begin
  Dest.Value := 10;
  Writeln('Start!');
end;

class operator TMyRecord.Finalize(var Dest: TMyRecord);
begin
  Writeln('Done.');
end;

begin
  with TMyRecord.Create(3) do
    Writeln(Value);
  Readln;
end.

結果はこうなりました。

Start!
Begin!
3
Done.

イニシャライザが先に走るのですね。

6.8. Delphi のヘルパー

Delphi にはヘルパーと呼ばれる機能があります。オブジェクトを継承せずに機能を拡張するもので、大別すると次の 3 種類があります。

  • クラスヘルパー
  • レコードヘルパー
  • レコードヘルパー (組み込み型用)

クラスヘルパーとレコードヘルパーは Delphi 2006 から利用可能でしたが、真価を発揮するのは Delphi 2009 以降です。XE4 以降では Integer 等のオブジェクトではない組み込み型に対してもヘルパーを作用させる事ができるようになっています。

See also:

6.9. Delphi のレコード型、オブジェクト型、クラス型、インターフェイス型の主な違い

Delphi でのレコード型、オブジェクト型、クラス型の主な違いは次の通りです。

レコード型 レコード型
(高度)
 レコード型
(カスタム管理)
オブジェクト型 クラス型 インターフェイス型
定義 record record record object class interface
継承 × × ×
フィールド ×
可変部 × × ×
メソッド ×
プロパティ × 8
可視性指定子 × 9 9 ×
コンストラクタ × 10 11 ×
デストラクタ × × 12 ×

6.10. クラスに関連する用語

言語によってクラスに関連する用語に違いがあります。同じものを指しているのに、日本語訳された時に用語がゆらいでいるものもあります。

言語 ルートクラス クラス スーパークラス サブクラス メッセージ
Delphi TObject クラス
(Class)
直接上位
(Immediate Ancestor)
直接下位
(Immediate Descendant)
メソッド呼び出し
(Method Call)
Turbo Pascal Object オブジェクト型
(Object Type)
直系上位
(Immediate Ancestor)
直系下位
(Immediate Descendant)
メソッド呼び出し
(Method Call)
Object Pascal
(Apple)
TObject オブジェクト型
(Object Type)
直系のアンセスタ (先祖)
(Immediate Ancestor)
直系のディセンダント (子孫)
(Immediate Descendant)
メソッド呼び出し
(Method Call)
Clascal TObject クラス
(Class)
スーパークラス
(Superclass)
サブクラス
(Subclass)
メソッド呼び出し
(Method Call)
Smalltalk Object クラス
(Class)
スーパークラス
(Superclass)
サブクラス
(Subclass)
メッセージ
(message)
  • Object Pascal (Apple) と Turbo Pascal ではクラスの事をオブジェクト型と呼んでいます。
  • Delphi のオブジェクト型は Turbo Pascal のオブジェクト型です。
  • Delphi ではオブジェクト型とは別にクラス型があります。
  • Delphi には Smalltalk の Message に概念のよく似た メッセージメソッド があります。

See also:

参考文献

Object Pascal Handbook

image.png

タイトル 著者 ISBN-10
(Amazon)
出版年
Object Pascal Handbook マルコ・カントゥ (著) 1514349949 2015/8/5
Object Pascal Handbook マルコ・カントゥ (著)
藤井 等 (訳)
487783401X 2016/6/10
Object Pascal Handbook
Delphi 10.4 Sydney Edition
マルコ・カントゥ (著) B08XZ44KXH 2021/3/3
Object Pascal Handbook
Delphi 11 Alexandria Edition
マルコ・カントゥ (著)
藤井 等 (訳)
4877835210 2022/4/10

最近の Delphi の文法はこれと DocWiki があればいいかと。英語版でよければ PDF を無償 DL できます。

DELPHI クイックリファレンス

image.png

タイトル 著者 ISBN-10
(Amazon)
出版年
Delphi in a Nutshell: A Desktop Quick Reference レイ・リシュナー (著) 1565926595 2000/3/26
DELPHI クイックリファレンス レイ・リシュナー (著)
光田 秀 (訳)
竹田 知生 (訳)
4873110408 2001/5/24

Delphi 5 時点の書籍ですが、Delphi の基本的な部分...言語仕様や組み込みルーチンに絞って書かれたものなので、かなりの部分が最新版の Delphi でも通用すると思います。

逆に言えば VCL 等のライブラリには一切触れられていないという事ですから、その点には注意が必要です。

Object Pascal Language Guide

image.png
Delphi 1.0 で加えられた変更を知る方法ですが、Delphi 2~4 の CD-ROM を持っているのなら DELPHI16\EXTRAS\MANUALS\ にある "Object Pascal Language Guide (OBJLANG.PDF)" を参照してみてください。紙マニュアルやヘルプ (WinHelp) に書かれている事と同じですが、視認性はいいと思います。

[ ← 5. (標準) Pascal へのオブジェクト指向拡張 ] [ ↑ 目次へ ]

  1. Delphi 1 では行コメントが使えません。行コメントが使えるのは Delphi 2 以降です。

  2. 「下位互換性のために残してある」と言いながらも Delphi, Delphi 2 で拡張が行われており、例えば下にある ObjTest.dpr はプロパティが使われているために Delphi 1 ではコンパイルできません。

  3. Delphi 2 以降で(動的)オブジェクト型や(動的)レコード型の逆参照演算子が省略できます。 2

  4. ARC は Delphi 10.4 Sydney で廃止されました。

  5. Delphi 2005 以降で利用可能です。 2

  6. Delphi 1 では オブジェクト参照型 と呼ばれていました。とても紛らわしいネーミングです。

  7. 空ブロックを使わないと、インライン宣言した変数の寿命は Readln() の後になります。

  8. Delphi 2 以降でプロパティが使えます。

  9. Publihed はありません。 2

  10. 1 つ以上のパラメータを持つ必要があります。

  11. コンストラクタと似たような働きをする Initialize 演算子があります。

  12. デストラクタと似たような働きをする Finalize 演算子があります。

13
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?