5

前方宣言に関連していると思われる問題がありますが、おそらくそうではありません。

関連するコードは次のとおりです。

ああ

#ifndef A_H_
#define A_H_

#include "B.h"

class A
{
    private:
        B b;

    public:
        A() : b(*this) {}
    
        void bar() {}
};

#endif /*A_H_*/

Bh

#ifndef B_H_
#define B_H_

#include "A.h"

class A;

class B
{
    private:
        A& a;

    public:
        B(A& a) : a(a) {}
    
        void foo() { /*a.bar();*/ } //doesn't compile
};

#endif /*B_H_*/

main.cpp

#include "A.h"

int main()
{
    A a;

    return 0;
}

問題は、A::bar() の呼び出しにあるようです。このメソッドを呼び出そうとすると、次の 2 つのエラーが発生するまで、プログラムは正常にコンパイルされます。

エラー: 不完全なタイプ '構造体 A' の無効な使用</p>

エラー: 'struct A' の前方宣言</p>

これは、両方のヘッダーが相互に参照しているため、 A::bar() がまだ定義または宣言されていないためだと思います。しかし、私はクラスAを宣言し、他に何をする必要があるか途方に暮れています。私はC++が初めてなので、ご容赦ください。この質問に対する答えは、オンラインのどこにも見つかりませんでした。いつものように、事前に感謝します!

4

6 に答える 6

11

循環参照があるので、Bhを分離する必要があります。次のようなものを試してください。

Bh:

#ifndef B_H_
#define B_H_

// don't include A.h here!

class A;

class B
{
   private:
      A& a;

   public:
      B(A& a) : a(a) {}

      void foo();
};

#endif /*B_H_*/

B.cpp:

#include "B.h"
#include "A.h"

void B::foo() { a.bar(); } // now you're ok

編集:2つのファイルに分割する必要がある理由の説明:

クラスBには、への参照が含まれていますA。これは、いわゆる不完全な型である可能性があります。コンパイラは一体何であるかをまだ知らないため、関数を呼び出すことはできません。コンパイラは、Aある種のクラスであることを知っているだけです。(.cppファイルに)Ahを含めると、それAは完全な型になり、好きなように操作できます。

循環参照を取得するため、すべてを1つのヘッダーファイルに保持することはできません。インクルードガードで無限ループを防いでいますが、不要なものを取得しています。以前と同じように、main.cppをコンパイルしたときにコンパイラが最終的にどうなるかを見てください。

// #include "A.h" ==>
#define A_H_

// #include "B.h" ==>
#define B_H_

// #include "A.h" ==> nothing happens! (since A_H_ is already defined)

class A;

class B {
private:
    A& a;

public:
    B(A& a) : a(a) {}

    void foo() { a.bar(); } // <-- what the heck is A here?
                            //     it's not defined until below
};

class A {
private:
   B b;

public:
   A() : b(*this) {}

   void bar() {}
};

int main() {
    A a;
    return 0;
}
于 2009-01-20T06:50:09.430 に答える
1

本当にB::fooをインラインにしたい場合は、Bhで実装できますが、お勧めしません。

Bh:

#ifndef B_H_
#define B_H_

// Forward declaration of A.
class A;

class B
{
private:
    A& a;

public:
    B(A& a) : a(a) {}

    void foo();
};

// Include definition of A after definition of B.
#include "A.h"

inline void B::foo()
{
    a.bar();
}

#endif /*B_H_*/

ああ:

// Include definition of B before definition of A.
// This must be done before the ifndef A_H_ include sentry,
// otherwise A.h cannot be included without including A.h first.
#include "B.h"

#ifndef A_H_
#define A_H_

class A
{
private:
    B b;

public:
    A() : b(*this) {}

    void bar() {}
};

#endif /*A_H_*/
于 2009-01-20T10:54:55.070 に答える
1

