复杂的循环依赖
在 C++ 中解决循环依赖的最佳实践是什么?
我可以使用前向声明,但随后出现 pointer to incomplete class type is not allowed
错误。这是否意味着使用彼此指针的两个类不能依赖?
另外,我考虑过向前声明每个类,然后将解决方案的每个标头包含在 main.cpp
中,因此它们都在一个位置。你会推荐它吗?
下面是整个项目的一个片段,因此如果在我熟悉的示例中更好地解释了该问题,您可以参考它,但这只是理论上的。 谢谢
what is the the best practice of solving circular dependency in C++
?
I could use the forward declaration, but then I get the pointer to incomplete class type is not allowed
error. Does that mean that two classes that uses each others pointer cannot be dependent?
Also, I thought about forward declaring each class and then including every header of the solution in the main.cpp
, so it's all in one place. Would you recommend it?
A snippet from the whole project is below, so you can refer to it if the issue is better explained on an example I'm familiar with, but it would do just to be theoretical.
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您只需要正确使用前向声明:
注意:添加包含防护。
如果没有实际的声明,很难真正做到这一点。该图很好,但没有足够的信息。一张图片可能胜过一千个单词,但精确定义的语言可以非常紧凑地传达更准确的信息(与英语及其不一致之处不同)。
You just need to use forward declaration correctly:
Note: Add include guards.
Its hard to actually do it without the actual declarations. The diagram is nice but does not have enough information. A picture may be worth a thousand words, but a precisely defined language can convey more exact information very compactly (unlike English and its inconsistencies).
一种想法是引入接口并消除循环依赖。因此,您将拥有一个 Effect、Player 和 EffectContainer 所依赖的 IEffect。可能,如果 Player 依赖于 Effect 的某些行为,而 EffectContainer 依赖于一组不同的行为,我会考虑引入两个接口,有效地遵循 接口隔离原则。这也遵循依赖倒置原则 。
One thought is to introduce interfaces and remove the circular dependencies. So you would have an IEffect that Effect, Player, and EffectContainer depend on. Possibly, if Player depends on certain behavior of Effect and EffectContainer depends on a different set of behavior, I would consider introducing two interfaces, effectively following the Interface Segregation Principle. This would also follow along with the Dependency Inversion Principle.
一般来说,这是通过让每个头文件在其
#include
之前预先声明它需要的类来实现的。此外,头文件中不应放置任何代码。所以你最终会得到:以及每个地方的等价物。然后在您的
.cpp
文件中,您实际上#include
其他类的标头。如果您的对象对其他类的内存布局没有循环依赖,那么这将起作用。这意味着方法和成员只能通过引用或指针(但不能通过值)引用其他类。 类的情况,这可能会变得有点奇怪如果您有诸如模板扩展有时需要完整类型而不仅仅是预先声明的类型之 。许多库避免此问题的一种方法是使用指向私有 impl 模式(通常称为 PIMPL 模式)的指针,其中每个类定义为:
然后
FooImpl
完全在中定义.cpp
文件,您的循环问题就可以得到解决。这种设计也很有价值,因为它可以保持库版本之间的二进制兼容性。它确实有点冗长,但没有人说 C++ 是一种简洁的语言。Generally this is implemented by having every header file pre-declare the classes that it needs before its
#include
. Additionally, no code should be put in the header files. So you end up with:and the equivalent in each place. Then in your
.cpp
files you actually#include
the headers for the other classes. This will work if your objects don't have a circular dependency on the memory layout of the other classes. Meaning that the methods and members can only refer to the other classes by reference or by pointer (but not by value). This can get a little squirrelly if you have things likeas the template expansion sometimes requires full types and not merely pre-declared types. One way many libraries avoid this issue is with a pointer to private impl pattern (often referred to as a PIMPL pattern), where every class is defined as:
then the
FooImpl
is defined entirely in the.cpp
file and your circularity issues can be ducked. This design is also valuable because it maintains binary compatibility across releases of your library. It does get a bit verbose, but nobody saidC++
was a succinct language.