调用Method时的C ++前向声明问题

时间:2009-01-20 06:40:43

标签: c++ compiler-construction declaration

我有一个问题,我认为这与前向声明有关,但可能没有。

以下是相关代码:

A.H

#ifndef A_H_
#define A_H_

#include "B.h"

class A
{
    private:
        B b;

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

        void bar() {}
};

#endif /*A_H_*/

B.h

#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()。程序成功编译,直到我尝试调用此方法,此时我得到两个错误:

  

错误:无效使用不完整类型'struct A'

     

错误:'struct A'的前向声明

我认为这是因为A :: bar()尚未定义或声明,因为两个标题相互引用。但是,我转发申报的A级,我不知道还需要做什么。我是C ++的新手,所以请原谅我。我无法在网上找到这个问题的答案。一如既往,提前谢谢!

6 个答案:

答案 0 :(得分:9)

你有一个循环引用,所以你需要分开B.h.尝试类似:

B.h:

#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

编辑:解释为什么需要将其拆分为两个文件:

班级B包含对A的引用,该引用可以是所谓的不完整类型。你不能调用它上面的任何函数,因为编译器还不知道he A是什么 - 它只知道它是某种类。一旦你包含A.h(在.cpp文件中),那么A就是一个完整的类型,你可以用它做任何你想做的事。

您无法将整个事件保存在一个头文件中,因为您将获得循环引用。你阻止了包含守卫的无限循环,但是你得到了一些你不想要的东西。查看编译器在编译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;
}

答案 1 :(得分: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;
}

答案 2 :(得分:1)

正如其他几位提到的那样,循环引用似乎是您的问题。另一个用语是“相互依赖”。但是,我没有试图找到合适的语法来让您的应用程序编译和运行(我假设实际问题存在于比您发布的程序略高的程序中),我鼓励您从对象中解决问题 - 导向设计的立场。

作为一般规则,应尽可能避免相互依赖。我之前在我自己的代码中遇到过这个问题(这导致了几天的调试,扯掉我的头发,诅咒我的编译器),这就是我最终能够克服它的方式。我将把我自己问题的一个淡化版本作为如何解决这个问题的一个具体例子,所以希望你能够揭开它背后隐藏的意义,最终都会有意义。

假设我们有两个类:Data和DataAnalyzer

Data保存对DataAnalyzer的引用(用于分析数据),DataAnalyzer保存对Data(要分析的数据)的引用 - 相互依赖!为了消除这种依赖性,我们从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);
}

现在,Data独立存在(据他所知,IAnalyzer不需要对Data的引用),只有DataAnalyzer依赖于Data。如果你想继续,你可以继续删除DataAnalyzer对Data的依赖,但是为了简单地打破相互依赖,这应该就足够了。

警告:我没有编译测试过此代码,因此可能需要进行一些小的调整才能编译并正常运行。

祝你好运!

答案 3 :(得分:1)

如果你真的希望B :: foo是内联的,你可以在B.h中执行,虽然我不推荐它。

B.h:

#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_*/

A.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_*/

答案 4 :(得分:0)

在B.h中,你包括A.h以及前方声明A。

您需要将B.h分为B.h和B.cpp,或删除前向声明。

PS你也有循环依赖。 A.h包括B.h,反之亦然。但是你的警卫正在解决这个问题;)

答案 5 :(得分:0)

要添加到另一个答案(循环引用,这是正确答案),如果您来自C#/ Java,请理解C ++是不同的,因为文件是按顺序解析的(而不是整体考虑) )。因此,您需要注意确保在使用之前定义所有内容,按照包含文件的实际顺序(和/或适当的.cpp文件中的单独功能)。