更改基类指针内容而不分配新地址

时间:2021-03-20 17:12:38

标签: c++ oop pointers arduino

我正在开发一个内存容量有限的 Arduino 项目,所以很多 C++ 神奇的便利都在窗外,其中之一是能够在任何你想要的地方分配内存(如果你这样做,可能会导致 Heap Fragmentation那)。但是 OOP 需要指针和分配:

class StateBase
{
  virtual run();
};
class StateA : public StateBase
{
  int bigArr[100];
  virtual run();
};
//About 5 more States, with different member variables
//Usually in Arduino, you allocate a big chunk of heap memory, and keep reusing it:
StateBase *memPool[] = {new StateA(), new StateB(), new...};

但我想试试这个,令我惊讶的是,它有效:

StateBase *memPool = new StateBase();
//...do something, now I need to switch state
StateA newState;
memcpy(memPool, &newState, sizeof(StateA));

问题:

  1. 这是个坏主意吗?有什么后果吗?
  2. 是否应该使用 memPool = new (memPool) StateA() 而不是 memcpy?
  3. 为什么它有效,显然 StateBase 和 StateA 的大小不同?

3 个答案:

答案 0 :(得分:1)

  1. 这是一个非常糟糕的主意,结果是undefined behavior
  2. 您应该使用复制/移动构造函数/赋值运算符,并相信编译器和操作系统会做得很好,直到您有理由不这样做。
  3. 它们有不同的尺寸 (sizeof)。见 1 中的结果。
  4. 当您像这样切换状态时,您可能正在做一个悲观的事情,阻止编译器做一些更重要的事情。

您可以使用类似 union 的结构(我们称之为 std::variant)来做类似的事情,并且仍然使用隐式移动赋值等。如果您的类中的类型是微不足道的,编译器将知道如何有效地做到这一点。如果类型不是微不足道,你正在做的事情很可能会使未定义的行为以更显着​​的方式表现出来。见Nasal Demons

enter image description here

N'os - Summoner of Nasal Demons - 图片归功于 Billyonion64 @ Reddit

答案 1 :(得分:0)

  1. 是的,是的。您可能会损坏其他一些对象,因为 StateA 比您处理它的内存大。
  2. 放置 new 通常更合理,但在这种情况下,它不会有太大变化。从第 1 点开始,您仍然有大问题。
  3. 之所以有效,是因为像 memcpy 这样的函数不关心内存管理。所以如果你想射你的腿,他们就在这里。 :) 在现代 C++ 中使用 string.h (cstring) 中的函数几乎总是个坏主意。

答案 2 :(得分:0)

尽管它看起来可行,但出于多种原因,这并不是一个好主意。一方面,因为编译器可以进行各种优化,您的代码可能会意外中断。更重要的是,您正在清除部分堆,因此您的程序崩溃可能只是时间问题。即使您对使用未定义的行为不那么敏感,这甚至不接近安全。在某些时候,您可以做一些技术上是 UB 但通常有效的事情,但鉴于您正在覆盖未分配的内存,这不是其中之一。

如果你真的很担心碎片,你实际上可以使用你的选项 2 并做一个新的放置,但是你使用的任何内存缓冲区都应该足够大,以便你将使用它的最大类对象。此外,我不会新建一个对象,然后用另一个对象新建。好乱啊只需使用 malloc 或其他方法单独分配缓冲区,然后在该地址上使用新的布局。当你完成你的对象时,显式调用析构函数,然后你可以用另一个新的位置重用你的内存。冲洗并重复。

相关问题