この行#include<file.h>は、行をの内容に置き換えるだけですfile.h。したがって、コンピュータがをコンパイルしようとするとmain.cpp、次のようにすべてがまとめられます。A :: bar()を使用したい場所では、定義されていません。

// result from #include "A.h"

#ifndef A_H_
#define A_H_

// Now, #include "B.h" in A.h will get you the following
#ifndef B_H_
#define B_H_

// here you include "A.h" again, but now it has no effect
// since A_H_ is already defined

class A;

class B
{
    private:
            A& a;
    public:
            B(A& a) : a(a) {}
            // Oops, you want to use a.bar() but it is not defined yet
            void foo() { /*a.bar();*/ } 
};

#endif /*B_H_*/

class A
{
    private:
            B b;
    public:
            A() : b(*this) {}
            void bar() {}
};
#endif /*A_H_*/

// now this is your main function
int main()
{
    A a;
    return 0;
}
于 2009-01-20T07:16:17.647 に答える
1

他のいくつかが言及しているように、循環参照はあなたの問題のようです。これに対する別の言い回しは「相互依存」です。ただし、アプリケーションをコンパイルして実行するための適切な構文を見つけようとするのではなく(実際の問​​題は、投稿したものよりも少し高度なプログラムに存在すると思います)、オブジェクトから問題を攻撃することをお勧めします-指向設計の観点。

原則として、相互依存は可能な限り回避する必要があります。私は以前に自分のコードでこの問題に遭遇しました(その結果、数日間のデバッグ、髪の毛の剥ぎ取り、コンパイラの罵倒が発生しました)、これが最終的にそれを克服することができた方法です。問題に対処する方法の具体例として、私自身の問題の骨抜きバージョンを紹介します。そのため、すべての背後にある隠された意味を抽出できれば、最終的にはすべて意味があります。

DataとDataAnalyzerの2つのクラスがあるとしましょう

データはDataAnalyzer(データの分析に使用)への参照を保持し、DataAnalyzerはデータ(分析対象のデータ)への参照を保持します-相互依存!この依存関係を排除するために、DataAnalyzerで必要なパブリックメソッド/属性を定義するインターフェイス(C ++では純粋な仮想クラス)をDataAnalyzerから抽出します。次のようになります。

class IAnalyzer
{
public:
    virtual void Analyze () = 0;
};

DataAnalyzerを定義するときは、次のようにします。

class DataAnalyzer : public IAnalyzer
{
public:
    DataAnalyzer (Data* data);

    virtual void Analyze (); // defined in DataAnalyzer.cpp
};

そして、データは次のようになります。

class Data
{
public:
    Data ();

    IAnalyzer* Analyzer;
};

コントローラクラスのどこかに、次のようなものがあります。

void main ()
{
    Data*  data = new Data ();
    data->Analyzer = new DataAnalyzer (data);
}

現在、データは独立しており(IAnalyzerはデータへの参照を必要としません)、DataAnalyzerのみがデータに依存しています。続行したい場合は、DataAnalyzerのデータへの依存関係を削除し続けることができますが、単に相互の依存関係を壊すためには、これで十分です。

警告:私はこのコードをコンパイルテストしていないので、正しくコンパイルして実行するには、若干の調整が必要になる場合があります。

幸運を!

于 2009-01-20T07:34:47.047 に答える
0

Bhでは、Ahを含めるだけでなく、Aを前方宣言します。

BhをBhとB.cppに分離するか、前方宣言を削除する必要があります。

PS循環依存もあります。AhはBhを含み、その逆も同様です。しかし、あなたの警備員は問題を捕らえています;)

于 2009-01-20T06:54:38.173 に答える
0

他の答え(正解である循環参照)に追加するには、C#/ Javaを使用している場合、ファイルが(全体としてではなく)順番に解析されるという点で、C++が異なることを理解してください。したがって、使用する前に、ファイルが含まれる実際の順序ですべてが定義されていることを確認するように注意する必要があります(および/または必要に応じて機能を.cppファイルに分割します)。

于 2009-01-20T06:55:40.060 に答える