l VisualC 条件コンパイル・他 '97.12.22
l.お知らせ 内容 LINK FILE   HTML Win PC Unix MS-DOS C C++ Mfc Java 
目次.C++言語_ 1 クラス 2 派生クラス 5 演算子のオーバーロード クラス(1)
 準備 3 オブジェクト 6 テンプレート 8 etc クラス(2)
 CC++ 4 フレンド・多重継承 7 メモリ確保・例外処理 未使用
条件コンパイル   条件式   条件コンパイルを使う必要がある場合   条件コンパイルのしくみ   ヘッダファイル  

T 条件コンパイル
ソースファイルの一部分を、コンパイルしないようにします。

次の例は、識別子を書き換えだけで、コンパイルする部分を変えます。

#define A を、#define B や #define C に書換えてコンパイルすると、確められます。
#include <iostream.h>
#define A // 識別子の宣言 (この A を書き換えて、コンパイルの仕方を変えます。)
// #define は別名やマクロを定義するのに使いますが、条件コンパイルのための識別子も定義できます。
// #define による別名やマクロの定義も、条件コンパイルの識別子に使うことができます。


void main( )
{ /* ここはそのままコンパイルされます。 */
#if defined (A) // 条件コンパイルの始まり
cout << "識別子が A のときにコンパイルされます。";
#endif // 条件コンパイルの終り
#if defined (B)
cout << "識別子が B のときにコンパイルされます。";
#endif // 条件コンパイルの終り
#if !defined (A)
cout << "識別子が A でないときにコンパイルされます。";
#endif // 条件コンパイルの終り
/* ここはそのままコンパイルされます。 */
}
条件コンパイルの範囲は #if で始まって #endif で終ります。
defined は、識別子が定義されているかどうかを判定します。
defined の後の条件識別子は、( )で囲む必要はありません。
!defined は、識別子が定義されていないことを判定します。
#if 〜 #endif の間には、次の例のように、#elif を複数書くことができます。

T 条件式を使うこともできます。
#include <iostream.h>
#define A 5 // A は、5 の別名 (A は識別子として使います。)
#if A < 3 // A < 3 なら、
#define X 2 // X の別名に 2 を定義します。
#endif // 条件コンパイルの終り

