我可以使用 std::stack 作为对象池容器吗?
我需要创建一个对象池来消除动态分配。使用 std::stack 包含分配对象的指针是否有效?
我怀疑每次将释放的对象推回到堆栈时,都会动态分配一个新的堆栈元素。我说得对吗?我应该使用 std::vector 来确保没有分配任何新内容吗?
I need to create a pool of objects to eliminate dynamic allocations. Is it efficient to use std::stack to contain pointers of allocated objects?
I suspect every time I push the released object back to stack a new stack element will be dynamically allocated. Am I right? Should I use std::vector to make sure nothing new is allocated?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
堆栈是否适合您的特定目的是我不会处理的问题。现在,如果您关心分配的数量,则 std::stack 的默认内部容器是 std::deque<>。它不需要在每次推送中为堆栈分配新的内存(只要它有空间),并且在分配时不需要将所有现有元素重新定位为 std::vector<> 。会。
您可以告诉堆栈使用 std::vector<>作为具有第二个模板参数的底层容器:
Whether a stack is suited for your particular purpose or not is an issue I will not deal with. Now, if you are concerned about the number of allocations, the default internal container for a std::stack is an std::deque<>. It will not need to allocate new memory for the stack in each push (as long as it has space) and when it allocates it does not need to relocate all existing elements as an std::vector<> would.
You can tell the stack to use an std::vector<> as underlying container with the second template argument:
指针的 STL 容器不会对它们指向的对象执行任何操作,这取决于您,因此您有责任不泄漏任何内存等。请查看 Boost Pointer Container Library 或尝试存储实际对象,从长远来看,您会省去麻烦。
如果您想减少容器进行的动态分配量,并且您大致知道需要存储多少对象,则可以使用向量的“reserve()”方法,该方法将预先分配您的内存一次性请求。
您还可以在构造函数中指定所需的记录数,但这种方式将为您创建 x 个对象,然后存储它们,这可能不是您想要的。
如果由于某些技术原因动态分配完全消失,您可能需要尝试使用 boost::pool 作为您的分配器(如您所知,如果您不想使用默认的分配器,可以指定不同的 std 库内存分配器)。
也就是说,当我测试它时,默认的总是更快,至少对于 g++ 来说是这样,所以可能不值得付出努力。确保您对其进行了概要分析,而不是假设您可以向标准委员会编写代码!
STL containers of pointers don't do anything with the objects they point to, that's up to you, so you are responsible for not leaking any memory etc. Have a look at Boost Pointer Container Library or try storing the actual objects, you will save yourself hassle in the long run.
If you want to reduce the amount of dynamic allocations made by the container and you know roughly how many objects you need to store, you can use vector's 'reserve()' method, which will preallocate the memory you request in one shot.
You can also specify the number of records you want in the constructor, but this way will create x objects for you and then store them, which might not be want you want.
If, for some technical reason dynamic allocation is out completely, you might want to try using boost::pool as your allocator, (as you know you can specify a different std library memory allocator if you don't want to use the default one).
That said, when I tested it, the default one was always faster, at least with g++ so it may not be worth the effort. Make sure you profile it rather than assume you can out code the standards commitee!
由于无抛出保证,在空闲期间进行任何分配都是错误。如果您必须进行分配才能进行释放,并且分配会抛出异常,那么您将指针放在哪里?您可以悄悄地捕获异常并泄漏,或者传播异常。传播异常意味着使用
YourObject
的对象无法放入 STL 容器中。泄漏就是泄漏。无论哪种情况,你都违反了规则。但是使用什么数据结构取决于您的对象生命周期控制习惯。
该习惯用法是与工厂方法和 freeInstance 一起使用的对象池,
还是与特定于类的运算符 new/operator delete(或分配器)一起使用的内存池?
如果它是一个对象池,并且您了解已发布代码和双端队列中 YourObject* 使用向量的数量,或者最好是循环缓冲区(因为双端队列没有保留,所以您必须将其添加为动态自调整大小的循环) buffer 正是您想要的)在调试代码中并保留大致数量。在发布中分配 LIFO,在调试中分配 FIFO,以便您拥有调试历史记录。
在没有空闲对象的路径中,请记住在动态创建之前对
YourObject*
集合执行reserve(nMade+1)
一个物体。(此保留的原因有两个。首先,它必须在
createInstance
时完成。其次,它简化了代码。否则,您有可能在中抛出 std::badalloc freeInstance 可能会使析构函数保证难以保证,例如 - 类 Y 中有一个
YourObject*
并执行一个freeInstance
。 > 对于其析构函数中的YourObject*
- 如果您在创建时没有为YourObject
* 保留空间,则将该指针存储在freeInstance 的位置
时间?如果您之后在getInstance
中保留空间,那么您必须捕获 std::badalloc 进行保留,释放刚刚创建的YourObject
,然后 <代码>重新抛出。)如果它是一个内存池,那么在内存块中,在释放中使用侵入式单链表,在调试中使用双向链表(我假设
sizeof(YourObject)>=2 *sizeof(void*)
)顺便说一句,有很多 MemoryPool 实现。再次在发布中分配 LIFO,在调试中分配 FIFO,以便您有调试历史记录。顺便说一句,如果您使用工厂习惯用法,请不要跳过重载的 getIntances() 并添加 reinit 方法。这只是开启了省略 reinit 调用的可能性。 getInstance 方法是您的“构造函数”,因为它们使对象达到您想要的状态。请注意,在对象池的情况下,您需要一个 freeInstance,它可能必须对对象执行“类似析构函数”的操作。
在这种情况下,谈论“公共类不变量”和“私有类不变量”是有意义的 - 对象处于不确定状态,其中公共类不变量在自由池中可能不得到满足。它是一个
YourObject
,就像一个YourObject
一样,但所有公共类不变量可能都不会得到满足。 YourObject::getInstance 的工作是获取实例并确保满足其公共不变量。以补充方式 freeInstance 释放可能已由 getInstance 获取的资源,以确保满足“公共类不变量”可以在空闲列表上的对象“空闲时间”期间释放。发布中的 LIFO 还具有缓存最后使用的对象/块的显着优势,而 FIFO 则保证在有足够多的对象/块时不会缓存 - 甚至页面< /strong>如果数字更大!但当您决定使用堆栈时,您可能已经意识到这一点。
强文本
Doing ANY allocs during a free is WRONG due to nothrow-guarantees. If you have to do an alloc to do a free and the alloc throws where do you put the pointer? You either quietly capture the exception and leak or propagate the exception. Propagating the exception means objects that use your
YourObject
cant be put in STL containers. And leaking is, well, leaking. In either case you have violated the rules.But what data structure to use depends on your object lifetime control idiom.
Is the idiom an object pool to be used with a factory method(s) and freeInstance
or a memory pool to be used with a class specific operator new/operator delete (or an allocator)?
If it is an object pool and you have an idea about the number of YourObject*'s use vector in released code and deque or preferably a circular buffer (as deque has no reserve so you have to add this where as a dynamically self sizing circular buffer is precisely what you want) in debug code and reserve the approximate number. Allocate LIFO in release and FIFO in debug so you have history in debug.
In the path where there are no free objects, remember to do the
reserve(nMade+1)
on theYourObject*
collection before you dynamically create an object.(The reason for this reserve is two fold. First, it must be done at
createInstance
time Second, it simplifies the code. For otherwise you have the possibility of throwing a std::badalloc infreeInstance
which may make destructor guarantees hard to guarantee. OUCH! e.g. - class Y has anYourObject*
in it and does afreeInstance
for thatYourObject*
in its destructor - if you don't reserve the space for theYourObject
* when you make it where do you store that pointer atfreeInstance
time? If you reserve the space afterwards ingetInstance
then you have to catch the std::badalloc for the reserve, release the just madeYourObject
, andrethrow
.)If it is a memory pool then in the memory blocks use an intrusive singly linked list in release and doubly linked list in debug (I am assuming that
sizeof(YourObject)>=2*sizeof(void*)
) BTW there are a lot of MemoryPool implementations out there. Again allocate LIFO in release and FIFO in debug so you have history in debug.BTW if you use the factory idiom don't skip on the overloaded getIntances() and add reinit methods. That just opens up the possibility of leaving out a reinit call. The getInstance methods are your "constructors" in the sense that it is they that get the object to the state that you want. Note that in the object pool case you need a freeInstance which may have to do "destructor like" things to the object.
In this case it makes some sense to speak of "public class invariants" and "private class invariants" - the object sits in a limbo state where public class invariants may NOT be satisfied while in the free pool. Its a
YourObject
as fas as aYourObject
is concerned but all of the public class invariants may not be satisfied. It is the job of YourObject::getInstance to both get an instance AND ensure that its public invariants are satisfied. In a complementary fashion freeInstance releases resources that may have been acquired by getInstance to ensure the "public class invariants" were satisfied may be released during the objects "idle time" on the free list.LIFO in release also has the SIGNIFICANT benefit of caching the last used objects/blocks where as FIFO is guaranteed not to cache if there are a sufficiently large number of objects/blocks - or even page if the number is larger! But you probably already realized this as you decided to use a stack.
strong text