Object Pascal のプログラムでは、モジュール分割してソースコードを分けることができます。コンパイルもユニット単位で、メインプログラムから利用宣言(uses句)することにより、リンク時にメインプログラム(EXEやDLL)に結合され一つのプログラムとなります。利用するユニットがさらに別のユニットを使用することもでき、利用されるすべてのユニットがリンクされます。
ユニットの基本的な構成について説明します。
ユニットの構成 |
|---|
Object Pascalでは、ユニットの構成を以下のように定義しています。
【ユニット】
| @ ユニットヘッダ (unit) | |
| A インターフェース部 (interface) | |
| B uses句 (インターフェース部用) | |
| C ブロック (インターフェース部用) | |
| D 実現部 (implementation) | |
| E uses句 (実現部用) | |
| F ブロック (実現部用) | |
| G ユニット初期化部/ユニット終了処理部(initialization 〜 finalization 〜 end) | |
| H ユニット終了記号 (.) | |
ユニットは、ユニットヘッダの「unit」で始まり、ユニットの終了記号「.」で終わります。
その中にインターフェース部、実現部、ユニット初期化部/ユニット終了処理部があります。
インターフェース部、実現部には、それぞれuses句とブロックがあります。順序はuses句が先です。uses句が不要な場合は省略します。
ブロックの構成はインターフェース部と実現部で違いがあります。
ユニットファイルには、この構成を一組だけ記述します。複数記述することはできません。
ユニットヘッダ |
|---|
ユニットヘッダは、unitキーワードに続けてユニット名の識別子を指定します。末尾は「;」で終了します。
【ユニットヘッダ】
unit 識別子;
ユニット名には途中に「.」を含むことができます。「Unit1」、「Unit1.Test」、「Hello.Hanako.Yamada」も有効です。「.」で始まったり「.」で終了することはできません。また「.」が連続するのもだめです。また「.」で分離されたものも識別子のルールに従います。「Unit1.2nd」では、「2nd」が数字で始まっているので識別子になりません。
ユニット名は、コンパイルで作成されてるppuファイルの名前にはなりません。ppuファイルの名前は、ユニットのソースコードのファイル名の拡張子をppuに変えたものになります。通常はソースファイル名とプログラム名は合わせます。
ソースファイル名が「Unit1.pas」で、ユニットヘッダがファイル名と同じ「unit Unit1;」の場合、コンパイルしてできるppuファイルは「Unit1.ppu」になります。この場合はメインプログラムや他のユニットから「uses Uni1;」で利用宣言できますが、もし、ソースファイル名が「Unit1.pas」で、ユニットヘッダがファイル名と違う「unit UnitX;」とした場合、コンパイルしてできるppuファイルは「Unit1.ppu」になります。この場合、「uses Unit1;」で使用宣言すると「Unit1.ppu」を参照して、その中に「Unitx」が存在しないためコンパイルエラーとなります。
識別子は何のためにあるかというと、このユニットを使用するプログラムで特定するための識別子の他、このユニット内(ソースファイル内)のネームスペースになります。「Unit1」で宣言された識別子「X」はそのままでも有効ですが、正式には「Unit1.X」となります。他のユニットの「X」と区別するために使用することができます。しかし、ユニットの識別子はユニットのスコープ内では他のユニットと競合することはないので(Unit1.が優先となるため)、明示的に示したいという理由でもなければ特に使用する機会はないと思います。Unit1を使用するプログラムやユニットでは、Unit1のネームスペースは他のユニットと区別する際に重要になります。
ユニットヘッダを省略することはできません。
インターフェース部 |
|---|
インターフェース部は、interfaceキーワードに続けてこのユニットを使用するプログラムやユニットから使用可能なブロック(インターフェース部用)を宣言します。このユニットを使用するプログラムやユニットでは、自分自身で宣言したかのように使用することができます。使用するプログラムやユニットでは識別子が競合しない限りネームスペース(ユニット名)で修飾する必要もありません。
手続きと関数については手続きヘッダ/関数ヘッダのみ指定します。手続きヘッダ/関数ヘッダは名前識別子、パラメータリスト、戻り値の型(関数の場合)などのシグネチャのみです。
実現部 |
|---|
実現部は、implementationキーワードに続けてインターフェース部で宣言した手続き/関数の実装を定義します。手続き/関数の実装は、インターフェースで宣言したヘッダに実装部分を追加した完全版です。実装部分はプログラム(program)のブロックと同じです。
実現部はこのユニットを使用するプログラムやユニットからは隠蔽されます。ユニット内プライベートです。
実現部では、インターフェース部ので宣言した手続き/関数ヘッダに実装部分を定義するだけではなく、実現部内独自のブロックの記述ができます。このユニットを使用するプログラムやユニットからは参照できないので、インターフェース部の内容をサポートするデータや処理を記述することになります。
uses句 (インターフェース部用/実現部用) |
|---|
メインプログラム同様、ユニットも他のユニットを使用することができます。
この使用宣言を行うのが「uses句」です。usesキーワードの後に使用するユニット名の識別子リストを並べて「;」で終了します。ユニット名リストは一つ以上のユニット名を「,」で区切って並べます。
【uses句】
uses
識別子1, 識別子2, ... , 識別子n;
使用するユニットがない場合は「uses句」を省略します。
通常、ユニットで使用する他のユニットはインターフェース部用のuses句ですべて指定して問題ありません。実現部でのみ使用するユニットがあればインターフェース部用のuses句で指定せず、実現部用のuses句で指定することもできます。
ブロック (インターフェース部用/実現部用) |
|---|
ユニットのブロックは、プログラム(program)のブロックの宣言部のみで構成されます。実行文部はありません。
【ブロック】
| @ ラベル宣言部 (label) A 定数宣言部] (const) B リソース文字列宣言部 (resourcestring) C 型宣言部 (type) D 変数宣言部 (var) E スレッド変数宣言部(threadvar) F プロパティ宣言部 (property) G 手続き・関数宣言部 (procedure / function) |
宣言部は、順序を変えたり、他の宣言や定義をはさんで複数回記述する事ができます。必要がないものは省略できます。
@のラベル宣言はインターフェース部では使用できません。実現部では使用できます。定義したラベルを使用できる場所はユニット初期化部/ユニット終了処理部です。
Gの手続き・関数宣言部はインターフェース部と実現部のところで説明した通り、インターフェース部では手続きヘッダ/関数ヘッダのみで、実現部では実装を含む完全版の定義となります。
【インターフェース部の手続き/関数の例】
procedure Proc(S: String);
function Func(A: Integer; B: Integer): Integer;
【実現部の手続き/関数の例】
procedure Proc(S: String);
const
Msg = 'さん、こんにちは。';
begin
WriteLn(S, Msg);
end;
function Func(A: Integer; B: Integer): Integer;
var
C : Integer;
begin
C := A + B;
Result := C;
end;
宣言と定義の意味についてはこちらを参照。
ユニット初期化部/ユニット終了処理部 |
|---|
ユニット初期化部とユニット終了処理部は、実現部の後に以下の構文で記述します。
initialization
{ ユニットの初期化処理の実行文 }
finalization
{ ユニットの終了処理の実行文 }
end
ユニット初期化部とユニット終了処理部の中では宣言ブロックは使用できません。必要な定数、型、手続き/関数などはインターフェース部か実現部で宣言します。ラベルについては実現部で宣言します。
初期化処理や終了処理の部分は、begin 〜 end で囲む必要はありません。直接記述します。begin 〜 end で囲んでもそれが複合文になるだけでエラーにはなりません。
ユニット初期化部は、メインプログラム(program)の実行文部の始まる前に実行されます。メインプログラムが始まる前に各ユニットの初期化が済んでいることが保証されるわけです。
ユニット終了処理部はメインプログラムの実行が完了した後に実行されます。動的に確保した記憶域の開放やオープンしたファイルのクローズなどの後始末を行うのに適しています。
初期化処理や終了処理が不要な場合は、不要な方の実行文を空にします。また、空にするだけではなくinitializationキーワードやfinalizationキーワードも削除することも可能です。両方削除した場合でも最後の end は削除できません。
また、初期化処理しかない場合は以下の構文で記述することも可能です。
begin
{ ユニットの初期化処理の実行文 }
end
ユニット初期化部とユニット終了処理部はプログラム(program)のブロックの実行文部にあたることがわかります。そして初期化処理(initialization)と終了処理(finalization)に分ける仕組みが加わったわけです。
そういう意味では、実現部以降がプログラム(program)のブロックと同じという見方もできます。
プログラム終了記号 |
|---|
ユニットの終了記号は「.」(ドット記号)です。
ユニットの記述終了を表します。
unitファイルのひな型(例) |
|---|
Object Pascalのunitファイルのひな型について以下の投稿を参照してください。
Free PascalのOBJFPCモードを想定したひな型の例です。
特に何もしないプログラムコードです。これにプログラムの構成ルールにしたがってプログラムコードを追加すると便利です。