ドット絵エディタを作る(Delphiのお勉強編)

前回仕様(?)を決めましたが,まだまだDelphiに慣れてないので,今日はDelphiのお勉強だ.
ソフトウェア学ぶの大好き.
大学での専攻はプロセッサだけどね.(-_-;)
ていうか,ウチの某大学には,ソフトウェア工学の専攻がないしね.

いっけねー.また脱線しちゃった.てへ.



■クラスの扱い■

まず,Delphiのクラスって C/C++ とちょいと違うっすね.
どっちかっていうと,Javaのクラスに近いっす.
というのも,クラス型って参照型なのね.
つまり,TEST というクラスがあっても C++ みたいに,
(C++)

TEST ts ;

ts.Run() ;
とは使えない.宣言しても明示的にインスタンス化しないといけないのね.
(Delphi)

var ts : TEST ;

ts := TEST.Create ; //インスタンス化!
ts.Run ;
うーん,参照にもかからわず,ポインタを明らかに ts^.Run って書かなくてもよいところが ますますJavaちっく.
あと,何も継承していないクラスを作ったつもりでも,実はTObjectクラスを継承しているってところも, とってもJavaちっく.

というわけで,Delphiでは,「クラスは必ず明示的にインスタンス化しなくてはいけない!!」…っと.

でも,レコード型(C/C++での構造体)は,明示的にやらなくてもいいのね.ふむふむ.
「レコード型は宣言したらすぐ使える!」…っと.


あれ??
でも,コンポーネント(VCL)はクラスなのに,インスタンス化した覚えはないにゃー(゚∀゚)?

結局,フォームに貼り付けたコンポーネントは自動的に構築&破棄してくれるから,プログラマが わざわざやる必要は無い…
ていうか,かってに破棄しちゃダメ!

まとめると.
・クラス(VCL):構築&破棄の必要なし
・クラス(非VCL):プログラマに構築&破棄の責任
・レコード:構築&破棄の必要なし



■ユニットの扱い■

うーん…ユニット(Unit)…
C/C++には無いにゃー・・・
ネームスペースに似てる気もするけどにゃー
でも,ユニットは,「ファイル名と一致」だからにゃー
そうすると,Javaのパッケージとも違うしにゃー

結局,ユニット名とファイル名が一致してるから,C/C++の include のノリで使えるみたいだけどね.
使いたいクラスとかモジュールとかあったら,そのユニット名を uses に書けばよいのね.
ただ,極力 implement 部分で uses しとくと後々相互参照エラーとか起きなくてよい.



■他のフォームへのアクセス■

うーん,VC++でアプリケーション作ると,ドキュメントビューアーキテクチャとかなんとかいうのがあったよね〜.
他のフォームにアクセスするのが面倒だった気がするけど.(あんまりやったことない)
Delphiでは,どうやって他のフォームと連携(?)すんのかにゃー・・・?
・・・
・・・
(解析中)
・・・
・・・
あッ,大域変数!!( ̄ロ ̄;)

・・・なのか?
フォームの定義が書かれたユニットがあるけど,その中で,既にインスタンスが作られてる!!
しかも,interface 部分に書かれてるから,そのユニットをusesしたら,どっからでも使える!!
まぁ,便利なんだけどね.
というわけで,フォームを作成すると,「自動的に大域変数が作られて解決!」…っと.

・・・まぁ,楽チンなんだけどね.



■プロパティ?■

いかにもDelphiチックなのが,このプロパティ.
最初は,まるで意味わからんかったね.
基本的には,C++でいうところの,GET系,SET系の関数を便利にまとめたもの?

そもそも,オブジェクト指向なんてろでは,クラスのメンバ変数(Java,Delphiではフィールド) へ直接アクセスするのは推奨されてない.
だから,メンバ変数は,直接アクセスされないように,普通は,private になってる.
んじゃ,どうやって,アクセスすんねん!?ていう疑問(?)が起きるが,
その変数にアクセスするためのメソッドを用意することで解決.
例えば,メンバ変数名が,int Value って定義されてたとすると,
読み出すには,int GetValue()を,
書き込むには,void GetValue( int v )というメソッドを用意する.
そのメソッドの中(特にGet系)で,値が有効かどうかをチェックすることで,変数へのアクセスの安全性を高めている.

じゃが,いかんせんメンドイ.
読み出すだけなのにGet何々とか書くのメンドイ.

そこで,Delphiのプロパティを使うとぐぐっと楽チン♪
property Value : Integer read GetValue write SetValue ;
と定義しておけば,Valueを読み出すような場合は,自動的に,GetValueメソッドが呼び出され, Valueに代入する場合は,SetValueが呼び出される.
うーん,便利.ステキ.



■With?■

With…なんだこれ?
なんか新鮮だにゃー.
使い方は,ある構造体(あるいはクラス)FSampleがあったときに
FSample.a := 10 ;
FSample.b := 100 ;
ってやるかわりに,
with FSample do begin
  a := 10 ;
  b := 100 ;
end;
と書ける.
まぁ,このくらいだとあんまりありがたみが無いけど,仮に,
FSample.SampleOfSample.SampleMember.MemberOfMember.a := 10 ;
FSample.SampleOfSample.SampleMember.MemberOfMember.b := 100 ;
とかだったら,カナリ楽になるねぇ.
それに,楽になるだけじゃなくて,大量にアクセスする場合はパフォーマンスがあがる…らしい.
というのも,このクラスの「a」にアクセスするためには,それをもつクラスの先頭フィールドのアドレスを求めなくてはならず, さらにそのクラスのクラス,そのまたクラスのクラスのアドレス…みたいにアドレス解決しなくちゃならないからだね.
そんなときにwithを使えば,
with FSample.SampleOfSample.SampleMember.MemberOfMember do
のところで,MemberOfMemberの先頭フィールドのアドレスを求めてくれて(たぶん), あとはこのアドレス値を元にフィールドのアドレスを求めてくれるから,何度も同じ計算しなくていいから速いのねぇ.

CとかC++だと,構造体が深くなったら一旦ポインタとか参照とかに入れて計算するノリかな?
struct ST_D {
  int d[10];
};

struct ST_C {
  ST_D stD ;
};

struct ST_B {
  ST_C stC ;
};

struct ST_A {
  ST_B stB ; 
};

ST_A stA ;
STD_D * pstD ;

// これよりも
for ( i=0 ; i < 10 ; ++i )  stA.stB.stC.stD.d[i] = 10 ;

// こっちの方がちょっと速い(?)
pstD = & stA.stB.stC.stD ;
for ( i=0 ; i < 10 ; ++i ) pstD->d[i] = 10 ; 



まぁ,こんなもんで, あとは,プログラムの書き方が微妙に違うとかで,ほかの言語といっしょだね.
この辺りを抑えとけば,すんなりアプリ作りを開始できましゅ.

というわけで,次回からは,いよいよエディタ作成開始!!…かな?



<< Back to Diary...