【问题标题】:How to prevent memory gaps in C++ new/delete?如何防止 C++ new/delete 中的内存间隙?
【发布时间】:2021-03-08 12:10:18
【问题描述】:

本题基于https://arduino.stackexchange.com/questions/81857/what-would-be-the-best-way-of-dynamically-change-instances-regarding-dynamic-m

我有一个基类,并且(可能)有许多派生类。我想为这些派生类提供 4 个实例,但这些实例会发生变化。例如。假设派生类 C1..C20,则删除 C4 的一个实例,并创建一个新实例 C7。

由于内存限制,我无法创建所有派生类的 4 个实例。

我在 Arduino 平台上工作。我可以假设:

  1. 当我delete一个实例和new一个实例,并且派生类没有任何实例变量时,delete第一个实例的内存缺口,会被new创建的实例填充?请注意,派生类有不同的方法(但我认为这只影响 V-table)。
  2. 我可以假设,当实例使用的内存量相同时,这也是正确的吗? (例如,如果基类使用 10 字节,派生实例使用 20 字节,新实例也使用 20 字节,那么它将使用相同的空间吗?

如果情况 2 为真,那么让两个实例保持相同(内存)大小的最佳方法是什么?就像为差异放置一个未使用的字节数组一样,例如:

class C
{ 
   byte a;
}

class C1: C
{
   byte a;
   long b;
}

class C2: C
{
   byte a;
   byte _unused[4];
}

我猜我也必须考虑对齐?

【问题讨论】:

  • 您的_unused 由于对齐而错误,C1 (可能)在ab 之间有填充,而C2a_unused 之间没有填充
  • @Jarod42:是的,所以我想我必须考虑对齐。

标签: c++ memory-management new-operator delete-operator arduino-c++


【解决方案1】:

在这种非常特殊的情况下,您可以使用您想要使用的所有类型的union。这可以保证您始终为最大的类分配足够的内存,而无需任何手工编写的代码。因此,您无需编写 sizeof(largest_type) 的手工版本。

之后,您可以随意使用deletenew at

顺便说一句:这是std::variant 的经典用例。

问题是没有arduino platform! Arduino 可以涵盖从没有任何 STL 支持的 AVR 8 位控制器到 ARM 的所有功能。不知道你是否可以使用std::variant

【讨论】:

  • 谢谢...即使可以支持 STL,也可能会增加很多资源。
  • @MichelKeijzers:联合没有添加任何内容,std::variant 没有添加更多内容,因为您还必须自己动手制作开关盒或跳台。即使这样,您也必须将哪种类型存储在独立于您的联合中的“某处”,无论它是否写为联合。
  • 即使我认为union 是这里的方法,如果某些类不是标准布局类和/或没有默认构造函数,它也需要特殊处理。工会成员的创建/销毁必须是明确的并使用放置新指令...
  • @SergeBallesta:实际上,您将亲手实现std::variant。如果您不喜欢将对象放在手工制作的联合中,则可以通过联合的大小来确定所有对象的大小,即使您没有实例化一个。因此,如果工会“内部”的所有东西都难以处理,那么您可以同时兼顾两个世界。
【解决方案2】:

你必须确保

  • 您有足够的内存来保存任何派生类型的 4 个实例。
  • 内存对齐适用于所有派生类型。

然后您可以分配足够的内存来容纳 4 个实例并使用new(buffer) Type 来构造实例。完成并想要切换类型后,您可以在每个实例上调用析构函数并将placement new 用于其他类型。

【讨论】:

  • 谢谢,是的,这确实是最好的方法。我想最初我创建了 4 个实例,它们是“最大的”(或一个虚拟的大型基类)。
  • @MichelKeijzers 你可以制作一个像alignas(most_strict_type) unsigned char buffer[sizeof(largest_type)]; 这样的字符缓冲区,其中most_strict_typelargest_type 是适当的派生类。然后使用placement new 在这个缓冲区中构造对象。
  • 太好了,谢谢。我在考虑 max(sizeof(derived_class_1), .... ) 但可能在编译时不起作用。
  • @MichelKeijzers 你可以编写一个编译时max,这样会更健壮。
  • 我尝试了一些东西,它似乎有效。我将放置new 用于实例。我为每个派生类型使用Max(sizeof(...), sizeof(...), sizeof(...)),所以我只有一个地方有派生类,这是创建不同派生实例的工厂的一部分。给其他人的提示(尤其是在 Arduin 上):别忘了#include <new>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-06
  • 2012-12-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多