5

我有一个我认为与前向声明有关的问题,但也许不是。

以下是相关代码:

#ifndef A_H_
#define A_H_

#include "B.h"

class A
{
    private:
        B b;

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

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

主文件

#include "A.h"

int main()
{
    A a;

    return 0;
}

问题似乎在于 A::bar() 的调用。该程序成功编译,直到我尝试调用此方法,此时我得到两个错误:

错误:无效使用不完整类型'struct A'</p>

错误:“结构 A”的前向声明</p>

我认为这是因为 A::bar() 尚未定义或声明,因为两个标题相互引用。但是,我转发了声明的 A 类并且不知道我还需要做什么。我是 C++ 新手,所以请原谅我。我在网上其他任何地方都找不到这个问题的答案。一如既往,提前致谢!

4

6 回答 6

11

你有一个循环引用,所以你需要分开 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

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

该类B包含对 的引用A,它可以是所谓的不完整类型。你不能在它上面调用任何函数,因为编译器还不知道到底A是什么——它只知道它是某种类。一旦你包含 Ah(在 .cpp 文件中), thenA就是一个完整的类型,你可以用它做任何你喜欢的事情。

您不能将整个内容保存在一个头文件中,因为您将获得循环引用。您正在使用包含防护来防止无限循环,但是您得到了您不想要的东西。当你编译 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

该行将#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

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的依赖,但为了简单的打破相互依赖,这应该足够了。

警告:我没有编译测试过这段代码,所以它可能需要一些小的调整才能正确编译和运行。

祝你好运!

于 2009-01-20T07:34:47.047 回答
1

如果你真的希望 B::foo 是内联的,你可以在 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 回答
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 回答