首页 > 解决方案 > C++ 放置删除如何在内部工作(C++ 运行时)?如何克服它的局限?

问题描述

C++ 是非对称的placement new 和placement delete。我们被允许以几乎任意的方式重载placement new。但是,放置删除函数只能从放置新表达式中调用。特别是,如果对象的构造函数抛出异常,则调用它们。根本无法为应用程序代码调用放置删除。

我有以下困惑和问题需要澄清:

1) 如果没有定义放置删除对应项,为什么 C++ 编译器不能简单地拒绝放置新方法签名?这样做可以帮助消除在这种情况下内存泄漏的可能性。

2)如果我有多个内存池(由应用程序代码管理)并且我想要不同的位置 new 来从不同的池中分配内存,则根本无法支持这一点,因为无法知道指针指向哪个内存池来自操作员删除?(操作员删除只有 void* 信息)。有什么办法可以在 C++ 中完成这个吗?

struct Node {
    void* operator new(size_t size, Strategy s) {
        // Depend on different strategy, allocate memory
        // from different Memory pool
    }

    void operator delete(void* memory, Strategy s) {
        // Return the memory to different Memory pool
        // depends on the Strategy
        // However this delete will only be invoked by
        // c++ runtime if Node constructor throws.
    }

    void operator delete(void* memory, size_t s) {
        // This delete doesn't have any clue about the
        // the strategy. Hence, it can't decide which
        // Memory pool to return to.
    }
}

3) 在placement new 的上下文中,C++ 运行时将使用相同的参数调用placement delete。C++ 运行时如何实现这一点?

标签: c++memory-managementdynamic-memory-allocationdelete-operatorplacement-new

解决方案


这个答案假设问题是指用户定义的布局分配函数

void* operator new  ( std::size_t count, user-defined-args... );
void* operator new[]( std::size_t count, user-defined-args... );

用户定义的布局释放函数

void operator delete  ( void* ptr, args... );
void operator delete[]( void* ptr, args... );

这些函数的行为是:

  • operator new:如果已定义,则由具有匹配签名的自定义单对象放置新表达式调用。如果定义了特定于类的版本,则优先调用它。如果用户都没有提供,则placement new 表达式格式不正确。
  • operator new[]: 同上,但对于数组形式。
  • operator delete:如果已定义,如果对象的构造函数抛出异常,则由具有匹配签名的自定义单对象放置 new 表达式调用。如果定义了特定于类的版本,则优先调用它。如果用户都没有提供,则不调用释放函数。
  • operator delete[]: 同上,但对于数组形式。

要回答您的问题:

  1. 不要为你不使用的东西付费。可能存在不需要释放功能的设置。

  2. 您必须包含一种机制,void *可以使用被删除的指针的值来确定它在哪个内存池中。这必须是可能的,因为您的不同池不能同时返回相同的值。一种天真的方法可能是让每个池管理一个不重叠的地址范围并针对每个池的地址范围测试指针。或者您可以在指向位置之前将元数据存储在内存中,例如,new版本将分配 N+16 个字节,将元数据存储在前 16 个字节中,并向用户返回指向块的第 16 个字节的指针)。或者,您可以保留将每个活动指针与元数据相关联的数据结构。

  3. 用于分配类类型对象的新表达式的评估将类似于:

    • 调用operator new,传递参数
    • 假设成功,调用类构造函数(如果它是一个正在分配的类类型)。
    • 如果类构造函数抛出并且匹配operator delete存在,则调用该函数并传递参数。
    • 现在新表达式的求值完成了,控制返回到包含新表达式的代码,或者返回到异常处理机制。

推荐阅读