C++ 避免动态内存分配

问题描述

假设我有一些 Node 结构体,其中包含指向左右孩子的指针和一些数据:

struct Node {
  int data;
  Node *left;
  Node *right;
};

现在我想做一些状态空间搜索,自然我想边走边构建图。所以我将有一种循环,必须创建节点并保持它们。类似的东西:

Node *curNode = ... ; // starting node

while (!done) {
  // ...
  curNode->left = new Node();
  curNode->right = new Node();
  // ..
  // Go left (for example)
  curNode = curNode->left;
}

问题是我必须在每次迭代时动态分配节点,这很慢。所以问题是:我怎样才能拥有指向某个内存的指针,而不是一个一个地分配它

我想到的第一个解决方案是使用 std::vector<Node> 来包含所有已分配的节点。问题是当我们 push_back 元素时,所有引用都可能无效,所以我所有的左/右指针都将是垃圾

第二种解决方案是预先分配一大块内存,然后当我们想要创建一个 Node 时,我们只需抓住下一个可用的指针。为了避免引用失效,当我们超过当前块的容量时,我们只需要创建一个大内存块的链表,以便每个给定的指针保持有效。我认为 std::deque 的行为是这样的,但它不是为此明确创建的。

另一种解决方案是存储向量索引而不是指针,但这不是解决方案,因为 Node 不想与任何容器相关联,它想要直接使用指针。

那么这里有什么好的解决方案,可以避免在每次迭代时分配新节点?

解决方法

您可以使用 std::deque<Node> ,它会为您按组创建元素进行内存管理,如果您不删除中间的元素,则不会使指针无效。但是,如果您想更精确地控制组中的元素数量,您可以非常简单地创建类似的内容:

class NodePool {
    constexpr size_t blockSize = 512;
    using Block = std::array<Node,blockSize>;
    using Pool  = std::list<Block>;

    size_t allocated = blockSize;
    Pool   pool;
public:
    Node *allocate() 
    {
       if( allocated == blockSize ) {
           pool.emplace_back();
           allocated = 0;
       }
       return &( pool.back()[ allocated++ ] );
    }
};

我没有尝试编译它,但它应该足以表达这个想法。在这里更改 blockSize 您可以微调程序的性能。尽管您应该知道 Node 对象将完全由组构造(不像 hoiw std::deque 会这样做)。据我所知,无法为符合标准的 Node 对象创建原始内存。