std::allocator
是基于底层内存模型的抽象,封装了调用new
和delete
的功能。 delete
没有大小要求,但deallocate()则需要传入大小。
void deallocate( T* p, std::size_t n );
"参数 n 必须等于最初生成 p 的 allocate() 调用的第一个参数;否则行为未定义。"
为什么呢?
现在我要在释放内存之前进行额外的计算,或者开始存储我的分配大小。如果我没有使用allocator,我就不必这样做。
std::allocator
是基于底层内存模型的抽象,封装了调用new
和delete
的功能。 delete
没有大小要求,但deallocate()则需要传入大小。
void deallocate( T* p, std::size_t n );
"参数 n 必须等于最初生成 p 的 allocate() 调用的第一个参数;否则行为未定义。"
为什么呢?
现在我要在释放内存之前进行额外的计算,或者开始存储我的分配大小。如果我没有使用allocator,我就不必这样做。
std::allocator
API的设计 - Allocator
概念 - 的目的是为了方便潜在的替代者。
它不必如此!通常情况下,分配器不需要使用C的
std::allocator
是基础内存模型的抽象。
malloc
和free
,也不需要delete
或非就地的new
。是的,默认的分配器通常会这样做,但分配器机制并不仅仅是C内存模型的抽象。成为不同的东西往往是自定义分配器的全部目的。请记住,分配器是可替换的:特定的std::allocator
可能不需要释放的大小,但任何替代品都可能需要。
std::allocator
的符合实现可以自由断言您确实将正确的n
传递给deallocate
,并且否则依赖于大小正确。
有时候malloc
和free
在其数据结构中存储块大小。但通常情况下,分配器可能不会这样做,要求它这样做是过早的悲观化。假设您有一个自定义池分配器,并且正在分配int
的块。在典型的64位系统上,存储64位size_t
以及32位int
将导致200%的开销。分配器的用户更有优势,可以在分配时将大小一起存储,或以更便宜的方式确定大小。
优秀的malloc实现不会为每个小内存分配都存储分配大小; 它们能够从指针本身中推导出块大小,例如通过从块指针派生块指针,然后检查块头以获取块大小。当然这只是一个细节。您可以使用特定于平台的API获得最小大小,例如OS X上的malloc_size
,Windows上的_msize
,以及Linux上的malloc_usable_size
。
struct
,并将大小存储为该struct
中的一个元素。调用解分配器的代码现在知道要提供什么值,因为结构本身包含它。标准库试图尽可能地通用。有些分配器需要跟踪大小,而有些则不需要,但所有分配器都需要符合接口规范。
std::allocator
是 Allocator
概念 的一个实现。所有这些实现都要求将正确的 n
传递给 deallocate
!它们可以选择忽略它,但是 用户 应该提供它,因为使用 n
是一个实现细节。如果愿意,用户当然可以将大小存储在分配本身中。 - Kuba hasn't forgotten Monican
是固定大小,则无需跟踪它。这是一种实现细节。标准不可能预测所有分配器的实现方式。因此,接口是严格的。 - user6678809n
都无所谓。当然,你不需要为 n
显式地分配存储空间,但如果你说“你不需要跟踪 n
”,那么你就是愚蠢的。是的,你需要跟踪它,但不一定要使用变量或任何其他运行时存储。典型的使用 malloc
的标准分配器不会为所有分配指定任何大小:你需要向 allocate
传递一个正确的 n
。如果它使用 free
而不是 delete[]
,它将需要一个正确的 n
来调用析构函数! - Kuba hasn't forgotten Monicadeallocate
不会调用析构函数。 - Kuba hasn't forgotten Monica我没有确凿的证据,但我的直觉是,分配器不需要使用C++的运算符new/delete,而可以使用没有分配数组并知道其大小能力的内存管理例程 - 例如malloc。
free
函数的实现知道,C++库中的delete []
函数也是独立地知道的。 - Kuba hasn't forgotten Monicaoperator delete
一样,分配器遵循这个新原则。 如果您不相信或需要详细解释原因,请查找演示文稿。 - JDługosz