复杂的循环依赖

发布于 2024-10-24 08:20:23 字数 349 浏览 2 评论 0原文

在 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

Models depending on each other

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

旧时浪漫 2024-10-31 08:20:23

您只需要正确使用前向声明:

  1. 将所有代码放入 cpp 文件中
  2. 将类声明放入头文件中
  3. 在头文件中:
    1. 如果仅使用指针或引用,请使用前向声明。
    2. 否则您需要包含头文件。 (请勿添加不需要的包含内容)
  4. 在 cpp 文件中
    1. 包含您需要的所有头文件。

注意:添加包含防护。

如果没有实际的声明,很难真正做到这一点。该图很好,但没有足够的信息。一张图片可能胜过一千个单词,但精确定义的语言可以非常紧凑地传达更准确的信息(与英语及其不一致之处不同)。

You just need to use forward declaration correctly:

  1. Put all code in cpp files
  2. Put just class declaration in header file
  3. In header file:
    1. Use forward declaration if you only use a pointer or a reference.
    2. Otherwise you to include header file. (Do Not add unrequired includes)
  4. In cpp file
    1. include all header files you require.

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).

沧桑㈠ 2024-10-31 08:20:23

一种想法是引入接口并消除循环依赖。因此,您将拥有一个 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.

柏林苍穹下 2024-10-31 08:20:23

一般来说,这是通过让每个头文件在其#include之前预先声明它需要的类来实现的。此外,头文件中不应放置任何代码。所以你最终会得到:

class Effect;
class Player;
class GameStack;

#include <vector>
// more includes

class EffectContainer { ... }

以及每个地方的等价物。然后在您的 .cpp 文件中,您实际上 #include 其他类的标头。如果您的对象对其他类的内存布局没有循环依赖,那么这将起作用。这意味着方法和成员只能通过引用或指针(但不能通过值)引用其他类。 类的情况,这可能会变得有点奇怪

 class EffectContainer {
   std::Vector<Effect> effects;
 }

 class Effect {
   boost::shared_ptr<EffectContainer> parent;
 }

如果您有诸如模板扩展有时需要完整类型而不仅仅是预先声明的类型之 。许多库避免此问题的一种方法是使用指向私有 impl 模式(通常称为 PIMPL 模式)的指针,其中每个类定义为:

class FooImpl;
class Foo {
  FooImpl* impl;
}

然后 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:

class Effect;
class Player;
class GameStack;

#include <vector>
// more includes

class EffectContainer { ... }

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 like

 class EffectContainer {
   std::Vector<Effect> effects;
 }

 class Effect {
   boost::shared_ptr<EffectContainer> parent;
 }

as 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:

class FooImpl;
class Foo {
  FooImpl* impl;
}

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 said C++ was a succinct language.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文