C++-允许类中的访问对象访问它们所在的类的推荐方法

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

本文关键字:访问 方法 许类中 对象 C++-      更新时间:2023-10-16

我有一个类Game和类EnemyManagerEnemyManager处理敌人的生成及其背后的逻辑。问题是EnemyManager需要访问游戏类中的函数和其他Game对象。我能想出两种方法来处理这个问题。

  1. 使用this作为EnemyManager中的参数之一传递类对象Game*的地址。

  2. 声明一个指向Game对象的全局指针,并在初始化Game类时对其进行设置。然后将其外部化到enemymanager.cpp中。

哪种方法更可取?

每当我遇到这样的情况时,我都会回顾相关类的总体设计。如果EnemyManager是Game对象的成员,并且需要在Game中调用东西,那么Game中的这些功能可能可以分解为一个单独的组件。如果你正在写的东西开始觉得过于复杂或像黑客一样,通常是时候做一些分解了。

在处理面向对象的设计时,通常最好考虑谁将采取什么行动来找到设计的第一个版本。在编写完这个版本之后,人们经常会发现弱点,并为第二次迭代重写它。

因此,在这种情况下,Game类管理世界(我认为),并提供了不同的操纵方式。你的EnemyManager管理世界的一个方面,敌人,但他们确实生活在世界内部。

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();
};

在第一个版本中,所有类型都将彼此视为适当的类,并通过调用适当的方法来操纵事物。如果你想在游戏中添加新的东西,这几乎不支持扩展游戏。

这就是接口变得方便的地方,您可以尝试思考不同部分将如何交互,而不是应该如何实现它们。

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
};

这将设计与实现分离开来,在我看来,这是一个很好的方式来完成正确的第一次尝试。

如果不了解整体架构布局,很难说,但我的2美分:

您所描述的第一种方式称为依赖项注入,并且在所有方面都被广泛使用。你应该留意你公开的方法/字段。我假设Game类具有不应该从EnemyManager类访问的方法,因此创建一个具有EnemyManager使用的方法声明的接口,然后将指针传递给EnemyManager实例(而不是Game)似乎是一个好主意。

例如:Game类实现了IGameEnemyManager,您将使用它作为初始化参数之一来传递IGameEnemyManager

如果您在EnemyManager中处理game objects,为什么它是类Game的一部分?我想你应该考虑审查你的设计,因为如果你不能很好地处理场景,就有可能出现循环引用问题。

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

这些都是我能想到的关于你的设计的小建议

您绝对需要使用第一种方法,但有一些更改:您应该将Game类分解为更多的组件。例如,您可以创建一个SceneManager类,该类负责所有游戏对象的创建/管理。当您实例化EnemyManager时,只需向其传递一个指针:

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

请注意,您的SceneManager类应该提供一个完整的接口

// 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
}

这种方法应该可以帮助您避免破坏您的体系结构,避免在friend(类)区域中被捕获。


[Upd.]请注意,我的方法假设场景中的所有对象都是GameObjects,既没有Enemy类,也没有Player类。每个GameObject可以具有一个RendererAnimatorAIBehaviour分量。