c++ - 一个程序有很多系统(类)。使系统可以通过类名调用其他人?

标签 c++ architecture typeid typeinfo

假设我有一个游戏引擎。

假设它包含类GraphicGamePlayPhysics 系统。
(真实案例是20+系统。)

这三个都是从System派生的。

这是简单初始化的草稿。

main(){
    Game_Engine* engine = new Game_Engine();
    Graphic* sys1= new Graphic(engine); //set to System::engine_pointer
    GamePlay* sys2= new GamePlay(engine);
    Physics* sys3= new Physics(engine);
    engine->addSystem(sys1); //add to Game_Engine's hash map
    engine->addSystem(sys2);
    engine->addSystem(sys3);
}

然后,我想让所有系统都可以互相调用。 前任。 Graphic可以调用GamePlay

所以我将 addSystem() 设计为:-

class Game_Engine {  
    std::unordered_map<std::type_index,Sys*> hashTable; 
    void addSystem (System* system){
        hashTable.add( std::type_index(typeid(*system)), system );
    }
    template <class SysXXX> SysXXX* getSystem(){
        return hashTable.get(std::type_index(typeid(SysXXX)) );
    }
}

结果是每个System可以只用类名互相调用:-

class Graphic : public System {
    void call_me_every_time_step(){
        engine_pointer->getSystem<GamePlay>()->... do something ;
    }
}

现在,它如我所愿,但是

  1. 听说 typeid 对性能不好。

  2. Game_Engine.h 现在必须 #include 所有 Graphic.hGamePlay.hPhysics.h ,因此编译时间增加。
    (我试图不包括它们 -> 3 个派生系统的 typeid 将返回错误的结果。)

是否可以避免这些缺点?怎么样?
还有其他缺点吗?
这首先是一个糟糕的设计吗?如果是这样,什么是好的设计? (因为我在 C++ 方面的经验非常有限。)

编辑 1:下面部分对 gudok 的回答的回应

为每个系统添加一定的get/set函数是我做的。

但是,我意识到,当系统越多时,管理就越难,至少对我而言是这样。

我避开了它,改用了模板代码,如上所述。

对于gudok的解决方案,单个系统会增加程序员的工作量如下:-

  1. 在“GameEngine”中添加字段声明
  2. 再添加一个返回某个系统的函数
  3. 重命名类时,例如通过使用自动重构工具将“图形”更改为“渲染”,我也必须将 getGraphics() 重命名为 getRender()(以使代码可读)

对比题中的代码,单个系统只需1行。

engine->addSystem(new Graphics(engine));

这不是那么微不足道,尤其是当大多数系统都在更名,系统数量不断增加时。

编辑 2:对 gudok 增强答案的回应

使 GameEngine 派生自 SystemHolder{T} 可以将每个 System 的工作减少到 2 个地方:-

: public SystemHolder<Graphics>

engine.addSystem<Graphics>(new Graphics());  

不过还是2个地方。
有问题的代码只使用了 1 个地方。
因此,它还不够好,但感谢您的尝试!

最佳答案

为什么要使用 hash map 和 typeids 而不是在 GameEngine 中单独存储每个系统?从语义上讲,所有这些系统都做不同的事情。我宁愿做以下事情:

class GameEngine {
  std::vector<System*> systems;

  Graphics* graphics;
  Gameplay* gameplay;
  Physics* physics;

  void setGraphics(Graphics* graphics) {
    this->graphics = graphics;
    this->systems.push_back(graphics);
  }
  Graphics* getGraphics() {
    return this->graphics;
  }

  ...
};

这个解决方案背后的想法是:

  1. 从语义的角度来看,每个系统都是不同的。当您从某个地方访问图形时,您很可能会使用特定于 Graphics 的函数而不是所有功能都通用 System秒。单独存储每个系统可以消除类型标识和不必要的类型转换的必要性。
  2. 当您需要以某种统一的方式处理所有系统时(例如,提前游戏时间),您可以使用 systems领域:

    for (auto it = systems.begin(); it != systems.end(); it++) {
      it->tick();
    }
    

编辑 这是增强的解决方案。您通过另外继承 GameEngine 添加新系统来自 SystemHodler .获取和设置特定 System 的实例使用 getSystem<T> 是统一的和 setSystem<T>方法——如您所愿。

#include <vector>

class System {
public:
  virtual ~System() {}
};
class Graphics : public System {};
class Physics: public System {};

template<typename T>
class SystemHolder {
public:
  T* getSystem() { return system; }
  void setSystem(T* system) { this->system = system; }

private:
  T* system;
};

class GameEngine: public SystemHolder<Physics>, public SystemHolder<Graphics> {
public:
  template<typename T>
  inline void addSystem(T* system) {
    systems.push_back(system);
    SystemHolder<T>::setSystem(system);
  }

  template<typename T>
  inline T* getSystem() {
    return SystemHolder<T>::getSystem();
  }

private:
  std::vector<System*> systems;
};

int main(int argc, char* argv[]) {
  GameEngine engine;
  engine.addSystem<Physics>(new Physics());
  engine.addSystem<Graphics>(new Graphics());

  engine.getSystem<Physics>();
  engine.getSystem<Graphics>();
}

关于c++ - 一个程序有很多系统(类)。使系统可以通过类名调用其他人?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37298601/

相关文章:

c++ - 使用 std::vector 时如何将索引信息传递给元素构造函数?

architecture - RISC-V 中 JAL 的定义是什么以及如何使用它?

java - Java JAR 文件是否类似于 .Net 程序集?

c++ - 为什么在使用 "typeid"时必须输入数组长度?

c++ - 获取当前保存的 std::variant 的 typeid(如 boost::variant type())

C++11 - typeid 唯一性

c++ - 将浮点值从大端转换为小端

C++:std::visit 不能在 gcc 下编译

c++ - Boost 和 OpenCV 库与 Eclipse CDR 的静态链接。错误

c - UART ISR Tx Rx 架构