了解 std::pmr::new_delete_resource

Understanding std::pmr::new_delete_resource

我最近开始研究分配器和 c++17 中引入的新 pmr。

查看 cppreference 上 std::pmr::new_delete_resouece 的定义,我阅读了以下内容:

Returns a pointer to a memory_resource that uses the global operator new and operator delete to allocate memory.

那个“全球”让我有点困惑。这是什么意思?是不是仅仅指像in

这样的算子的正常调用
int* i = new int;
delete i;

因此在堆上分配东西,还是指分配全局变量的静态内存?

在这两种情况下使用这种结构有什么意义?

来自您提到的同一资源:

Returns a pointer p to a static storage duration object of a type derived from std::pmr::memory_resource...

静态存储有什么作用:

static storage duration. The storage for the object is allocated when the program begins and deallocated when the program ends. Only one instance of the object exists. All objects declared at namespace scope (including global namespace) have this storage duration, plus those declared with static or extern. See Non-local variables and Static local variables for details on initialization of objects with this storage duration.

所以根据评论,它调用一个函数(在这种特殊情况下是运算符),returns 静态存储持续时间对象。

我是这样理解上面的,它调用一些运算符new即returns静态存储对象。运算符 new 在正常意义上是 global。所以这不是某些 new 特定于某些 struct/union/class.

operator new/delete可以针对特定类型进行覆盖,这样如果你调用new T,系统会调用专门与T.

关联的内存分配器函数

但是,低级内存分配函数仍然存在。这些函数是 global 函数。因此,当该文本说它正在调用“global operator new”时,它的字面意思是:operator new 函数是全局的,不特定于特定类型。

此函数可以由用户提供,但这会影响所有试图调用它的操作。


And what's the point in both cases to use such structure?

重点是...您想使用全局内存分配函数为容器分配器分配内存。毕竟,这就是 std::allocator 所做的。

PMR 内存资源是一种允许分配机制成为容器类型一部分的方法。所以任何 vector<T, pmr::polymorphic_allocator> 都可以使用任何特定的 pmr::memory_resource 派生的 class 来分配它的内存。

NicolBolas 已经给出了一个非常明确的答案,但是由于关于“静态存储持续时间”的评论似乎有些混乱,我觉得需要澄清一下。

调用 new_delete_resource() 函数 被定义为在 cppreference 上具有以下影响:

Return Value:

Returns a pointer p to a static storage duration object of a type derived from std::pmr::memory_resource, with the following properties:

  • its allocate() function uses ::operator new to allocate memory;
  • its deallocate() function uses ::operator delete to deallocate memory;
  • for any memory_resource r, p->is_equal(r) returns &r == p.

The same value is returned every time this function is called.

(强调我的)

这意味着从该函数返回的 std::pmr::memory_resource 对象具有静态存储持续时间; 不是 allocate() 的调用在静态存储持续时间内运行。

例如,这可以实现为:

namespace std::pmr {
  
  memory_resource* new_delete_resource() {
    static internal_new_delete_resource s_new_delete_resource;
    
    return &s_new_delete_resource;
  }

} // namespace std::pmr

明确一点:以下代码分配具有静态存储持续时间的内存(默认[1]):

auto* p = new_delete_resource()->allocate(...);

相反,上面的代码实际上大致相当于这样写:

auto* p = new char[...];

文档中提到的“全局运算符”是函数 ::operator new::operator delete


[1] 默认情况下,它们在 heap 上运行——尽管 ::operator new::operator delete如果 用户 选择定义这些,则可以覆盖它们。一个程序在法律上被允许定义他们自己的分配机制,如果他们选择的话——在这一点上,实际上,这可能是静态存储持续时间。但是,这样的观点比较深奥;就标准而言,指针的存储持续时间是动态的——而不是明确的静态存储。

那有什么意义呢?

除了适合和有用的 default 分配器(例如 default_resource),这还提供了与其他 memory_resource 的很好的可组合性。例如,pool_resource 可以将其用作上游 memory_resource,用于当池用完分配时。

拥有这样的资源对于与 std::allocator<T> 对称(这意味着便宜的升级路径)以及使 std::pmr::polymorphic_allocator 具有合适的默认值变得非常重要。

为什么 newdelete

newdelete 是 C++ 中的内置工具——因此很容易使用它们并以此为基础。由于全局 ::operator new::operator delete 可以被自定义定义覆盖,这使它成为一个简单的自定义点,可以在任何现有应用程序中无缝工作。此外,它遵循 std::allocator<T> 的现有模式,该模式使用 newdelete.

如果相反,这是 std::malloc/std::free 或其他一些分配函数——那么之前为 ::operator new 定义自定义挂钩的旧用户代码将停止正常运行.这将导致使用 std::allocator<T> 的容器的行为与使用 std::pmr::polymorphic_allocator<T>std::pmr::new_delete_resource 的容器的行为不同——这是不可取的。 (注意: 默认资源是 new_delete_resource,它默认提供这种对称性。