我可以使用 std::stack 作为对象池容器吗?

发布于 2024-08-05 22:00:43 字数 133 浏览 2 评论 0原文

我需要创建一个对象池来消除动态分配。使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦明 2024-08-12 22:00:43

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

您可以告诉堆栈使用 std::vector<>作为具有第二个模板参数的底层容器:

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

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:

std::stack< int, std::vector<int> > vector_stack;
悲欢浪云 2024-08-12 22:00:43

指针的 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!

凉宸 2024-08-12 22:00:43

由于无抛出保证,在空闲期间进行任何分配都是错误。如果您必须进行分配才能进行释放,并且分配会抛出异常,那么您将指针放在哪里?您可以悄悄地捕获异常并泄漏,或者传播异常。传播异常意味着使用 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* 使用向量的数量,或者最好是循环缓冲区(因为双端队列没有保留,所以您必须将其添加为动态自调整大小的循环) 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

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

or a memory pool to be used with a class specific operator new/operator delete (or an allocator)?

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

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 the YourObject* 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 in freeInstance which may make destructor guarantees hard to guarantee. OUCH! e.g. - class Y has an YourObject* in it and does a freeInstance for that YourObject* in its destructor - if you don't reserve the space for the YourObject* when you make it where do you store that pointer at freeInstance time? If you reserve the space afterwards in getInstance then you have to catch the std::badalloc for the reserve, release the just made YourObject, and rethrow.)

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 a YourObject 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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文