我经常发现自己处于这样的情况下,由于一些错误的设计决策(由其他人做出的:),导致C++项目中出现多个编译/链接器错误,从而导致不同头文件中C++类之间的循环依赖(也可能发生)在同一文件中)。但是幸运的是(?)发生的次数不够多,以至于我下次再次遇到该问题时仍记得该问题的解决方案。
因此,为了将来方便起见,我将发布一个具有代表性的问题及其解决方案。当然,更好的解决方案是受欢迎的。
A.h
class B;
class A
{
int _val;
B *_b;
public:
A(int val)
:_val(val)
{
}
void SetB(B *b)
{
_b = b;
_b->Print(); // COMPILER ERROR: C2027: use of undefined type 'B'
}
void Print()
{
cout<<"Type:A val="<<_val<<endl;
}
};
B.h
#include "A.h"
class B
{
double _val;
A* _a;
public:
B(double val)
:_val(val)
{
}
void SetA(A *a)
{
_a = a;
_a->Print();
}
void Print()
{
cout<<"Type:B val="<<_val<<endl;
}
};
main.cpp
#include "B.h"
#include <iostream>
int main(int argc, char* argv[])
{
A a(10);
B b(3.14);
a.Print();
a.SetB(&b);
b.Print();
b.SetA(&a);
return 0;
}
最佳答案
考虑这一点的方法是“像编译器一样思考”。
假设您正在编写一个编译器。您会看到这样的代码。
// file: A.h
class A {
B _b;
};
// file: B.h
class B {
A _a;
};
// file main.cc
#include "A.h"
#include "B.h"
int main(...) {
A a;
}
当您编译 .cc 文件时(请记住 .cc 而不是 .h 是编译单位),您需要为对象
A
分配空间。那么,那么多少空间呢?足以存储B
!那么B
的大小是多少?足以存储A
!哎呀。显然,您必须中断循环引用。
您可以通过允许编译器保留预知的最大空间来破坏它-例如,指针和引用将始终为32位或64位(取决于体系结构),因此如果您将(替换为)指针或引用,事情将会很棒。假设我们替换为
A
:// file: A.h
class A {
// both these are fine, so are various const versions of the same.
B& _b_ref;
B* _b_ptr;
};
现在情况好了。有些。
main()
仍然说:// file: main.cc
#include "A.h" // <-- Houston, we have a problem
#include
,在所有方面和用途(如果您将预处理器取出),只需将文件复制到 .cc 即可。真的, .cc 看起来像:// file: partially_pre_processed_main.cc
class A {
B& _b_ref;
B* _b_ptr;
};
#include "B.h"
int main (...) {
A a;
}
您可以看到为什么编译器无法处理此问题-它不知道
B
是什么-以前从未见过该符号。因此,让我们告诉编译器有关
B
的信息。这被称为forward declaration,并将在this answer中进一步讨论。// main.cc
class B;
#include "A.h"
#include "B.h"
int main (...) {
A a;
}
这有效。这不是很好。但是在这一点上,您应该已经了解了循环引用问题以及我们为修复此问题所做的工作,尽管修复效果很差。
此修复程序不好的原因是,下一位
#include "A.h"
的人必须先声明B
才能使用它,并且会遇到严重的#include
错误。因此,让我们将声明移到 A.h 本身。// file: A.h
class B;
class A {
B* _b; // or any of the other variants.
};
现在,在 B.h 中,您可以直接使用
#include "A.h"
。// file: B.h
#include "A.h"
class B {
// note that this is cool because the compiler knows by this time
// how much space A will need.
A _a;
}
HTH。
关于c++ - 解决由于类之间的循环依赖而导致的构建错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50726754/