我目前正在使用SFML开发2D游戏引擎,并且从程序上入手,但从长远来看,决定将其移至OOP会更好。我了解OOP的概念,但是,我不确定我应该在哪里定义其他类(ImageManager,Grid等)的实例化。

例如,My Engine类依赖于ImageManager,而Engine中的许多函数都依赖于ImageManager中的变量。因此,我不能在一个函数中简单地声明和定义ImageManager类的实例,因为它在其他函数中不可用。

我所做的是:
Engine.h:

class Engine
{
private:
    sf::RenderWindow window;
    grid (*gridArray)[SIZE];
    ...//more variables, removing for length
    //Initializes the engine
    bool Init(); //Main Game Loop
    void MainLoop(); //Renders one frame
    void RenderFrame(); //Processes user input
    void ProcessInput(); //Updates all Engine internals
public:
    Engine(int w, int h, int tileSize);
    ~Engine();
    ImageManager * imageManager;
    GridClass * gridClass;
    ...//removed some methods for length
};

因此,基本上,您可以看到我在 header 中声明了两个类ImageManagerGridClass
在Engine.cpp内部:
Engine::Engine(int w, int h, int tileSize)
{
    imageManager = new ImageManager;
    gridClass = new GridClass();
    gridArray = new grid[SIZE][SIZE]();
    masterArray = new unsigned char[MAP_SIZE][MAP_SIZE];
    videoSize = sf::Vector2i(w, h);
    this->tileSize = tileSize;
    startX = startY = endX = endY = 0;
}

我正在定义类。我在构造函数中执行此操作是因为我不确定应该在哪里执行该操作,以符合良好的做法。
我一直遇到imageManager元素损坏的问题,所以我想知道我的操作方式是否是个坏主意。

如果这是一个坏主意,您能告诉我在哪里实例化这些类吗?请记住,Engine中的许多函数都依赖于这些类的这些实例中的变量,并且我真的不想将大量参数传递给每个函数。

谢谢。

最佳答案

从类内部实例化成员对象会使代码的模块化/灵活性降低。

想象一下,您的ImageManager类有多种实现方式(例如,在对MockImageManager进行单元测试时,您可能希望拥有Engine,或者您可能希望使用各种第三方Engine来尝试自己的ImageManager),这是使Engine使用一个实现ImageManager或其他方法是通过修改Engine本身的实现。

这会阻止您利用OOP的动态分配,尤其是C++的动态分配(此处的关键字是:继承,接口(interface)和虚拟调用)。

您会发现您的代码将变得更加灵活,例如:

class Engine
{
public:
    Engine(int w, int h, int tileSize, const std::shared_ptr<ImageManger>& im, const std::shared_ptr<GridClass>& gc);
protected:
    std::shared_ptr<ImageManger> imageManager;
    std::shared_ptr<GridClass> gridClass;
    // ....
};

只需复制在构造函数中给定的shared_ptr:
Engine::Engine(int w, int h, int tileSize, const std::shared_ptr<ImageManger>& im, const std::shared_ptr<GridClass>& gc) : imageManager(im), gridClass(gc)
{
    // ...
}

实例化方式:
int main(void)
{
    Engine engine(42, 42, 42, std::make_shared<ImageManager>(), std::make_shared<GridClass>());
    // ...
    return 0;
}

这样,您需要更改以提供新的ImageManager实现(假设ImageManager的接口(interface)由虚拟调用组成),就是将其编码为MyRevolutionaryImageManager(从ImageManager派生而来),并在其中更改std::make_shared调用。主要到std::make_shared<MyRevolitionaryImageManager>()。您甚至不必重新编译Engine,将使用新的实现(虚拟的美感使旧代码可以调用新代码)!

当然,现在如果ImageManager和/或GridClass仅是Engine的实现细节,并且如果不需要灵活性,那么从Engine中实例化它们就足够了。

不需要使用shared_ptr,您可以根据需要使用普通指针,但是您需要考虑何时/何地删除事物。

10-07 20:40