【问题标题】:Odd Circular Dependency Issue奇怪的循环依赖问题
【发布时间】:2009-10-21 01:03:06
【问题描述】:

所以我有 2 个类,Bullet 和 Ship,它们相互依赖,因此循环包含。由于我将 Ship 的接口#included 到 Bullet 的接口中,显而易见的决定是将 Bullet 声明转发给 Ship。

但是,当我第一次尝试这个时,我仍然遇到编译器错误。我阅读了一些关于前向声明的内容,并意识到我正在使用 Ship 的一种方法构造 Bullet,并且 Bullet 的默认构造函数是成员初始化的,这(我可能错了)不起作用,因为前向类声明不起作用允许 Ship 查看接口中的定义(即成员初始化)。

所以我决定我可以放弃成员 init 并在 Bullet 的实现文件中定义构造函数,但是我仍然遇到循环依赖的相同问题。

特别是消息是invalid use of undefined type struct Bullet

我可以将 Bullet 和 Ship 的界面放在同一个文件中,但这是最后的手段。感谢您提供有关此问题的任何帮助。谢谢。

这里是发生错误的地方:

case SDLK_UP: // Fire
{
    Bullet(*this) fired_bullet; // Create bullet. Line where error occurs.
    fired_bullet.Move(); // Move bullet
    break;
}

Bullet 的默认构造函数接受正在发射子弹的 Ship 的参数,并且该代码位于 Ship 方法中。

【问题讨论】:

  • 子弹肯定属于枪而不是船?
  • 其实Ammunition不是更准确吗?
  • 项目符号是显示在屏幕上的单个对象,所以不是。

标签: c++


【解决方案1】:

你想要:

Bullet fired_bullet(*this);

但是您的耦合非常紧密。 Bullet 需要 Ship 什么,Ship 需要 Bullet 什么?

我认为子弹需要知道它来自哪艘船,这样敌人的子弹就不会伤害敌人的子弹,反之亦然。也许您需要团队类型?

enum bullet_team
{
    bullet_player,
    bullet_enemy,
}

而你的飞船和敌人只会告诉子弹他们在哪支队伍,而不是强迫子弹追踪它的来源:

关于射击,也许做一个 BulletManager 单例。告诉经理您想在 X 位置、团队方向 Y 和属性 Z 制造子弹,经理会为您处理。

BulletManager::reference().fire(getPosition(), bullet_player);

【讨论】:

  • Dito,也许会提到替代方法?
  • 我是个白痴。谢谢。 Bullet 需要知道它是从哪艘飞船发射的,而 Ship 需要在用户按下向上键时发射子弹。
  • 我现在只是在编写一个基本的触发机制。我一点一点地设计和编码。
  • @trikker - 您使用的增量方法通常是最好的方法。
  • 确实,只要您保持良好的代码实践,我会说这是最好的,这意味着使事情易于更改、清理和模块化。
【解决方案2】:

替换:

Bullet(*this) fired_bullet;

与:

Bullet fired_bullet(*this);

【讨论】:

    【解决方案3】:

    您必须将头文件中的定义移到源文件中,只留下头文件中的声明。它应该看起来像这样:

    // Ship.h
    class Bullet;
    class Ship
    {
        // Declare stuff, using only pointers/references to Bullet instances
        Ship();
        ...
    };
    
    // Bullet.h
    class Ship;
    class Bullet
    {
        // Declare stuff, using only pointers/references to Ship instances
        Bullet(const Ship & ship);
        ...
    };
    
    // Ship.cpp
    #include "Bullet.h"
    #include "Ship.h"
    
    // Ship definitions
    Ship::Ship()
    {
        ...
    }
    ...
    
    // Bullet.cpp
    #include "Bullet.h"
    #include "Ship.h"
    
    // Bullet definitions
    Bullet::Bullet(const Ship & ship)
    {
        ...
    }
    ...
    

    最后,您实例化 Bullet 的语法是错误的。你应该像这样实例化它:

    Bullet fired_bullet(*this);
    

    【讨论】:

    • 除了1到2行方法和成员初始化之外,定义在标题之外。这是一个语法错误。
    【解决方案4】:

    方法如下。

    Bullet.h

    class Ship; //forward declaration
    class Bullet
    {
      //declaration of Bullet
      //may use Ship references and Ship pointers
    };
    

    Bullet.cpp

    #include "Bullet.h"
    #include "Ship.h"
    //definitions of Bullet methods go here
    

    Ship.h

    class Bullet; //forward declaration
    class Ship
    {
      //declaration of Ship
      //may use Bullet references and Bullet pointers
    };
    

    Ship.cpp

    #include "Ship.h"
    #include "Bullet.h"
    //definitions of Ship methods go here
    

    这将永远有效:

    • 在每个头文件中,声明一个类;对其他类使用前向声明(仅名称),并且仅通过引用或指针而不是值来引用这些类(作为成员方法参数和成员实例数据)。

    • 在每个CPP文件中,先包含自己的头文件;然后为您需要声明的类包含任何其他标头(如果您定义的方法通过值实例化这些其他类的实例,或调用这些其他类的方法,则需要它们)。

    有时您可能希望在头文件中通过值(而不是通过指针或引用)来引用第二个类;没关系,但如果你这样做了,那么你就到了循环依赖的一半。如果确实以循环依赖结束,那么您需要更改它,以便至少一个类(如果不是两者)仅通过指针或引用而不是值来引用另一个。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-18
      • 1970-01-01
      相关资源
      最近更新 更多