void main( )
{ /* ここはそのままコンパイルされます。 */
#if A < 5 // 条件コンパイルの始まり
cout << " A < 5 のときにコンパイルされます。";
#elif A == 5
cout << " A ==5 のときにコンパイルされます。";
#elif !(A==5)
cout << " A = 5 以外のときにコンパイルされます。";
#endif // 条件コンパイルの終り
}
/* ここはそのままコンパイルされます。 */
#elif は、#if や 前の #elif の条件に一致しない場合に、
更に条件判断を行います。
#endif の直前には #else を使うことができます。
#if #endif はネストできます。
(#if〜#endif の間に、更に #if〜#endif を書くことができます。)


T 条件コンパイルを使う必要がある場合
普通は、ひとつの実行プログラムを作るためにも複数のソースファイルを作ります。
#include で、複数段のインクルードを行うと、 結果的に一部の内容が重複してしまうことがあります。

次の例は、A.cpp のクラス CA が、D.cpp に重複して挿入されないように、 AAA という識別子を設けています。
テストするには、四つのファイルを作ります。

#ifndef は、#if !defined と同じです。
#ifdef は、#if defined と同じです。
/* A.cpp */
#ifndef AAA // もし識別子 AAA がなければ
#define AAA //   識別子 AAA を定義する
class CA { public: int a; }; // クラスを宣言する
#endif // 条件コンパイルの終り
A.cpp は、複数回挿入されても、1回しかコンパイルされません。
/* B1.cpp */
#include "A.cpp"
class CB { /* クラス CB の内容 */ };
 
/* B2.cpp */
#include "A.cpp"
class CC { /* クラス CC の内容 */ };
/* D.cpp */
#include <iostream.h>
#include "B1.cpp" /* A.cpp が挿入され、AAA が定義される。 */
#include "B2.cpp" /* AAA が定義済なので、A.cpp はコンパイルされない。 */

class CD :public CA
{ public: int d; };

void main( )
{ CD OB;
OB.a=1; OB.d=5; cout << OB.a << OB.d << endl;
int q; cin >> q;
}
A.cpp の内容はそそのまま、B1.cpp と B2.cpp に挿入されます。

D.cpp には、A.cpp がふたつ挿入されますが、 条件コンパイルが指定されているので、ひとつだけしかコンパイルされません

A.cpp の条件コンパイルの指定を削除してビルドすると、エラーメッセージが表示されます。

T 条件コンパイルの原理
ソースファイルは、コンパイルされる前にプリプロセッサによって書き直されます。
コンパイラの前処理をするので、プリ(pre : 前置)という名前が付けられています。

#include A.cpp のように書かれていると、プリプロセッサは、 その部分に実際に A.cpp をコピーします。

#define AAA のように書かれていると、プリプロセッサは AAA を記憶します。
次に #ifdef AAA のように書かれていると、
 AAA が既に記憶されている場合は、#endif までをコピーします。
  ( もしコピーの途中でまた #define が現われたら、その処理をします。)
 AAA が記憶されていない場合は、#endif まで、コピーを中止します。
  ( コピーしない部分に #define があるときは、その分の #endif は無視します。)

必要がなくなった #include や #ifdef は取り除かれます。
コンパイラは、プリプロセッサが前処理してくれたファイルをコンパイルします。



T ヘッダファイル ( インクルードファイル )
説明の都合上ここでは、ヘッダファイルは ソースファイルと呼ばないことにします。

ヘッダファイル ( .h ) はコンパイルされません
コンパイルされるのは、ソースファイル ( .c .cpp ) だけです。
コンパイルされる前に、ヘッダファイルは ソースファイルに挿入されます。


C言語では、次のような目的でヘッダファイルが使われます。
ソースファイルが大きくなりすぎた場合に、分割するために使います。
有用な関数を作ったような場合に、別なプログラムを作るときに使えるように、 ヘッダファイルに保存しておきます。
基本的な関数が、標準ライブラリと呼ばれるヘッダファイル群に用意されています。


C++ の場合
C++ では クラスを使います。
クラスは オブジェクトの設計図で、オブジェクトが実際のプログラムに変換されます。

クラスAで クラスBのオブジェクトを宣言する場合は、
クラスAで クラスB を使えるようにしておく必要があります。
これを簡単に行うために、クラスの宣言だけを記述したヘッダファイルを、 クラス毎に設けます。

ソースファイルでは、必要なヘッダファイルをインクルードして使います。
宣言の内容がわかれば、変数などのサイズは分りますから、 コンパイラは、その分を空白にして、とりあえずコンパイルができます。
空白部分は、その定義部分まで読み進んだときに埋めることができます。

ソースファイルは他のファイルにインクルードしないので、 ファイルが二重にインクルードされる心配はありません
(例) アプリケーションクラスは、主ウィンドウ、ビュー、ドキュメント などのクラスと、互いにインクルードしあっています。

もしヘッダファイルを設けない場合には、インクルードには注意が必要です。
クラスAと クラスBで、互いに相手を使いたいような場合に インクルードの 記述をすると、無限にインクルードが繰返されることになります。
それを回避するには条件コンパイルを指定する必要があります。
ただし、互いにメンバオブジェクトを作るようなことはできません。
メンバオブジェクトの中にメンバオブジェクトが、無限に作られてしまいます。



ただし、次のような例外もあります。( クラス毎のヘッダファイルがない場合。)

MFCにあらかじめ用意されているクラスは、複数のクラスの宣言が 種類毎に ひとつのヘッダファイルにまとめられています。

包含関係が限定されている小さなソースコードのクラスでは、 ヘッダファイルとソースファイルに分けないで、 ひとつのファイルにまとめまた方が、全体としては単純化されます。
(例) バージョン情報ダイアログ用のクラスは、宣言部分も定義部分も、 アプリケーションクラスのソースファイルの中に いっしょに書かれています。

メンバオブジェクトを宣言する場合
他のクラスのオブジェクトをメンバにする場合は、 ソースファイルではなくヘッダファイルに、 目的のクラスのヘッダファイルをインクルードする必要があります。
このように 他のファイルをインクルードしたヘッダファイルは、 上記のように 自由に他のファイルにインクルードすることはできません。
インクルードしてあるファイルが二重にインクルードされる可能性があるからです。
例 A.h → A.cpp にインクルードする。
  B.h → B.cpp にインクルードする。
  A.h → B.h にインクルードする。( この場合は要注意です。)
  B.h → A.cpp にインクルードする。( A.cpp に、B.h の中の A.h がインクルードされます。)
  A.cpp には、A.h が二個インクルードされます。

ヘッダファイル B.h に他のファイル A.h をインクルードする場合に、 A.h が二重にインクルードされないようにするには、 A.h に あらかじめ条件コンパイルの指定を記述しておきます。
ただし、互いにメンバオブジェクトを作るようなことはできません。
メンバオブジェクトの中にメンバオブジェクトが、無限に作られてしまいます。



ヘッダファイルは、プロジェクトに追加する必要はありません
ヘッダファイルは、プロジェクトに追加するのではなく、ソースファイルに挿入します。
ソースファイルに書かれた #include MainFrm.h という文字が、MainFrm.h の内容に書換えられます。

AppWizard が作ったヘッダファイルは複数のソースファイルにインクルードされて使われます。
ヘッダファイルを自分で作った場合は、#include と書いた所にだけ挿入されます。
従って、大きな関数を作ってソースファイルが読みにくい場合には、
関数定義を書いたヘッダファイルを作り、
ソースファイルには 関数の定義を書く代わりに #include を書くことができます。

ヘッダファイルは、任意の位置に挿入できます
#include は、広い意味では文字列の置換記号です。
MFCを使ったプログラムでは、主に他のクラスを使いやすくする目的で使われます。
そのため、クラスの宣言部分がヘッダファイルに書かれます。
そのため、ヘッダファイルは ソースファイルの先頭部分に挿入されます。
しかし、ヘッダファイルの挿入位置は ソースファイルの先頭に限られているわけではありません。


T

C++言語 7 メモリ確保・例外処理 8 etc  クラス(1)
 
  mtoga@sannet.ne.jp   登録日 '96. 6.15
URL : http://www.page.sannet.ne.jp/mtoga/index.html