【问题标题】:C++ allocator and memory pool ownershipC++ 分配器和内存池所有权
【发布时间】:2014-09-08 07:38:21
【问题描述】:

我对某事感到困惑。假设我有一个任意的 C++ 分配器——比如说,像这样:

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

    // [other members here]
};

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

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?

究竟在什么时候可以释放后备存储池?

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

我一直认为 - 没有太多考虑 - 相同值类型的分配器共享他们自己的内存池的所有权,但现在我想到了他们也可以共享所有 rebound 分配器后面的内存池的所有权,即使它们管理完全不同的类型。

反弹类型的分配器必须“保持活跃”彼此的内存池直到所有它们都被销毁?

如果 C++03 和 C++11 的答案不同,请说明两者以及它们之间的区别。

【问题讨论】:

  • @Downvoter:我希望上帝知道当你点击那个按钮时你在想什么,因为我肯定不知道!
  • 您在谈论没有上下文的内存池和分配器。它肯定超出了我的想象。只是一点点额外的背景可能会使更广泛的受众受益。 PS。我不是反对者。

标签: c++ c++11 memory-management allocator


【解决方案1】:

反弹类型的分配器是否必须“保持活跃”彼此的内存池,直到它们全部被销毁?

简短的回答是肯定的,尽管有一些警告。长答案如下...


C++11(和 C++14)说 [allocator.requirements] 从 my_allocator&lt;int&gt; 构造的 my_allocator&lt;long&gt; 必须能够删除从 my_allocator&lt;int&gt; 分配的内存。这在分配器要求表中表示为:

X a(b);

后置条件为:

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

如你所知,operator== 在这里用来表示:两个相等的分配器可以释放彼此分配的指针。表格还记录了bY 类型的对象,其中XY 是与rebind 相关的分配器,如上所示。

现在,仅此一项并不能确定您的确切问题,因为您的问题my_allocator&lt;int&gt; 从未实际分配任何东西。然而,同一张表中的另一行继续说明分配器的运算符==:

a1 == a2

后置条件:

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

(重点是我自己的)

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

这个细节说明了你的问题。第一个临时的my_allocator&lt;long&gt; 是从alloc 复制而来的,因此等于alloc

第二个临时my_allocator&lt;long&gt; 也是从alloc 复制而来,因此也等于alloc。此外,由于具有传递性,两个临时的my_allocator&lt;long&gt; 也必须彼此相等。

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

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

确实,C++98/03 也包含了这种“鼬鼠”的措辞(C++11/14 中不再存在):

给定分配器类型的所有实例都必须是 可互换且始终相互比较。

即允许 C++98/03 容器假设所有实例(实际上甚至反弹实例)总是相等。直到 C++11 才真正开始对“有状态”分配器的官方支持。

【讨论】:

  • 顺便说一句,这真的应该是“仅当”,还是应该是“当且仅当”?
  • @Mehrdad:这是标准的复制/粘贴引用。乍一看,我不确定更改为“当且仅当”会添加什么。但如果它确实添加了一些东西,可以按照以下说明进行操作:open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#submit_issue
  • 感谢您的链接。想多了我才意识到你是对的,它没有添加任何东西,因为早期的后置条件确保了 if 部分得到照顾。 :) 再次感谢。
  • 句法元变量 a1a2 指的是 same 分配器类型的值,因此您引用的要求没有为分配器定义 operator==不同的类型。那将是“a == b: 与a == Y::rebind&lt;T&gt;::other(b)”要求相同。换句话说,不同类型的两个分配器不需要能够释放相同的内存分配,但是从特定分配器实例构造的相同反弹类型的所有分配器必须比较相等。
  • @Casey:确实如此。我试图让我的回答足够简短,以便于理解。用X 释放Y::pointer 甚至不会编译。但如果它们相等,则需要在重新分配步骤(如 OP 中所示)之前将 X 重新绑定到 Y 才能工作。
猜你喜欢
  • 1970-01-01
  • 2017-07-30
  • 2012-02-22
  • 2011-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-17
  • 1970-01-01
相关资源
最近更新 更多