【问题标题】:How does C++ placement delete work internally (C++ runtime)? How to overcome its limitation?C++ 放置删除如何在内部工作(C++ 运行时)?如何克服它的局限?
【发布时间】:2019-10-16 14:58:39
【问题描述】:

C++ 对放置新和放置删除是不对称的。我们被允许以几乎任意的方式重载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++ 运行时如何做到这一点?

【问题讨论】:

  • 你可以使用“智能指针”吗?
  • 您的评论无关紧要。
  • 如果你在做T* p = new (memory) T();,你不应该在做p->~T();吗?什么是“展示位置delete”?
  • 你不需要placement new:它只是调用析构函数,你可以直接这样做。因此,没有理由调用它。等等,您是否覆盖放置新行为?这甚至允许吗?这绝对不是一个好主意...
  • @MooingDuck new 调用的分配函数是可替换的,因此用户可以控制分配(和/或调试)

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


【解决方案1】:

查了一下:您应该提供一个 Node::destroy 来摆脱节点并将其返回到您的本地堆。

如果这些是通用的 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 中特定于供应商的废话。

【讨论】:

  • 不可能限制用户写delete ptr; 而不是调用destroy 所以也许要做的事情是用所谓的“花哨的技巧”实现delete ptr; 的预期行为" 并在调用者知道策略时提供此选项作为优化。
  • @M.M:我只做过new (size_t, int),没有类型,所以我无法使用。直接致电delete 是一场等待发生的事故。
【解决方案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:如果已定义,则由具有匹配签名的自定义单对象放置新表达式调用。如果定义了特定于类的版本,则优先调用它。如果用户都没有提供,则placement new 表达式格式不正确。
  • operator new[]: 同上,但对于数组形式。
  • operator delete:如果已定义,如果对象的构造函数抛出异常,则由具有匹配签名的自定义单对象放置 new 表达式调用。如果定义了特定于类的版本,则优先调用它。如果用户都没有提供,则不调用释放函数。
  • operator delete[]: 同上,但对于数组形式。

回答您的问题:

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

  2. 1234563同时具有相同的值。一种天真的方法可能是让每个池管理一个不重叠的地址范围并针对每个池的地址范围测试指针。或者,也许您可​​以在指向位置之前将元数据存储在内存中,例如new 版本将分配 N+16 个字节,将元数据存储在前 16 个字节中,并向用户返回指向块第 16 个字节的指针)。或者,您可以保留一个数据结构,将每个活动指针与元数据相关联。
  3. 用于分配类类型对象的 new 表达式的评估将类似于:

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

【讨论】:

  • 感谢您的详细回复。 1)你能指出一个不需要释放函数的实际例子吗?如果编译器可以在编译时出错以防止潜在的内存泄漏,这不是更安全吗? 2)存储元数据更实用,被许多项目采用。然而,这依赖于一个假设,即所有不同的内存池都由同一个管理库管理。这可能不是正确的情况。假设我有两个第三方内存管理库 A 和 B。将 B 分配的内存地址传递给库 A 可能会损坏内存。
  • 3) 我了解机制。但我试图弄清楚 C++ 是如何完成第三点的:“如果类构造函数抛出,并且存在匹配的运算符 delete,则调用该函数,并传递参数。”它如何测试匹配的 operator delete 是否存在?我猜 C++ 编译器会在编译时测试这个? (无论如何我都想不出在运行时对此进行测试)。如果是这样,那就回到我的问题一。我认为如果匹配的运算符 delete 不存在,在编译时出错可能会更好......
  • @OliverYoung (1) 某些任务可能只会分配而永远不会退出,或者在退出时释放进程内存的操作系统上运行。 (2) 您可以在元数据中包含库 ID,(3) 由编译器/链接器决定这些函数是否存在。 C++ 不牵你的手,这是一个高级主题,如果你正在编写内存分配器,那么你应该能够提供匹配的签名并测试你的代码。
  • (1) 对于这种用法,C++ 设计可以要求用户提供无操作操作符删除。事实上,这就是 C++ 标准放置删除所做的,不是吗? (void* operator delete(void*, void*) )。如果不存在匹配的 operator delete 则在编译时出错。
  • @OliverYoung operator delete(void*, void*)(cppreference 中的第 9 项)始终存在,编译器提供一个并且用户可以选择替换它。这与用户定义的放置(取消)分配不同,因为您为分配版本制作了所需的签名,所以没有提供任何内容。我不明白您所说的“统一解除分配机制”是什么意思。如果您建议编译器应该“记住”为每个指针提供给new 的参数,那么这将对不需要的用户造成巨大的性能损失。
猜你喜欢
  • 2021-12-02
  • 1970-01-01
  • 1970-01-01
  • 2013-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-27
相关资源
最近更新 更多