インクルードの交差を抑止する方法

出典: YKAwiki

目次

概要

互いにインクルードしあう形になると、クラスが宣言されていない状態になるためエラーになります。 クラスの仮宣言を使うなどして回避しましょう。

問題の例

  • Parent
  • Child

というクラスを作りたいとします。また、

という条件があるとします。 (たとえば、ChildはParentに従属しており、Parentが親、Childが子という状況を考えます。Childは親へのポインタが欲しいし、Parentは子供を持っていたいわけです。)

コードで書くと

[Parent.h]
#ifndef PARENT
#define PARENT

#include "Child.h"
class Parent{
    Child piyo;
};
#endif
[Child.h]
#ifndef CHILD
#define CHILD

#include "Parent.h"
class Child{
    void func();
    Parent hoge;
};
#endif

となります。#ifndef とかは多重宣言を避けるおまじないです

このとき、

  • Parent.hが先に呼ばれ場合

Child.hが呼ばれ、さらにParent.hを呼びますが、すでにPARENTが宣言されるのでスキップされます。するとまだクラスParentが宣言されてないのにメンバ変数Parent hoge;を宣言しようとします。

  • Child.hが先に呼ばれた場合

上のことが逆になるだけです。

従属関係を整理する方法

Childが親であるParentをメンバクラスに持たせる必要が低ければ、 ヘッダーからはずす事で回避できます。 CPPファイルからならいくら呼んでもかまわないので、 そっちに移すのもよいです。

[Child.h改]
#ifndef CHILD
#define CHILD

class Child{
    void func();
};
#endif
[Child.cpp]
#include "Parent.h"
#include "Child.h"
void Child::func(){
    Parent hoge;
}


クラスの仮宣言を使う方法

上の方式が気に入らない場合は、仮宣言で解決できる場合が多いです。

[Parent.h改]
#ifndef PARENT
#define PARENT

#include "Child.h"
class Child;
class Parent{
    Child piyo;
};
#endif
[Child.h改]
#ifndef CHILD
#define CHILD

class Parent;
class Child{
    void func();
    Parent hoge;
};
#endif

注意点として、仮宣言はあくまで「仮」なので、そのままではメンバにアクセスできません。

グローバル変数に任せる方法

[Parent.h改]
#ifndef PARENT
#define PARENT

#include "Child.h"
class Parent{
    Child piyo;
};
extern Parent *globalParent;
#endif

[Parent.cpp]
#include "Parent.h"
globalParent = new Parent();
[Child.h改]
#ifndef CHILD
#define CHILD

class Child{
    void func();
};
#endif
[Child.cpp]
#include "Parent.h"
#include "Child.h"
void Child::func(){
    //globalParentを使って色々
}

グローバル変数を使い出すと構成が途端に複雑になるので、シングルトン以外は使わない方がよいです