[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

5.4 一個のヘッダファイルに宣言と定義を入れる

C++ のオブジェクトの定義は極めて複雑になることがある。 原則として、ソースコードには、複数のソースファイルにまたがって 使用するオブジェクト毎に二種類のものが必要となる。 第一に、インターフェースの指定が必要になる。 これは、オブジェクトの構造を型宣言と関数プロトタイプで記述する。 第二に、実装自体が必要になる。 ヘッダファイルにインターフェース定義を分けて、実際の実装と並行して 保守するのはうんざりする話である。 また、危険でもある。インターフェースの定義と実装の定義を分けておくと、 一貫性が保たれない可能性があるからである。

GNU C++ では、一個のヘッダファイルで両方の目的に使うことができる。

注意。この機能を指定する方法は移行中である。 当面は、二つの #pragma 文のうちの一つを使わなければならない。 将来的には、別の方法を使ようになり、#pragma 文は必要なくなるだろう。

ヘッダファイルには完全な定義を入れておくが、ソースコードから インクルードするときは ‘#pragma interface’ という目印を付ける。 これにより、通常のソースファイルがそのヘッダファイルを #include で取り込んだときに、そのヘッダファイルをインターフェース 仕様としてだけ使うことをコンパイラに許す。 完全な実装を含む一個のソースファイルでは名前付の規約か ‘#pragma implementation’ を使って、ヘッダファイルのこの別の 使い方を指示することができる。

#pragma interface
#pragma interface "subdir/objects.h"

オブジェクトクラスを定義している ヘッダファイルでこの文を使って、 そのクラスを使っているオブジェクトファイルの大部分でスペースを節約する。 通常、一定の情報のローカルのコピー(インラインメンバ関数、 デバッグ情報、仮想関数を実装する内部テーブルのバックアップコピー) は、クラス定義を含む各オブジェクトファイルで保持しなければならない。 この pragma を使うと、そのような重複を避けることができる。 ‘#pragma inreface’ を含むヘッダファイルが、あるコンパイル単に インクルードされていると、この補助的な情報は生成されない (主となる入力ソースファイル自体が ‘#pragma implementation’ を 使っていない限り)。 代わりに、オブジェクトファイルに、リンク時に解決される参照が入る。

この制御子の二番目の形式は、同じ名前のヘッダファイルが色々なディレクトリに 複数あるときに役に立つ。この形式を使う場合は、‘#pragma implementation’ に同じ文字列を指定しなければならない。

#pragma implementation
#pragma implementation "objects.h"

インクルードされたヘッダファイルから完全な出力を得たい(そして グローバルに可視にしたい)時は、このプラグマを 主となる入力ファイル で使う。インクルードされるヘッダファイルは、‘#prama interface’ を 使うべきである。インライン展開されるメンバ関数、デバッグ情報、 仮想関数を実装するのに使われる内部的な表のバックアップコピーが 全て実装ファイルに生成される。

#pragma implementation’ を引数なしで使うと、 ソースファイルとしてベース名(2)が同じインクルードファイルに 適用される。例えば、他に ‘#pragma implementation’ と書くのはこれだけで、‘#pragma implementation "allclass.h"’ に 等価である。

GNU C++ のバージョン 2.6.0 以前では、‘allclass.h’ は、 ‘allclass.cc’ からインクルードしていると、‘#pragma implementation’ を指定していなくても、常に実装ファイルとして 扱われた。これは利点より問題点の方が多いように思われる。

#pragma implementation’ を明示的に使う場合には、ソースファイルでは、 それにより影響を受けるヘッダファイルをインクルードする前に、 現れなければならない。

一個の実装ファイルに複数のヘッダファイルからコードをインクルードさせたい 場合は文字列引数を使う。(また、ヘッダファイルをインクルードする ‘#include’ も使わなければならない。‘#pragma implementation’ は指定したファイルの使い方を指定するだけであって、 実際のインクルードは行なわないからである。)

一個のヘッダファイルの内容を複数の実装ファイルに分ける方法はない。

#pragma implementation’ と ‘#pragma interface’ は、 関数のインライン展開にも影響する。

#pragma interface’ を記述したヘッダファイル内でクラスを定義すると、 そのクラス内で定義される関数の効果は、明示的な extern 宣言に 似ている。コンパイラは、この関数の一個の独立したバージョンを定義する コードを全く吐かない。その定義は、その関数の呼び出し側でインライン 展開するのに使われるだけである。

逆に、同じヘッダファイルを ‘#pragma implementation’ を宣言している 主となるソースファイルでインクルードすると、コンパイラは関数自身のコードを 吐く。これは、ポインタ経由で(あるいはインライン展開なしでコンパイル された呼び出し側から)見つけることのできるバージョンの関数を定義する。 その関数への呼び出しが全部インライン展開可能なら、 ‘-fno-implement-inlines’ 付きでコンパイルすることで、 関数のコードを吐くのを避けることができる。 呼び出しがどれか一つでも印ライン展開されてないと、リンク時のエラーとなる。


This document was generated using texi2html 1.78.