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

时间:2019-05-30 23:42:35

标签: c++ memory-management dynamic-memory-allocation delete-operator placement-new

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

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

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

2)如果我有多个内存池(由应用程序代码管理),并且我希望新的放置位置可以从不同的池中分配内存,则由于没有办法知道哪个内存,因此根本无法支持这种情况。池指针来自运算符删除? (操作员删除仅具有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 ++运行时如何做到这一点?

2 个答案:

答案 0 :(得分:2)

此答案假设问题涉及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:如果已定义,则由具有匹配签名的自定义单对象放置新表达式调用。如果定义了特定于类别的版本,则优先于此版本进行调用。如果用户都不提供,则放置新表达式的格式不正确。
  • operator new[]:同上,但用于数组形式。
  • operator delete:如果已定义,则在对象的构造函数引发异常时,由具有匹配签名的自定义单对象放置新表达式调用。如果定义了特定于类别的版本,则优先于此版本进行调用。如果用户都不提供,则不会调用释放函数。
  • operator delete[]:同上,但用于数组形式。

要回答您的问题:

  1. 不支付不使用的费用。可能存在不需要取消分配功能的设置。

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

  3. 对用于分配类类型对象的new表达式的求值将类似于:

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

答案 1 :(得分:1)

已经进行了查找:您应该提供一个Node :: destroy,该节点将摆脱该节点并将其返回到本地堆中。

如果这些是new的通用void*,我们将进行以下销毁:

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

您可以使用一些花哨的技巧来摆脱策略,例如检查堆ptr所在的位置,或将其存储在new中的负偏移处。

无用的历史答案:

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

delete (strategy) pointer;

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