将耦合的类拆分为单独的标头

时间:2018-08-26 07:19:18

标签: c++ class header-files circular-dependency forward-declaration

我正在尝试将以下两个类分开,以便每个类都可以在自己的标题中定义:


#include <iostream>

class Boo;
class Foo
{
public:
    Foo(Boo *booPtr)
    :booPtr(booPtr){};
    virtual ~Foo(){};
    Boo *booPtr;
};

class Boo
{
public:
    Boo()
    :foo(this){};
    virtual ~Boo(){};
    Foo foo;
    int num = 32;
};

int main()
{
    Boo *boo = new Boo;
    std::cout << "booPtr : " << boo->foo.booPtr->num << '\n';
    delete boo;
}

结果:

booPtr : 32
Program ended with exit code: 0

这是我分离失败的尝试:

"Foo.hpp"

#ifndef Foo_hpp
#define Foo_hpp

#include <stdio.h>
#include "Boo.hpp"

class Foo
{
public:
    Foo(Boo *booPtr)
    :booPtr(booPtr){};
    virtual ~Foo(){};
    Boo *booPtr;
};

#endif /* Foo_hpp */

"Boo.hpp"

#ifndef Boo_hpp
#define Boo_hpp

#include <stdio.h>
#include "Foo.hpp"

class Boo
{
public:
    Boo()
    :foo(this){};
    virtual ~Boo(){};
    Foo foo; // Error : Field has incomplete type 'Boo'
    int num = 32;
};

#endif /* Boo_hpp */

"main.cpp"

#include <iostream>
#include "Foo.hpp"
#include "Boo.hpp"

int main()
{
    Boo *boo = new Boo;
    std::cout << "booPtr : " << boo->foo.booPtr->num << '\n';
    delete boo;
}

但是我无法构建代码,因为它会产生以下错误:

  

Boo.hpp-> Foo foo; ->“字段的类型'Boo'不完整”

如何修复我的代码?

3 个答案:

答案 0 :(得分:2)

您拥有一个包含另一个文件的文件,反之亦然。这创建了一个包含的循环。

认识到#include宏除了将自身(即,它的行)替换为另一个文件外什么也不做。这很明显为什么您不能拥有文件A的包含文件B。

显而易见的解决方案是在Foo中预先声明Boo:

#ifndef Foo_hpp
#define Foo_hpp

#include <stdio.h>
class Boo;

class Foo
{
public:
...

奇怪的是,在分离它们之前,您已经这样做了。

现在为您提供更多理论:从技术上讲,一类是数据存储。它需要知道它的大小才能保留内存。因此,它需要知道其成员的所有大小,只有在声明它们时才能知道。因此,一个类需要包含其作为成员的每个类的声明标头。但是,指向对象的指针是不同的(引用也一样)。指针始终采用相同的大小,即32或64位,具体取决于您的平台(由于当前我们有64位平台,因此可能为64位)。因此,该类不需要知道它指向的类,它为其指针成员保留的内存始终是相同大小的。这就是为什么在这里没有任何关于类大小的正向声明的原因。

答案 1 :(得分:2)

虽然循环包含本身(假设适当的包含保护)不是错误,但请记住,它只是文本替换:

确保所有排列都是可行的,并且具有相同的含义!

在您的情况下,将"Boo.hpp"的包含替换为您在合并源中使用的前向声明。

或者,您知道,只需放弃拆分标题即可:
请记住,类不一定是组织的适当单位。

答案 2 :(得分:1)

以Foo.hpp开头的通函包含附录

#ifndef FOO
#define FOO  // (!)

// now including BOO!
#ifndef BOO
#define BOO

// now including FOO  a g a i n
// as FOO  i s  already defined, all that remains is:
#ifndef FOO
#endif

class Boo { }; // now lacking definition of Foo...

#endif // BOO

class Foo { };

#endif // FOO (first one)

类似地,对于Boo.hpp(您只需要预先声明Foo,但是没有,因为之后的定义再次出现...)。

如您所见,通函包括,提供了适当的包括保护措施,不会导致无限的自我接纳。但是,结果却不是您想要的。