C++ 放置删除在内部如何工作(C++ 运行时)?如何克服它的局限性?

How does C++ placement delete work internally (C++ runtime)? How to overcome its limitation?

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

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

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

2) 如果我有多个内存池(由应用程序代码管理)并且我想要不同的 placement new 从不同的池中分配内存,那么根本没有办法支持它,因为无法知道这一事实operator delete 中指针来自哪个内存池? (操作员 delete 只有 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++ 运行时如何实现这一点?

这个答案假设问题是指 user-defined placement allocation functions:

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

user-defined placement deallocation functions:

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

这些函数的行为是:

  • operator new:如果已定义,则由具有匹配签名的自定义 single-object placement new 表达式调用。如果定义了 class-specific 版本,则优先调用它。如果用户均未提供,则放置新表达式为 ill-formed.
  • operator new[]:同上但数组形式。
  • operator delete:如果已定义,则在对象的构造函数抛出异常时,由具有匹配签名的自定义 single-object placement new 表达式调用。如果定义了 class-specific 版本,则优先调用它。如果用户都没有提供,则不会调用释放函数。
  • operator delete[]:同上但数组形式。

回答您的问题:

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

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

  3. 用于分配 class 类型对象的 new-expression 的评估将如下所示:

    • 调用operator new,传递参数
    • 假设成功,调用 class 构造函数(如果分配的是 class 类型)。
    • 如果 class 构造函数抛出异常,并且匹配 operator delete 存在,则调用该函数并传递参数。
    • 现在 new-expression 的评估已经完成,控制 return 到包含 new-expression 的代码,或者进入异常处理机制。

查了一下:你应该提供一个 Node::destroy 来摆脱节点并将它 returns 到你的本地堆。

如果这些是通用的 new 和 for void* 我们将有以下销毁:

template<class T> void destroy (Strategy s, T* ptr)
{
    ptr->~T();
    // return to custom heap
 }

您可以使用花哨的技巧来摆脱策略,例如检查堆 ptr 所在的位置,或者将其存储在 new.

中的负偏移量处

无用的历史答案:

已经很久了,但我记得语法是

delete (strategy) pointer;

这似乎是来自 BC4.5 的特定于供应商的废话。