★前方宣言★


ここでは「前方宣言」について説明します。

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);
};

こちらもエラーは出ません。

「前方宣言」は、

 後でちゃんとクラスの宣言が出てくるので、今は見逃して・・・

と言うようなものです。


次へ

戻る

目次へ