简体   繁体   English

为什么std :: allocator :: deallocate需要一个大小?

[英]why does std::allocator::deallocate require a size?

std::allocator is an abstraction over the underlying memory model, which wraps the functionality of calling new and delete . std::allocator是基础内存模型的抽象,它包装了调用newdelete的功能。 delete doesn't need a size though, but deallocate() requires it. delete不需要大小,但是deallocate() 需要它。

void deallocate( T* p, std::size_t n ); 无效的deallocate(T * p,std :: size_t n);
"The argument n must be equal to the first argument of the call to allocate() that originally produced p; otherwise, the behavior is undefined." “参数n必须等于最初产生p的对allocate()的调用的第一个参数;否则,行为是不确定的。”

Why? 为什么?

Now I either have to make additional calculations before deallocating, or start storing the sizes that I passed to allocate. 现在,我必须在取消分配之前进行其他计算,或者开始存储传递给分配的大小。 If I didn't use the allocator I wouldn't have to do this. 如果我不使用分配器,则不必这样做。

The design of the std::allocator API - the Allocator concept - is to facilitate the job of potential replacements. std::allocator API( Allocator概念 )的设计是为了简化潜​​在的替换工作。

std::allocator is an abstraction over the underlying memory model std::allocator是基础内存模型的抽象

It doesn't have to be! 不一定是! In general, an allocator does not need to use C malloc and free , nor delete or the not-in-place new . 通常,分配器不需要使用C mallocfree ,也不需要delete或就位new Yes, the default one usually does it, but the allocator mechanism isn't merely an abstraction over C's memory model. 是的, 默认情况下通常会这样做,但是分配器机制不仅仅是对C内存模型的抽象。 To be different is often the whole purpose of a custom allocator. 与众不同通常是自定义分配器的全部目的。 Remember that allocators are replaceable: a particular std::allocator might not need the size for deallocation, but any replacements are likely to. 请记住,分配器是可替换的:特定的std::allocator可能不需要释放大小,但任何替换都可能。

A compliant implementation of std::allocator is free to assert that you indeed pass the correct n to deallocate , and to otherwise depend on the size being correct. 符合标准的std::allocator可以自由声明您确实传递了正确的n来进行deallocate ,否则可以依赖于正确的大小。

It happens that malloc and free store the chunk size in its data structures. 碰巧mallocfree将块大小存储在其数据结构中。 But in general an allocator might not do it, and requiring it to do so is premature pessimization. 但是总的来说,分配器可能不会这样做,而要求这样做是过早的悲观化。 Suppose you had a custom pool allocator and were allocating chunks of int s. 假设您有一个自定义池分配器,并且正在分配int的块。 On a typical 64-bit system it'd be a 200% overhead to store a 64-bit size_t along with the 32-bit int . 在典型的64位系统上,存储64位size_t和32位int会产生200%的开销。 The user of the allocator is much better positioned to either store the size along in the allocation, or to determine the size in a cheaper fashion. 分配器的用户可以更好地定位存储大小,或者以更便宜的方式确定大小。

Good malloc implementations don't store allocation size for every small allocation; 好的malloc实现不会为每个小分配存储分配大小。 they and are able to derive the chunk size from the pointer itself eg by deriving a block pointer from the chunk pointer, and then inspecting the block header for the chunk size. 它们并能够从指针本身得出块大小,例如,通过从块指针中得出块指针,然后检查块头中的块大小。 That's but a detail of course. 那当然是一个细节。 You could obtain the lower bound on the size using platform-specific APIs, such as malloc_size on OS X, _msize on Windows, malloc_usable_size on Linux. 你可以得到下界使用特定平台的API,如大小malloc_size在OS X上, _msize在Windows上, malloc_usable_size在Linux上。

It is often useful for memory-allocation algorithms to minimize the amount of overhead they require. 对于内存分配算法来说,使它们所需的开销最小化通常很有用。 Some algorithms which keep track of free areas rather than allocated areas can reduce the total amount of overhead to a low constant value with zero per-block overhead (book-keeping information is stored entirely within the free areas). 某些跟踪空闲区域而不是已分配区域的算法可以将开销总量减少到一个低恒定值,每块开销为零(簿记信息完全存储在空闲区域内)。 On systems using such algorithms, an allocation request removes storage from the free pool, and a de-allocation request adds storage to the free pool. 在使用这种算法的系统上,分配请求从空闲池中删除存储,而解除分配请求将存储添加到空闲池中。

