问题描述
假设我有一些 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 对象创建原始内存。