2013-07-02 40 views
1

我目前正在研究一个带有SFML的2D游戏引擎,并且从程序上开始,但是决定从长远来看将它转移到OOP会更好。然而,我理解OOP的概念,我并不确定我应该在哪里定义其他类的实例(ImageManager,Grid等)。在哪里实例化依赖于主类的类?

例如,我的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 
}; 

所以基本上,你可以看到我声明了两个班的头,ImageManagerGridClass。 Inside 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中的许多函数都依赖于这些类的这些实例中的变量,并且我并不想将大量参数传递给每个函数。

谢谢。

+1

我认为你应该使imageManager和gridClass专用,以缩小可以改变它们的范围。你可能想考虑把它们放在一个智能指针中。 – doctorlove

回答

1

从您的类内部实例化成员对象使您的代码更加模块化/灵活。

想象一下,你有你的ImageManager类的各种实现方式(例如,你可能希望有一个MockImageManager时,单元测试你Engine,或者你可能想试试你Engine与各种第三方ImageManager),可以使唯一途径您的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; 
} 

这样,所有你必须改变以提供一个新的impl ImageManager(假设ImageManager的接口由虚拟呼叫组成)将编码为MyRevolutionaryImageManager(将从ImageManager得出),并将主要呼叫std::make_shared更改为std::make_shared<MyRevolitionaryImageManager>()。你甚至不需要重新编译Engine,新的实现将被使用(虚拟的美妙之处,允许旧代码调用新代码)!

现在当然如果ImageManager和/或GridClassEngine只有实现细节,如果没有需要有灵活性,那么它很公平,从Engine中实例化它们。

不需要使用shared_ptr如果需要,您可以使用普通指针,但这时您需要考虑何时/在哪里删除东西。

+0

接受的答案,因为它是最详细的 – Anteara

1

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

简短的回答是,它可以在类似这样的其他类中的类。在构造函数中实例化这些没有任何问题。构造函数的目的是初始化该类的一个对象需要开始工作的所有东西,所以如果您认为Engine需要imageManager才能正常运行,那么在构造函数中实例化它就没问题。

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

engine.imageManager.somefunction() 

这可以变得非常混乱和令人费解的,如果你需要访问一些嵌套深跌,但只要你不嵌套太多的对象像这个,你应该没问题。如果您认为您需要在引擎外部使用imageManager,最好单独实例化imageManager,并在Engine中设置可容纳所需值的私有变量。然后,您可以在Engine中创建一些公共职能,从imageManager接收任何更新信息并相应地更新变量。希望这可以帮助!

+0

谢谢,这确实有帮助:) – Anteara

1

我认为这样做是完全可以接受的。进入Engine::Engine()正文后,Engine的内存将被分配。那么没有可能破坏imageManager

当所有的成员分配在构造函数中,虽然,它变得非常相似,使得它们的Engine部分(称为"Composition"),即

ImageManager imageManager; 
GridClass gridClass; 

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

+0

谢谢,这是有帮助的:) – Anteara