If allocation requests for 256 and 768 bytes get satisfied using a contiguous region of the pool, the memory-manager state would be identical to what it would be if two requests for 512 bytes had been satisfied using that same region. 如果使用池的连续区域满足了对256和768字节的分配请求,则内存管理器状态将与使用同一区域满足两个对512字节的请求的情况相同。 If the memory manager were passed a pointer to the first block and asked to release it, it would have no way of knowing whether the first request had been for 256 bytes, or 512 bytes, or any other number, and thus no way of knowing how much memory should be added back to the pool. 如果向内存管理器传递了指向第一个块的指针并要求释放它,则它将无法知道第一个请求是用于256字节,512字节还是任何其他数字,因此也就无法知道应该将多少内存添加回池中。

Implementing "malloc" and "free" on such a system would require that it store the length of each block at the beginning of its region of storage and return a pointer to the next suitably-aligned address that would be available after that length. 在这样的系统上实现“ malloc”和“ free”将要求它在存储区的开头存储每个块的长度,并返回一个指针,指向在该长度之后可用的下一个适当对齐的地址。 It's certainly possible for an implementation to do that, but it would add 4-8 bytes of overhead to each allocation. 实现当然可以做到这一点,但是它将为每个分配增加4-8字节的开销。 If the caller can tell the deallocation routine how much storage to add back to the memory pool, such overhead can be eliminated. 如果调用方可以告诉释放例程将多少存储添加回内存池,则可以消除此类开销。

Furthermore, it is quite easy to accommodate this design point: simply allocate things as a struct , and store the size as an element within that struct . 此外,很容易适应这个设计要点:只需将事物分配为struct ,并将大小存储为该struct内的元素。 Your code that calls the deallocator now knows what value to provide, since the structure itself contains it. 您的调用deallocator的代码现在知道要提供什么值,因为结构本身包含它。

By doing this, you're essentially doing exactly what any other language-implementation might be graciously doing for you. 这样,您实际上就是在完成任何其他语言实现可能正在您做事情。 You're just doing the same thing explicitly. 您只是在明确地做同样的事情。

Now, given that we're talking about C++ , which has oodles of great container-classes already built in, I would candidly encourage you to avoid "rolling your own" if you can possibly avoid it. 现在,考虑到我们正在谈论的是C ++ ,它已经内置了许多优秀的容器类,因此,如果可以避免的话,我谨鼓励您避免“自己动手”。 Just find a way to use one of the nifty container-classes that the language and the standard library already provide. 只要找到一种使用该语言和标准库已经提供的漂亮容器类的方法即可。

Otherwise, be sure to package what you're building here as a home-grown container class. 否则,请确保将您在此处构建的内容打包为本地容器​​类。 Be sure that the logic which deals with the allocator and the deallocator occurs only once in your program. 确保处理分配器和取消分配器的逻辑在程序中仅出现一次。 (Namely, within this class.) Sprinkle it generously with logic that is specifically designed to detect bugs. (即,在此类之内。)大量使用专门设计用于检测错误的逻辑。 (For instance, a sentinel value that is inserted into an object when it is allocated, and that must be found when the object is deallocated, and which is wiped-out just before it is. Explicit checks of the stored size-value to make sure that it makes sense. And so on.) (例如,一个哨兵值,它在分配对象时插入到对象中,并且必须在释放对象时找到,并且在对象被抹除之前就被删除。显式检查要存储的size-value确保有道理。等等。)

You're not required to keep track of the size. 不需要跟踪大小。 The standard allocator does not keep track of the size because it assumes a size for all allocations. 标准分配器不跟踪大小,因为它假定所有分配的大小。 Of course, there are different types of allocators for different purposes. 当然,出于不同的目的有不同类型的分配器。 A block size allocator, as you may have guessed, has a fixed size. 您可能已经猜到,块大小分配器的大小是固定的。 Some applications like video games pre-allocate memory all upfront and eliminate the overhead of needing to keep track of the size for each allocation. 诸如视频游戏之类的某些应用程序会预先预先分配内存,从而消除了需要跟踪每次分配大小的开销。

The standard library tries to be as generic as possible. 标准库尝试尽可能通用 Some allocators need to keep track of the size, others do not, but all allocators are required to conform to the interface. 一些分配器需要跟踪大小,而其他则不需要,但是所有分配器都必须符合接口。

我没有确凿的证据,但是我的直觉是不需要使用C ++运算符new / delete分配器,并且不妨使用不具有分配数组和了解其大小的能力的内存管理例程-例如malloc,例如。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM