我目前正在使用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 中声明了两个类
ImageManager
和GridClass
。在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
,您可以根据需要使用普通指针,但是您需要考虑何时/何地删除事物。