开发者

Odd Circular Dependency Issue

开发者 https://www.devze.com 2022-12-09 04:33 出处:网络
So I have 2 classes, Bullet and Ship, that are dependent on each other, hence circular inclusion. Since I have Ship\'s interface #included into Bullet\'s interface, the obvious decision was to forward

So I have 2 classes, Bullet and Ship, that are dependent on each other, hence circular inclusion. Since I have Ship's interface #included into Bullet's interface, the obvious decision was to forward de开发者_高级运维clare Bullet to Ship.

However, when I first tried this I still got compiler errors. I read up a bit on forward declaration and realized that I was constructing a Bullet in one of Ship's methods, and Bullet's default constructor is member initialized, which (and I may be wrong) wouldn't work because a forward class declaration doesn't allow Ship to see definitions in the interface (i.e. member initialization).

So I decided I could give up the member init and just defined the constructor in Bullet's implementation file, however I still receive the same problem with circular dependency.

The message in particular is invalid use of undefined type struct Bullet.

I could just put the interface for Bullet and Ship in the same file, but that's kind of a last resort. Any assistance regarding this problem is appreciated. Thanks.

Here is the spot where the error occurs:

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

Bullet's default constructor takes an argument of the Ship that is firing the bullet, and that code is in a Ship method.


You want:

Bullet fired_bullet(*this);

But your coupling is very tight. What does Bullet need from Ship, and what does Ship need from bullet?

I assume the bullet needs to know what ship it came from so enemy bullets don't hurt enemy's and vice versa. Perhaps you need a team type?

enum bullet_team
{
    bullet_player,
    bullet_enemy,
}

And your ships and enemies will only tell the bullet what team they are on, rather than forcing the bullet to keep track of where it came from:

About firing, maybe make a BulletManager singleton. Tell the manager you want to make a bullet at a position X, with team orientation Y, and properties Z, and the manager will take care of it for you.

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


replace:

Bullet(*this) fired_bullet;

with:

Bullet fired_bullet(*this);


You'll have to move the definitions out of your headers into your source files, leaving only the declarations in the headers. It should look something like this:

// 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)
{
    ...
}
...

Finally, your syntax for instantiating a Bullet is wrong. You should be instantiating it like this:

Bullet fired_bullet(*this);


A way to do it is as follows.

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

This will always work:

  • In each header file, declare one class; use a forward declaration (name only) for other classes, and refer to those classes (as member method parameters, and as member instance data) only by reference or by pointer, not by value.

  • In each CPP file, include your own header file first; and then include any other headers, for classes whose declarations you need (you need them if the methods which you're defining instantiate instances of these other classes by value, or invoke methods of these other classes).

Sometimes you may want to refer to a second class by value (instead of by pointer or reference) in the header file; that's OK, but if you do then you're half-way to a circular dependency. If do do end up with a circular dependency, then you need to change it so that at least one of the classes (if not both) refers to the other only by pointer or reference instead of by value.

0

精彩评论

暂无评论...
验证码 换一张
取 消