【问题标题】:Where to instantiate classes dependent on main class?在哪里实例化依赖于主类的类?
【发布时间】:2013-06-30 00:07:39
【问题描述】:

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

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

我所做的是: 引擎.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 中的许多函数都依赖于这些类的这些实例中的变量,我真的不想向每个函数传递大量参数。

谢谢。

【问题讨论】:

  • 我认为您应该将 imageManager 和 gridClass 设为私有,以缩小可以更改它们的范围。您可能还想考虑将它们放入智能指针中。

标签: c++ oop instance instantiation sfml


【解决方案1】:

在您的类中实例化成员对象会降低您的代码的模块化/灵活性。

假设您有各种 ImageManager 类的实现(例如,在对 Engine 进行单元测试时,您可能希望拥有 MockImageManager,或者您可能希望与各种第三方 @987654325 一起试用 Engine @),你可以让你的Engine 使用ImageManager 的一种实现,或者另一种方法是修改Engine 本身的实现。

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

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

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

这样,您只需将其编码为MyRevolutionaryImageManager(派生自ImageManager),并将 main 中的 std::make_shared 调用更改为 std::make_shared&lt;MyRevolitionaryImageManager&gt;()。你甚至不必重新编译Engine,新的实现就会被使用(虚拟的美妙之处在于允许旧代码调用新代码)!

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

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

【讨论】:

  • 接受的答案是最详细的
【解决方案2】:

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

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

ImageManager imageManager;
GridClass gridClass;

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

【讨论】:

  • 谢谢,这很有帮助:)
【解决方案3】:

首先,做好转换工作。 OOP 是一个非常好的游戏制作选择。

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

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

engine.imageManager.somefunction()

如果您需要访问嵌套在深处的东西,这可能会变得非常混乱和令人费解,但只要您不嵌套太多这样的对象,就可以了。如果您认为您需要在引擎之外大量使用imageManager,最好单独实例化imageManager,并在Engine 中使用私有变量来保存您需要的值。然后,您可以在Engine 中创建一些公共函数,这些函数可以接收来自imageManager 的任何更新信息并相应地更新变量。希望这会有所帮助!

【讨论】:

    猜你喜欢
    • 2012-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-10
    • 1970-01-01
    • 1970-01-01
    • 2020-04-26
    • 1970-01-01
    相关资源
    最近更新 更多