ユニット
ユニットとはVBにおける標準モジュールのようなものです。 ただ、標準モジュールとは異なり、Object Pascalではインターフェイス部と呼ばれる外部に公開するプロシージャ・関数・定数・型などを記述する部分があります。 これはC/C++でいうプロトタイプ宣言によく似たものです。 また、ユニットはメインプログラムのあるファイルとは別のファイルに記述されます。 簡単なユニットとそれを用いたサンプルを次に示します。
まず、ユニットを利用する側ではuses文で使用するユニットとそれが記述されているファイルを指定します。 それ以外に特別な記述は必要ありません。 後はユニットにあるプロシージャや型などを利用するだけです。 そして、それらの要素を実際に記述するのがユニットの方です。 ユニットは次のような構造になっています。
まず、ユニットヘッダではユニットの名前を指定します。 Delphiではここで指定した名前とファイル名が一致している必要があります。 ユニット本体の記述では、インターフェイス部と実現部の二つの部分を記述します。 実現部は実装部と言い換えてもいいかもしれません。 インターフェイス部でユニット外に公開するプロシージャ・関数の宣言部だけを記述し、実現部でその実装を行います。
また、実装部にはinitialization文とfinalization文をオプションで記述することができます。 ユニットをインスタンスかできないクラスと考えれば、この部分はコンストラクタ・デストラクタに相当するといえます。 この例ではusesをインターフェイス部で使用しましたが、ユニット同士の循環参照が起こる場合にはいずれかのusesを実現部に移すことで解決できます。 また、usesはできるだけ実現部で使用するべきとされています。
実行結果を検証してみると、メインプログラムから呼び出されたユニットのプロシージャ・関数は適切に動作しています。 また、すべてのプロシージャ・関数などが使用される前にユニットの初期化部が動作しています。 この例の終了処理部にWritelnなどを記述しても、アプリケーション終了直前に呼び出されるため、実際には表示された結果を見ることはできません。
クラス 基本編
Object Pascalにおけるクラスの概念もC/C++のクラスと大域的には同じといえます。 また、記述方法もC/C++と非常によく似ています。
まず、type文で「識別子 = class」としてクラス型を宣言します。 さらに、そのクラスのフィールド・インターフェイスを定義します。 各メンバはスコープを持つことができ、基本的にはC/C++と同じ「private, proteced, public」の三つです。 これ以外にもいくつか特殊なものがありますがここでは省略します。 プロシージャ・関数・フィールドのインターフェイスは今までどおりです。 また、Object Pascalのクラスはプロパティを持つことができます。 プロパティとなるフィールドはpropertyで宣言し、別途プロパティプロシージャを用意してある必要があります。
プロパティプロシージャの名前は任意ですが、書き込み用プロシージャは値をひとつだけ取り戻り値なし、読み取り用関数は引数なしで戻り値ありのものを用意します。 これらのプロパティプロシージャはプロパティ宣言部より前で宣言されている必要があります。 また、プロパティは読み取り専用にも書き込み専用にもすることができ、そうする場合はreadないしwriteを省略します。 この例ではプロパティプロシージャはprivateスコープですが、publicにすることで普通のプロシージャ同様に呼び出すことも可能です。 また、コンストラクタおよびデストラクタを実装することもできます。 その場合は「constructor 識別子(引数リスト);」ないし「destructor 識別子;」などとします。
インターフェイスの宣言が完了したら、実現部でその実装を行います。 これはC/C++のクラスにおけるメンバ関数の外部定義と同様の方法で行います。 つまり、「procedure クラス名.プロシージャ名」、「function クラス名.関数名」のように、関数・プロシージャの名前の前にクラスの名前を指定します。 また、これらのプロシージャの中はクラスの中と同様なので、クラスのメンバにクラス名を付加する必要はありません。 こうしてできたクラスは次のようにして使用します。
まず、クラス型変数の宣言は普通の変数宣言と変わりありません。 インスタンスを生成するには「クラス型.コンストラクタ(引数)」とします。 このときコンストラクタに渡すべきパラメータがないときは省略できますし、クラスにコンストラクタ宣言がないときは暗黙の基底クラスであるTObjectから継承したCreateメソッドを利用することができます。 また、デストラクタも同様で、特にクラスで宣言されていない場合はFreeまたはDestroyメソッドを使用することができます。 その他メソッドの呼び出しやフィールド・プロパティへのアクセスはC/C++やVBと変わりありません。 このコードを実行すると次のようになります。
参考までに、これと同様の動作をするクラスとそれを利用するコードを、VB.NETで組んだものを例示しておきます。 見た目もできる限り似せています。
クラス 継承編
Object Pascalのクラスでは、当然継承もサポートされます。
あるクラスを基底クラスとしてクラスを派生するにはtype文で「派生クラス = class(基底クラス)」とします。 Delphiにおける継承は、.NET FrameworkのCLSにおける継承と同じく、クラスの多重継承は認められていないものの、インターフェイスは複数実装することができます。 この例ではTMusumeを継承してTDerivedという派生クラスを作成しています。 まず、Introduceメソッドをオーバーライドするために、 TDerivedでのIntroduceメソッドの宣言の最後にoverrideを付加します。 当時に、オーバーライドされる基底クラスの Introduceメソッドにもvirtualを付加します。 後は各メンバの実装を行うだけです。
ちなみに、実行結果を見ると、TDerived型のインスタンスをTMusume型に代入しても、当然TDerived型の Introduceメソッドが呼び出されます。 しかし、派生クラスでのメソッド宣言でoverrideを取り去ると、派生クラスのメソッドは基底クラスのメソッドをシャドウしたかのような動作になります。
ただし、このままだと「'Introduce'メソッドが基本型'TMusume'の仮想メソッドを隠しました」という警告が発生します。 これを解決するにはさっき消し去ったoverrideの変わりにreintroduceを付け替えます。
クラス TObject編
Object Pascalのクラスは、暗黙的にTObjectを継承します。
ここで呼び出したメソッドは全てTObjectから継承したメソッドです。 このほかにもいくつかメソッドが存在しますが、その多くは特別な場合を除いてあまり使用しないでしょう。
Windows アプリケーション
Delphiが賞賛される要因、そしてVisual Basicと比較される要因の一つが、RADにあると思います。 コンソールアプリで遊ぶのをそろそろ切り上げて、簡単なWindowsアプリケーションを作ってみます。 ところで一般的にはよくRAD環境と言いますけど、RADとはRapid Application Developmentの略なので「RAD環境」はRapid Application Development Developmentと冗長な表現になっています。 SerializableAttribute属性とか言っているのと同じ気がするのですがいいのでしょうか。 (確かに日本語の語感からするとRAD環境の方がしっくりくるのですが・・・)
よけいな話しになりましたが、まずはじめは生成されるテンプレートについて見てみることにします。
生成されたテンプレート
生成されたフォーム
メインプログラムの方は特に問題ありません。 アプリケーションを初期化して、フォームを作成して、実行してメッセージループに入るだけです。 ただ、 MainFormユニットの方はフォームの実装を記述する部分ですが、VBとかなり異なります。 まず注目すべきは、フォームはTForm型から継承したクラス型であることです。 VBでもフォームは一種のクラスでしたが、Delphiでのフォームは継承が可能です。
それでは早速ログインダイアログのようなものを作ってみます。 このアプリケーションはエディットボックスに入力された文字をメッセージボックスを利用して表示するだけの単純なアプリケーションです。
灰色の部分はデザイン時に自動的に追加された部分と、イベントハンドラとして作成された部分です。 青いところが実際に記述したコードです。 これを実行するとこんな感じになります。