在哪里实例化依赖于主类的类?

时间:2013-07-02 14:48:37

标签: c++ oop instance instantiation sfml

我目前正在开发一款带有SFML的2D游戏引擎,并从程序上开始,但决定从长远来看将它移动到OOP会更好。我理解OOP的概念,但是,我不确定在哪里定义我对其他类的实例化(ImageManager,Grid等)

例如,我的Engine课程取决于ImageManagerEngine中的许多职能依赖于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
};

基本上,你可以看到我在标题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中的许多函数都依赖于这些类的这些实例中的变量,我真的不想将大量参数传递给每个函数。

感谢。

3 个答案:

答案 0 :(得分:1)

首先,做好转换工作。 OOP是游戏制作的最佳选择。

简短的回答是,可以将类放在其他类中。在构造函数中实例化这些并没有错。构造函数的要点是初始化该类对象开始工作所需的所有内容,因此如果您认为Engine需要imageManager才能正常运行,那么在构造函数中实例化它就可以了。

但是,请记住,只要您不需要经常在imageManager函数之外调用Engine,就可以将对象放在另一个对象中。然后你必须使用这样的语法:

engine.imageManager.somefunction()

如果你需要访问内嵌深层的东西,这可能会变得非常混乱和复杂,但只要你没有嵌套太多像这样的对象,你应该没问题。如果您认为您需要在引擎之外使用imageManager,那么最好分别实例化imageManager并在Engine中拥有可以保存值的私有变量你需要的。然后,您可以在Engine中创建一些可以从imageManager接收任何更新信息的公共函数,并相应地更新变量。希望这有帮助!

答案 1 :(得分:1)

我认为按照你的方式去做是完全可以接受的。进入Engine::Engine()主体后,将分配Engine的内存。没有可能破坏imageManager

在构造函数中分配所有成员时,它变得非常类似于使 Engine(称为"Composition")的一部分,即

ImageManager imageManager;
GridClass gridClass;

这样,您不必担心清理内存。 另一方面,你失去了灵活性。您实现它的方式,Engine 有一个 ImageManager"Aggregation"),可以在程序运行时方便地替换它。

答案 2 :(得分:1)

从类中实例化成员对象会使代码变得模块化/灵活。

想象一下,您有ImageManager课程的各种实现(例如,您可能希望在单元测试MockImageManager时获得Engine,或者您可能想要试用Engine ImageManager使用各种第三方Engine),您ImageManager使用Engine或其他实现的唯一方法是修改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; // .... }; 本身的实现。

这可以防止您利用动态调度OOP,特别是C ++(这里的关键字是:继承,接口和虚拟调用)。

你会发现你的代码会变得更灵活,例如:

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

只需复制构造函数中给出的shared_ptr:

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

实例化该批次的方法:

ImageManager

这样,您只需要更改以提供ImageManager的新实现(假设MyRevolutionaryImageManager的接口由虚拟调用组成),则需要将其编码为{{1} (将从ImageManager派生),并将主要std::make_shared调用更改为std::make_shared<MyRevolitionaryImageManager>()。您甚至不必重新编译Engine,将使用新的实现(允许旧代码调用新代码的虚拟之美)!

当然,如果ImageManager和/或GridClass只是Engine的实现细节,并且如果不需要灵活性,那么从{{{}}内部实例化它们是公平的。 {1}}。

如果你愿意,不需要使用Engine你可以使用普通指针,但是你需要考虑删除东西的时间/地点。