C++分配器和内存池的所有权

14

我有一些困惑。假设我有一个任意的C++分配器 - 就像这样:

template<class T>
struct my_allocator
{
    template<class Other>
    struct rebind { typedef my_allocator<Other> other; };

    // [other members here]
};

现在考虑以下代码(请阅读注释):

typedef my_allocator<int> Alloc;
Alloc alloc = get_my_allocator();  // assume this works properly

long *const p = Alloc::rebind<long>::other(alloc).allocate(1, NULL);
// Notice that the rebound allocator for 'long' is now destroyed

// Can a NEW rebound allocator for 'long' deallocate the memory from the old one?
Alloc::rebind<long>::other(alloc).deallocate(p, 1);
// i.e., does the 'int' allocator 'alloc' keep alive the 'long' memory pool too?

在哪个时刻可以释放后备存储池?

或者换句话说:哪个分配器共享哪个内存池的所有权

我一直认为同一值类型的分配器共享其自己的内存池,但现在我想到它们还可能共享所有重新绑定分配器背后的内存池,即使它们管理完全不同类型。

必须让重新绑定类型的分配器“保持活动”,直到它们全部被销毁吗?

如果 C++03 和 C++11 的答案不同,请解释两者之间的区别。


@Downvoter:我希望上帝知道你在点击那个按钮时脑海中在想些什么,因为我真的不知道! - user541686
你在谈论内存池和分配器,但缺乏上下文,让我有些难以理解。稍微多提供一点背景信息可能会使更多人受益。顺便说一句,我不是那个给你点踩的人。 - R Sahu
1个回答

11

对于重新绑定类型的分配器,它们是否需要保留彼此的内存池直至被全部销毁?

简短地回答是需要的,但需注意一些限制。下面详细说明:


C++11(和C++14)中规定[allocator.requirements],从一个my_allocator<int>构造出的my_allocator<long>必须能够删除从my_allocator<int>分配的内存。这在分配器需求表中表达为:

X a(b);

具有以下后置条件:

Y(a) == b and a == X(b)

正如您所知,operator==用于表示:相等的两个分配器可以释放对方分配的指针。此外,表格记录了b是类型为Y的对象,其中XY是通过rebind相关的分配器,就像上面所示。

但仅凭这些还无法回答您的确切问题,在您的问题中,my_allocator<int>实际上从未进行任何分配。然而,该表中的另一行继续讲述了关于分配器operator==的以下内容:

a1 == a2

后置条件:

仅当从每个中分配的存储可以通过另一个进行释放时,才返回true。operator==应该是自反的、对称的和传递的,并且不应该通过异常退出。

(强调是我的)

传递意味着如果a1 == a2,并且a2 == a3,则暗示a1 == a3。

这个细节解决了你的问题。第一个临时的my_allocator<long>是从alloc复制而来,因此等于alloc

第二个临时的my_allocator<long>也是从alloc复制而来,因此也等于alloc。而且,由于传递性质,这两个临时的my_allocator<long>也必须相互相等。

这并不完全意味着它们都必须共享同一内存池。但它确实意味着这三个分配器必须能够以某种方式释放彼此分配的指针。也就是说,你的例子需要工作。

C++03缺乏“传递”要求。尽管如此,将“传递”添加到C++11措辞中被认为只是对C++03意图的“清理”,而不是新要求。因此,语言律师可以争论C++98/03是否需要传递,但从实际角度来看,代码最好假设它是必需的,因为这是意图。

事实上,C++98/03还包括这个“獾”的措辞(在C++11/14中不再使用):

给定分配器类型的所有实例都需要可互换,并且始终相互比较相等。

也就是说,C++98/03容器允许假设所有实例(实际上甚至是重新绑定的实例)始终相等。对“有状态”的分配器的官方支持直到C++11才真正开始。


顺便问一下,那真的应该是“只有当”,还是应该是“当且仅当”? - user541686
@Mehrdad:这是标准中的复制/粘贴引用。乍一看,我不确定改为“当且仅当”会增加什么。但如果它确实有所添加,可以按照以下说明进行操作:http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#submit_issue - Howard Hinnant
谢谢提供链接。我再想了想,意识到你是对的,因为之前的后置条件已经确保了if部分的处理。:) 再次感谢。 - user541686
语法元变量 a1a2 指的是相同分配器类型的值,因此您引用的要求并未为具有不同类型的分配器定义 operator==。这将是 "a == b:与 a == Y::rebind<T>::other(b) 相同" 的要求。换句话说,不需要能够释放相同内存分配的两个不同类型的分配器,但从特定分配器实例构造的所有相同重新绑定类型的分配器 必须 相等比较。 - Casey
@Casey:确实如此。我试图让我的答案简洁明了。使用X释放Y::pointer甚至无法编译通过。但是,如果它们相等,则在重新分配步骤之前将X重新绑定到Y(如OP中所示)是必需的才能正常工作。 - Howard Hinnant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接