2015年12月18日金曜日

「高度なレコード型」は要らない子

Lazarus や Delphi には、「Object 型(Object Types)」というものがあります。(参照: http://docwiki.embarcadero.com/RADStudio/Seattle/en/Classes_and_Objects#Object_Types )

とても便利な機能なのですが、あまりにも普通で抽象的すぎる名前を付けてしまったことを後悔しているのか、Delphi では非推奨の機能とされています。

しかし、何度でもいいますが Object 型は便利です。そのことに中の人も気付いたのでしょうか、Delphi では同じ機能が「高度なレコード型(Advanced Record)」として追加されました。(参照:http://docwiki.embarcadero.com/RADStudio/Seattle/en/Structured_Types#Records_.28advanced.29

FPC でも、ソースの先頭付近に、


{$modeswitch ADVANCEDRECORDS}
 

を追加すれば「高度なレコード型」を使用できます。
FPC では、「Object 型」は非推奨の機能ではありませんので、「高度なレコード型」の方がオプション扱いなのでしょう。
個人的には、「高度なレコード型」よりも「Object 型」を使ったほうがよい気がします。なぜなら、「高度なレコード型」は「承継(inherited)」がサポートされていないなど、どちらかというと「Object 型」の劣化版といえそうだからです。

「高度なレコード型」や「Object 型」の便利なところは、「Class型(Class Types)」とは異なり、宣言するだけで実体が作成され、スコープから抜けると実体が破棄されるところです。


type
  TMyStruct = object
  public
    item: integer;
  end;

var
   my: TMyStruct;
begin
  my.item := 100;
  ShowMessage(IntToStr(my.item));
end;

上で紹介済みの http://docwiki.embarcadero.com/RADStudio/Seattle/en/Classes_and_Objects#Object_Types では、「Object 型」は New と Dispose で生成と破棄ができると書いてあります。生成と破棄のタイミングを管理したいこともたしかに多いですが、それなら「Class型」を使えば十分ですから、「Object 型」で New と Disposeを使用するメリットはないでしょう。

ところで、「高度なレコード型」や「Object 型」で少々混乱しやすいのは、実体の作成とコンストラクタの関係です。

type
  TMyClass = class
  public
    item: integer;
    constructor Create(aItem: integer);
  end;

  TMyStruct = object
  public
    item: integer;
    constructor Create(aItem: integer);
  end; 

  :

var
  myclass: TMyClass;
  myobject: TMyStruct;
begin
  myclass:=TMyClass.Create(100);
  mystruct:=TMyStruct.Create(100);
  :
  myclass.Free;
end;

「Class型」の場合は、コンストラクタにより実体が作成されるのでイメージをつかみやすいですが、「Object 型」は var で宣言するだけで実体が作成されるのでコンストラクタという概念がいまいちピンときません。
個人的には、「Object 型」におけるコンストラクタは単なる「クラスメソッド(Class Method)」に過ぎないので、コンストラクタは使わず「クラスメソッド」を使えば十分だと考えています。

type
  TMyStruct = object  public
    item: integer;
    class procedure Create(aItem: integer);
  end; 
  :

var
  myobject: TMyStruct;
begin
  mystruct:=TMyStruct.Create(100);   :
end;

おそらく、「Object 型」におけるコンストラクタやデストラクタは、既述の New や Dispose で真価を発揮するものだと思います。そして既述のように、New や Dispose を使うくらいなら「Class型」を使った方がよいですから、「Object 型」ではコンストラクタやデストラクタの出番はないということになります。

・・・そうとすると、 「高度なレコード型」でもコンストラクタが使えるみたいなのですが、おそらくその出番はないでしょう。

2015年12月11日金曜日

TRect のお話

Lazarus や Delphi では矩形領域を現す TRect という便利な型とそれを操作するための便利な関数群が用意されています。
TRect は、上下左右の座標で定義されますが、標準では、下と右は+1してあげる必要があります。例えば、画面上の左上隅の1ドットを TRect で現すと Left = 0, Top = 0, Right = 1, Bottom = 1 となります。 もちろん、自己のプログラムの中で1ドットの矩形は Left = Right, Top = Bottom とするんだ!とすることは自由なわけですが、少なくとも Lazarus や Delphi の標準ルーチンはそのように処理しません。例えば 、矩形が空かどうかを調べる IsRectEmpty 関数は次のようになっています。

function IsRectEmpty(const Rect : TRect) : Boolean;
begin
  IsRectEmpty:=(Rect.Right<=Rect.Left) or (Rect.Bottom<=Rect.Top);
end;

左右あるいは上下の座標が同じなら空と判定してますね。