这是一个非常重复的问题,在StackOverflow中也是如此,但是即使尝试不同的答案,我也无法解决我的问题。所以,我有一些课程:
主.cpp:
#include "foo.h"
#include "bar.h"
...
傅炯:
#include "bar.h"
class foo {
foo();
bar& bind(bar &b);
gru& bind(gru &g);
};
酒吧:
#include "foo.h"
class bar {
bar();
foo& bind(foo &f);
gru& bind(gru &g);
};
显然,我有一种循环依赖。所以我得到了臭名昭著的错误'bar' does not name a type
.在这种情况下,我将class bar;
添加到foo
声明并删除#include
。当我这样做时,我得到:invalid use of incomplete type ‘struct bar'
。
我尝试了一些不同的方式,也向栏添加了class foo;
,但我总是有某种错误。在最后一种情况下,我得到:
bar.cpp:48:11: error: prototype for ‘foo& bar::bind(bar::foo&)’ does not match any in class ‘bar’
bar.h:55:12: error: candidates are: gru& bar::bind(gru&)
bar.h:54:13: error: bar::foo& bar::bind(bar::foo&)
另外,我从来没有收到过任何关于gru
的抱怨。作为附加信息,该程序中已经存在,在我添加foo
之前,该程序已经与bar
和main
完美配合
。有什么有用的想法吗?非常感谢:)
in foo.h
#pragma once //(or use a guard symbol)
class bar;
class foo
{
bar & bind(bar & b);
};
在巴尔·
#pragma once //(or use a guard symbol)
class foo;
class bar
{
foo & bind(foo & f);
};
在福.cpp
#include <foo.h>
#include <bar.h>
bar foo:bind(bar & b)
{
// here you can use bar methods
}
在酒吧.cpp
#include <bar.h>
#include <foo.h>
foo bar:bind(foo & f)
{
// here you can use foo methods
}
这对我来说编译得很好(注意:这没有实例化 foo 或 bar 类):
文件栏.h 包含:
#ifndef BAR_H
#define BAR_H
#include "foo.h"
class foo;
class gru;
class bar {
bar();
foo& bind(foo &f);
gru& bind(gru &g);
};
#endif
文件 foo.h 包含:
#ifndef FOO_H
#define FOO_H
#include "bar.h"
class bar;
class gru;
class foo {
foo();
bar& bind(bar &b);
gru& bind(gru &g);
};
#endif
主.cpp文件包含:
#include "foo.h"
#include "bar.h"
非常感谢你们的回答。在许多方面,他们很有帮助。
最后,我意识到我必须重新排序代码中的所有#include
,因为,正如您可能已经意识到的那样,有更多的编码,我放在这里是一个更简单的版本(抱歉)。
因此,我的最终解决方案是将class bar;
包含在foo.h
中,class foo;
包含在bar.h
中。然后对main.cpp
和生成文件中的包含重新排序。
再次感谢;-)
C++编译器非常古老,此外,在代码中处理此示例的部分不是编译器本身,而是编译器链的一部分,称为预处理器。如果我们同意C++编译器不是很聪明,那么预处理器是一个沉闷但友好的助手。
编译 *.cpp 文件,稍后与程序中的其他 *.cpp 文件链接。.h文件本身实际上没有任何意义:这种分离对程序员来说应该是一个优势。这里的关键点是,无论您在 *.cpp 文件中包含多少 *.h 文件,最终都会有一个巨大的、生成的 cpp 文件,其中包含 *.h 文件中存在的整个代码,以及 *.cpp 文件中存在的源代码。
循环依赖可以通过多种方式解决,但是,正如您可以想象的那样,编译器在问题产生之前甚至没有机会处理任何事情。C 或 C++ 不支持循环依赖关系。
更现代的编译器(如Java或C#)能够提取类的公共接口,并在B
类需要A
类时使用此信息,反之亦然。同样,这在C++是不可能的。
唯一的解决方案是重写代码。目标是消除循环#include
指令。实际上,如果您的代码像您在此处显示的那样简单,则可以使用前向声明轻松解决它:
// foo.h
class bar;
class foo {
foo();
bar& bind(bar &b);
gru& bind(gru &g);
};
--
// bar.h
class foo;
class bar {
bar();
foo& bind(foo &f);
gru& bind(gru &g);
};
--
// main.cpp
#include "foo.h"
#include "bar.h"
// ...
int main()
{
// ...
}
前向声明可以让编译器知道这个类实际上将要存在,并且你提前声明它的存在,但没有提供有关其内容的详细信息。这就是为什么你可以把这个前向声明用于另一个类中的指针(或引用)(编译器需要转发类的大小才能创建属性,如果没有细节,这是不可能的)。
这里还有其他概念需要处理,例如编译器保护。
希望这有帮助。