user923
user923

Reputation: 559

C++ - Recommended way to allow access objects in a class to access the class they are in

I have a class Game with class EnemyManager. EnemyManager deals with spawning of enemies and the logic behind it. The problem is that EnemyManager needs access to functions and other Game objects in the game class. I can think of two ways to handle this.

  1. Pass the address of the class object Game* using this as one of the arguments in the EnemyManager.

  2. Declare a global pointer to a Game object and set it when initializing the Game class. Then extern it into the enemymanager.cpp.

Which is the more advisable way to do this?

Upvotes: 2

Views: 1073

Answers (5)

Dmitry
Dmitry

Reputation: 163

It is hard to say without knowing the overall architecture layout, but my 2 cents:

The way you describe as a first one is called the dependency injection and is widely used all around. You should keep an eye on what methods/fields you're making public. I assume that Game class has the methods that should not be accessible from the EnemyManager class, thus is seems like it's a good idea to create the interface which has the declaration of the methods that are used by EnemyManager and then pass the pointer to the EnemyManager instance (instead of the Game).

For example: The Game class implements IGameEnemyManager, and you're passing IGameEnemyManager using this as one of the initialization arguments.

Upvotes: 1

Netherwire
Netherwire

Reputation: 2787

You're absolutely need to use the 1st approach, but with a few changes: you should disintegrate your Game class to more components. For example you can create a SceneManager class, which is responsible for all game object's creation/management. When you're instantiating the EnemyManager - just pass a pointer to it:

// in SceneManager
EnemyManager* emgr = new EnemyManager(this);
InterfaceManager* imgr = new InterfaceManager(this);

Note that your SceneManager class should provide a complete interface

// in EnemyManager
GameObject* spawnEnemyAt(string name, EnemyClass* eclass, Vector3 position, AIBehaviour* behaviour)
{
    GameObject* myEnemy = smgr->createGameObject(name, position, etc...);
    //register an enemy in the enemies list, initialize it's behaviour and do any other logic

    return myEnemy
}

This approach should help you not to ruin your architecture and not to be captured in the friend(class)-zone.


[Upd.] Note that my approach assumes that all objects on the scene are GameObjects, there's neither Enemy nor Player classes. Every GameObject may has a Renderer, Animator, AIBehaviour components.

Upvotes: 0

HonkyTonk
HonkyTonk

Reputation: 2019

When dealing with object oriented designs, it is typically good to think about who will act how on what to find a first version of a design. After having written this version, one often finds the weaknesses and rewrite it for the second iteration.

So, in this case, the Game class manages the world (I assume) and offers different ways to manipulate it. Your EnemyManager manages one aspect of the world, enemies, but they do live inside the world.

class Enemy {
public:
  Enemy(Location& location, int hitpoints);
  bool Move(Direction& direction);
};

class Game {
public:
  bool CreateInitialState();
  bool AddEnemy(Enemy& enemy);
  bool AddBullet(Location& location, Direction& direction, int speed);
  void Render();
};

class EnemyManager {
public:
  EnemyManager(Game& game);
  void MoveEnemies();
};

In this first version, all types see each other as proper classes and manipulates things by calling the appropriate method. This offers little support for expanding on the game if you want to add new things to it.

This is where interfaces become handy and you can try to think about how the different parts will interact instead of how they should be implemented.

class Canvas {
public:
  // Different graphical primitives.
};

class GameObject {
public:
  virtual ~GameObject() {};
  virtual void Draw(Canvas& canvas) = 0;
  virtual bool Move(Direction& direction) = 0;
};

class GlobalState {
public:
  virtual AddGameObject(GameObject& gameObject) = 0;
};

class Game : public Canvas, public GlobalState {
public:
  bool CreateInitialState();

  void Render() {
    // Send itself to the Draw method in all GameObjects that have been added
  }

  // Other game logic
};

class Enemy : public GameObject {
  // This can be specialized even more if you need to
};

class Bullet : public GameObject {
  // This can also be specialized even more if you need to
};

This separates design from implementation and, as I see it, is a good way to end up with a proper first attempt.

Upvotes: 1

sakthisundar
sakthisundar

Reputation: 3288

If you are handling game objects in EnemyManager, why is it part of the class Game ? I suppose you should consider reviewing your design as there are chances of circular reference problem if you don't handle the scenarios well.

Consider segregating both the classes to ensure a single responsibility principle.
Define proper interface in your
EnemyManagerto Game object as argument and act on the functions

These are little suggestions that I can think of with limited idea about your design

Upvotes: 0

Ben Hobbs
Ben Hobbs

Reputation: 84

Whenever I encounter situations like this I review the overall design of the related classes. If EnemyManager is a member of a Game object and needs to call things within Game, maybe those functions in Game can be factored out into a separate component. If something you are writing is beginning to feel overly-complex or like a hack it's usually time to do some factoring.

Upvotes: 5

Related Questions