ここでは「前方宣言」について説明します。
C言語やC++にはヘッダファイルが必要ですが、ヘッダファイル絡みのエラーは結構面倒です。
1つ例を書いてみます。
プロジェクトを作成し、↓の4つのファイルを準備してください。
Player.h Player.cpp Enemy.h Enemy.cpp
最小限のプログラムで説明しますので、main関数も作りません。
まずは、Enemy.cppから作りましょう。
<sample program cpp079-01>
/* Enemy.cpp */
#include "Enemy.h" |
単にヘッダファイルをインクルードするだけです。
続いて、Enemy.hを書きます。
<sample program cpp079-02>
/* Enemy.h */
#pragma once class Enemy { public: }; |
Enemyクラスを宣言するだけで特にメンバは不要です。
次に、Player.cppを書きます。
<sample program cpp079-03>
/* Player.cpp */
#include "Player.h" |
こちらもヘッダファイルをインクルードするだけです。
最後に、Player.hを書きます。
<sample program cpp079-04>
/* Player.h */
#pragma once #include "Enemy.h" class Player { public: void Collision(Enemy &refEnemy); }; |
こちらは、引数として「Enemyクラスの参照」を受け取るCollision関数があります。
Enemyクラスを使うため「Enemy.h」をインクルードしています。
main関数が無いため「ビルド」すると「ビルドエラー」になりますので、「Player.cpp」を開いて「コンパイル」します。
※全てのファイルを保存する事を忘れないでください。
(ctrl + shift + S で全てのファイルを保存出来ます)
きちんと書けていればエラーは出ません。
※ヘッダファイルを開いたまま「コンパイル」する事は出来ません。
では、ここから問題点を書いていきます。
Enemy.hを開いてください。
次のコードを追加します。
<sample program cpp079-05>
/* Enemy.h */
#pragma once #include "Player.h" class Enemy { public: void Collision(Player &refPlayer); }; |
EnemyクラスでもPlayerクラスを必要とするCollision関数を追加しました。
当然、Playerクラスを使うため「Player.h」をインクルードしました。
※Enemy.hを保存しておいてください。
それでは、「Player.cpp」を開いて「コンパイル」してみましょう。
<コンパイル結果>
error C2061: 構文エラー: 識別子 'Player'
「出力」タブのエラーメッセージをダブルクリックするとエラーの箇所が分かります。
エラーは「Enemy.h」の
void Collision(Player &refPlayer); |
で発生しています。
もう1つ「Enemy.cpp」を開いて「コンパイル」してみましょう。
<コンパイル結果>
error C2061: 構文エラー: 識別子 'Enemy'
こちらは別のエラーが出ました。
エラーの場所は「Player.h」の
void Collision(Enemy &refEnemy); |
です。
C言語編の「インクルードガード」でも、同じような問題点がありました。
お互いにインクルードし合う事によって発生するエラーです。
しかし、今回は「Player.h」にも「Enemy.h」にも「#pragma once」の記述があり、インクルードガードは出来ているはずです。
では、細かく見てみましょう。
Playerクラス側から検証してみます。
Player.hでは、まずEnemy.hをインクルードしています。
前にもやったように「展開」してみましょう。
<sample program cpp079-06>
/* Player.h */
#pragma once
#include "Player.h"
class Enemy {
public:
void Collision(Player &refPlayer);
};
class Player {
public:
void Collision(Enemy &refEnemy);
};
|
展開すると、再度「Player.h」をインクルードしていますが、インクルードガードがあるため2度目は無視されます。
と言う事は、↓のようになります。
<sample program cpp079-07>
/* Player.h */
#pragma once class Enemy { public: void Collision(Player &refPlayer); }; class Player { public: void Collision(Enemy &refEnemy); }; |
Playerクラスが宣言される前に、Enemyクラスのメンバ関数でPlayerクラスを使うようになっています。
そのため「Playerクラスって何?」と言う事でエラーになっているのです。
では、このエラーに対する対処法を書きます。
あるクラスで別のクラスの参照やポインタが必要になった場合、インクルードでは無く「前方宣言」を使います。
では「Player.h」を開いて、次のように書き換えましょう。
<sample program cpp079-08>
/* Player.h */
#pragma once
class Enemy;
class Player {
public:
void Collision(Enemy &refEnemy);
};
|
これが「前方宣言」です。
Player.cppを開いて「コンパイル」してもエラーは出ません。
同様に、Enemy.hを開き「前方宣言」に書き換えてみます。
<sample program cpp079-09>
/* Enemy.h */
#pragma once
class Player;
class Enemy {
public:
void Collision(Player &refPlayer);
};
|
こちらもエラーは出ません。
「前方宣言」は、
後でちゃんとクラスの宣言が出てくるので、今は見逃して・・・
と言うようなものです。