0

我是一个新的、自学成才的程序员,我的程序设计和结构一直存在问题。基本问题是释放通过 API 提供给我的资源(例如,必须使用 glDeleteBuffers 函数释放的 OpenGL 缓冲区等),而不会破坏我系统的封装。让我简要介绍一下我的设置:

首先,我的程序使用了一个名为“Engine”的大型“外观”类,其中包含一些用于图形渲染、音频流、事件处理等的子系统。我设计背后的主要思想之一是每个子系统将作为尽可能封装;OpenGL 调用仅存在于图形渲染系统、音频中的 OpenAL 调用等中。

我还想注意资源分配和释放,因此我没有在程序的其他部分使用“新”创建各种资产(图像、声音等),而是在 Engine 类中设置了一些工厂方法,用于创建对象,将这些对象注册到适当的子系统(例如,将图像对象引用发送到可以正确缓冲/注册到 OpenGL 的图形子系统),并返回指向这些对象的智能指针。

class Engine     //..simplified for sake of example..//
{
private:
    GraphicsSystem graphicsRenderer;
    AudioSystem audioRenderer;
    //..window, events, etc...//

public:
    //ctors.. dtor.. tons of other methods..//

    std::shared_ptr<Image> createImage( /*filepath, size info, other params*/ );

    /*..both of these call equivalent graphics system functions:*/

    bool registerImage( Image & tempImage );    //allocates and registers texture resources..
    bool deregisterImage( Image & tempImage );  //frees those same resources upon object destruction..
};

这是其中一种工厂方法可能看起来像的示例。

std::shared_ptr<Image> Engine::createImage( /*params*/ )
{
    Image * tempImagePtr = new Image( /*params*/ ); //create..

    registerImage( *tempImagePtr ); //register..

    return std::shared_ptr<Image>( tempImagePtr ); //return..
}

但这是我遇到的主要问题:我发现很难释放使用 registerImage 函数分配的资源!我想让代码工作,以便客户端只负责从引擎中检索资产对象,资源获取和管理的细节被抽象出来并在资产对象被破坏时自动处理..

以下是我尝试/考虑过的一些选项:

  • 让客户端(Engine 类的用户)负责在删除对象之前调用适当的注销函数。这个对象很容易预先编码,但确实有点糟糕,并且消除了通过工厂方法使用 RAII 样式对象创建的要点。这并不能帮助我实现让客户简单地“获得资产并忘记它”的目标。

  • 将整个引擎的单个实例的引用传递给每个资产类,并允许资产类使用该引用在其析构函数中调用注销函数。这是我之前尝试过的一个解决方案,只是为了确保我有适当的自动资源清理工作正常。它〜工作〜但我不喜欢让资产访问所有引擎调用的想法;当他们所需要的只是在引擎中注册/注销自己的能力时,为每个资产提供这种访问权限似乎有些过火了!

  • 我试图通过将指向正确注册和注销函数的指针传递给资产对象来“清理我的代码”并纠正这种情况。这样,对象可以在构造时注册自己,并在销毁时取消注册。在我尝试之前,这似乎是一个不错的计划。到目前为止,我很难通过资产工厂方法将引擎的非策略方法传递给资产对象!C++ 语法是一团混乱,我对函数指针没有经验(更不用说分发指向非静态方法的指针了),而且它变得如此令人沮丧,以至于我正在考虑回到草率的、破坏抽象的传递策略对整个 Engine 实例的引用!我尝试使用方法名称,我尝试在方法名称前添加一个“&”,添加一个“this”。方法名称之前的关键字等

这是我的一个图像资产构造函数的样子:

Image( std::string filepath , 
       void (*ptrRegFunc)(unsigned int * tempTextureID, const sf::Image * const tempSourceImage) , 
       void (*ptrDeregFunc)(unsigned int * tempTextureID) );

Image 类包含两个相同类型的指针,起初看起来很好,直到我尝试传递指针......

std::shared_ptr<Image> Engine::createImage( std::string filePath )
{
    Image * tempImagePtr = new Image( filePath, &registerImage, &deregisterImage ); //create and send func ptrs..

    return std::shared_ptr<Image>( tempImagePtr ); //return..
}

当然,这并没有编译,我明白为什么:不清楚引用的是哪个 Engine 对象/实例。但我查过并尝试了所有这些不同的 C++ 语法风格;

&(this->registerImage)

&((*this).registerImage)

没有任何效果,而且语法太可怕了,几乎不值得麻烦!

但现在我觉得我又回到了原点;所以我一直在寻找其他可能性。在我的脑海中,这个想法似乎很简单:我只想让每个 Asset 对象能够使用 Engine 对象注册和取消注册自己。

有没有更好/更简单的方法来实现我的目标,而无需让每个资产访问整个引擎类接口?我已经阅读了一些关于Command Pattern的内容,这听起来可能是我正在寻找的东西,但我不确定如何实现该模式,或者它是否真的是我需要的。当然,我也遇到过Function Objects的概念。它们中的任何一个都可以解决我的问题吗?

无论如何,感谢您在这方面与我交谈,我仍在学习,我知道这对你们中的许多人来说可能是简单的事情。如果还有什么我可以解释我想要什么或我当前的系统正在使用什么,请告诉我,我会尽快更新

4

2 回答 2

1

问题中有很多信息,我不确定我是否正确理解了所有这些信息,但我认为shared_ptr在这种情况下使用 a 处理 RAII 的原则听起来不错。如果我没看错,问题是你想Engine::deregisterImage()在最后一个Image引用被释放时调用,对吧?

您是否尝试过为您shared_ptr的 s 使用自定义删除器?如果您想Engine::deregisterImage()在被销毁之前调用Image,那么您需要某种方式将Engine实例绑定到共享指针。有很多方法可以做到这一点(lambdas, std::bind),但使用仿函数很容易:

struct ImageDeleter
{
    Engine& engine;
    explicit ImageDeleter(Engine& e) : engine(e) {}

    void operator() (Image* img) {
        engine.deregisterImage(*img);
        delete img;
    }
};

然后在您的Engine::createImage()方法中,您将在shared_ptr构造函数中注册删除器:

return shared_ptr<Image>(tempImagePtr, ImageDeleter(*this));

现在,当最后一个引用被释放时,shared_ptr将调用ImageDeleter::operator(),它应该都可以工作。这要求您的Engine实例比它创建的所有图像的寿命都要长(否则您将留下一个悬空引用),但我想无论如何都会如此。

于 2013-11-01T02:40:36.837 回答
1

这有点高水平,但看起来这就是您在设计中所处的阶段。

出于某种原因,我认为您可能会从专门针对资源管理的课程中受益。这个类将处理资源的创建和释放,它还可以提供一种重用资源的方法,比如资源池。

于 2013-11-01T02:30:45.647 回答