我目前正在尝试使用C ++实现A *寻路算法。
我在使用指针时遇到了一些问题......我通常会找到一种方法来避免使用它们,但现在我想我必须使用它们。
所以,假设我有一个像这样实现的“节点”类(与A *无关):
class Node
{
public:
int x;
Node *parent;
Node(int _x, Node *_parent)
: x(_x), parent(_parent)
{ }
bool operator==(const Node &rhs)
{
return x == rhs.x && parent == rhs.parent;
}
};
它有一个值(在本例中为int x)和一个父(指向另一个节点的指针),用于通过父指针导航节点。
现在,我想要一个包含已经或正在考虑的所有节点的节点列表。它看起来像这样:
std::vector<Node> nodes;
我想要一个包含指向 nodes 列表中节点的指针的列表。 宣布如下:
std::vector<Node*> list;
但是,我肯定不能正确理解指针,因为我的代码不起作用。 这是我正在谈论的代码:
std::vector<Node> nodes;//nodes that have been considered
std::vector<Node*> list;//pointers to nodes insided the nodes list.
Node node1(1, NULL);//create a node with a x value of 1 and no parent
Node node2(2, &node1);//create a node with a x value of 2 and node1 being its parent
nodes.push_back(node1);
list.push_back(&nodes[0]);
//so far it works
//as soon as I add node2 to nodes, the pointer in "list" points to an object with
//strange data, with a x value of -17891602 and a parent 0xfeeefeee
nodes.push_back(node2);
list.push_back(&nodes[1]);
显然存在未定义的行为,但我无法看到哪里。 有人可以告诉我,我对指针的理解不足会破坏这段代码吗?为什么?
答案 0 :(得分:2)
一个问题是push_back可以强制重新分配向量,即它创建一个更大的内存块,将所有现有元素复制到该更大的块,然后删除旧块。这会使你对向量中元素的任何指针无效。
答案 1 :(得分:2)
所以,你在这里遇到的第一个问题是你正在使用你的一个向量的各个节点的地址。但是,随着时间的推移,当您向向量添加更多Node对象时,这些指针可能会变为无效,因为向量可能会移动节点。
(向量以某个预先分配的大小开始,当你填充它时,它会分配一个新的,更大的存储区域并将所有元素移动到新位置。我打赌你的情况,只要将第二个节点添加到节点,就会执行此操作。)
有没有理由不能存储索引而不是原始指针?
答案 2 :(得分:1)
问题在于,每次添加到矢量时,可能需要 扩展其内部存储 。如果它这样做,它会分配一个新的存储空间,复制所有内容,并删除旧的 使迭代器和指针 无效到其所有对象。
作为您问题的解决方案,您可以
nodes.reserve(42)
)来避免重新分配nodes
转换为 std::list
(这不会使迭代器无效或指向不受更改直接影响的元素的指针)除了你的问题,但还是值得一提:
+=
),通常最好实现为自由函数,而不是成员函数。答案 3 :(得分:1)
只是添加现有答案;而不是原始指针,考虑使用某种形式的智能指针,例如,如果boost可用,请考虑shared_ptr。
std::vector<boost::shared_ptr<Node> > nodes;
和
std::list<boost::shared_ptr<Node> > list;
因此,您只需要创建一个Node实例,并为您“管理”它。在Node类中,您可以选择父级的shared_ptr(如果您希望确保在删除所有子节点之前不清除父节点,或者您可以将其设为weak_ptr。
使用共享指针也可以帮助缓解您希望在多个容器中存储“句柄”的问题(即,您不一定需要担心所有权 - 只要删除所有引用,那么对象将被清除)。
答案 4 :(得分:0)
您的代码对我来说很好,但请记住,当节点超出范围时,列表将变为无效。