【问题标题】:Can I use std::stack as object pool container?我可以使用 std::stack 作为对象池容器吗?
【发布时间】:2009-09-29 09:28:43
【问题描述】:

我需要创建一个对象池来消除动态分配。使用 std::stack 包含已分配对象的指针是否有效?

我怀疑每次我将释放的对象推回堆栈时,都会动态分配一个新的堆栈元素。我对吗?我应该使用 std::vector 来确保没有分配任何新内容吗?

【问题讨论】:

    标签: c++ stl memory-management containers


    【解决方案1】:

    堆栈是否适合您的特定目的是我不会处理的问题。现在,如果您关心分配的数量,std::stack 的默认内部容器是 std::deque。它不需要在每次推送中为堆栈分配新内存(只要它有空间),并且在分配时它不需要像 std::vector 那样重新定位所有现有元素。

    您可以通过第二个模板参数告诉堆栈使用 std::vector 作为底层容器:

    std::stack< int, std::vector<int> > vector_stack;
    

    【讨论】:

      【解决方案2】:

      指针的 STL 容器不会对它们指向的对象做任何事情,这取决于你,所以你有责任不泄漏任何内存等。看看Boost Pointer Container Library 或尝试存储实际的对象,你从长远来看,这将为您省去麻烦。

      如果你想减少容器动态分配的数量并且你大致知道你需要存储多少个对象,你可以使用vector的'reserve()'方法,它会预先分配您一次性请求的内存。

      你也可以在构造函数中指定你想要的记录数,但是这样会为你创建 x 个对象然后存储它们,这可能不是你想要的。

      如果由于某种技术原因动态分配完全失效,您可能想尝试使用boost::pool 作为分配器,(如您所知,如果您不想使用默认一个)。

      也就是说,当我测试它时,默认的总是更快,至少使用 g++ 时可能不值得。确保您对其进行分析,而不是假设您可以编写标准委员会的代码!

      【讨论】:

        【解决方案3】:

        由于 nothrow-guarantees,在空闲期间执行 ANY 分配是 错误。如果你必须做一个 alloc 来做一个 free 并且 alloc 抛出你把指针放在哪里?您要么悄悄地捕获异常并泄漏,要么传播异常。传播异常意味着使用 YourObject 的对象不能放入 STL 容器中。泄漏就是泄漏。无论哪种情况,您都违反了规则。

        但是使用什么数据结构取决于你的对象生命周期控制习惯。

        成语是与工厂方法和freeInstance一起使用的对象池

         YourObject* pO = YourObject::getInstance(... reinitialize parameters ...);
         ......object public lifetime....
         pO->freeInstance();
        

        或与特定于类的运算符 new/operator delete(或分配器)一起使用的内存池?

        YourObject::operator new(size_t);
        ......object public lifetime....
        delete pO;
        

        如果它是一个对象池,并且您知道 YourObject* 在已发布代码和双端队列中的使用向量的数量,或者最好是一个循环缓冲区(因为双端队列没有保留,因此您必须将其添加为动态自我调整大小的循环缓冲区正是您想要的)在调试代码中并保留近似数字。在 release 中分配 LIFO,在 debug 中分配 FIFO,以便您在 debug 中拥有历史记录。

        在没有空闲对象的路径中,记得在动态创建对象之前YourObject* 集合执行reserve(nMade+1)

        (这个保留的原因有两个。首先,它必须在createInstance时间完成第二,它简化了代码。否则你有可能在freeInstance中抛出一个std::badalloc,这可能使析构函数难以保证。哎哟!例如 - Y 类中有一个 YourObject* 并在其析构函数中为 YourObject* 执行 freeInstance - 如果你不保留空间对于YourObject*,当您创建它时,您将指针存储在freeInstance 时间的什么位置?如果您之后在getInstance 中保留空间,那么您必须捕获std::badalloc 进行保留,释放刚刚创建的YourObjectrethrow。)

        如果它是一个内存池,那么在内存块中,在发布时使用侵入式单链表,在调试时使用双链表(我假设 sizeof(YourObject)&gt;=2*sizeof(void*))顺便说一句,那里有很多 MemoryPool 实现。再次在 release 中分配 LIFO,在 debug 中分配 FIFO,以便您在 debug 中有历史记录。

        顺便说一句,如果您使用工厂习语,请不要跳过重载的 getIntances() 并添加 reinit 方法。这只是打开了省略 reinit 调用的可能性。 getInstance 方法是您的“构造函数”,因为正是它们使对象达到您想要的状态。请注意,在对象池的情况下,您需要一个 freeInstance 可能必须对对象执行“类似析构函数”的操作。

        在这种情况下,谈论“公共类不变量”和“私有类不变量”是有道理的 - 对象处于不确定状态,在这种状态下,公共类不变量可能得到满足。免费游泳池。它的YourObjectYourObject 一样重要,但可能无法满足所有公共类不变量。 YourObject::getInstance 的工作是获取实例并确保满足其公共不变量。以互补的方式 freeInstance 释放可能由 getInstance 获取的资源,以确保满足“公共类不变量”,可以在空闲列表上的对象“空闲时间”释放。

        LIFO 的发布还具有缓存最后使用的对象/块的重要优势,如果有足够多的对象/块,FIFO 保证不会缓存 - 甚至 page 如果数字更大!但是当您决定使用堆栈时,您可能已经意识到这一点。

        强文本

        【讨论】:

        • >>由于 nothrow-guarantees,在空闲期间执行任何分配都是错误的。>在发布的代码中使用向量并在调试代码中使用双端队列或者最好是循环缓冲区
        • RE 如何:空闲期间的分配是向量或双端队列是否执行分配 - 如果分配失败,您尝试释放的指针在哪里?我认为“这种保留的原因是双重的”。将问题解释为发生在 Y 析构函数中,该析构函数具有指向池化 RE 的 YourClass 的指针 为什么:在调试模式下,FIFO 分配会留下历史记录以进行调试 LIFO 破坏历史记录
        猜你喜欢
        • 1970-01-01
        • 2015-08-11
        • 1970-01-01
        • 2018-03-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-23
        • 1970-01-01
        相关资源
        最近更新 更多