图结构——指针不好吗?

Graph structure - are pointers bad?

本文关键字:指针 结构      更新时间:2023-10-16

我在c++中实现了一个寻路算法的图结构。

当一个新的边或节点被创建时,它被存储在一个单独的向量中,以便稍后由图类的析构函数删除。我使用指针是因为它们提供了简单的图形导航;我可以通过简单地比较节点的地址来轻松地测试节点的身份,或者我可以通过创建指针向量来创建路径,然后直接使用这些指针在图中导航。

struct Edge
{
    Node* from;
    Node* to;
}
struct Node
{
    Data data;
    std::vector<Edge*> outEdges;
    std::vector<Edge*> inEdges;
}

我读过一些关于指针是如何不好的文章,应该避免使用或用智能指针代替(但即使是它们也应该避免)。或者优秀的程序员根本不使用它们(除了例外)。我知道它们是内存泄漏、安全风险的来源,而且很难正确管理(特别是在多线程应用程序中)。

我的问题:是指针方法在这种情况下坏吗?

编辑1:有一些问题我在哪里读到的指针(智能)应该避免。163279年https://softwareengineering.stackexchange.com/questions/56935/why-are-pointers-not-recommended-when-coding-with-c/163279

在他回答的第二部分:

  • " c++中指针的大部分使用都是不必要的。"
  • "…现代c++习惯用法通常根本不需要指针……"
  • "对于了解现代c++的人来说,很明显你很少需要任何指针(无论是智能指针还是原始指针;除非将它们用作迭代器)。"

我要重复一遍。指针也不错。用友好的黄色字母(C)打印出来,然后别在墙上。它们非常有用。我从来没有见过一个专业的c++程序完全避免了指针。

管理你自己的指针通常是不好的,除非你在指针管理器上工作。

不受约束的标准内存分配可能成为高性能应用程序的瓶颈——但是指针不是标准内存分配的同义词。

指针不是内存泄漏或安全风险的来源。指针并不难管理(一般来说不比编写好的程序难)。

如果你不想使用指针,说明你选错了语言。

如果可能的话,为什么要避免使用指针?
因为你必须在内存中跟踪它们,所以它们的缓存局部性非常差。

一个对象可以有多少个所有者?
只能有一个!
除非它是shared_ptr(它是真正的单个所有者)。因此,你可以使用无所有权的指针,只要你只跟随它们,不删除或转移它们的所有权。

指针会神奇地更新,如果我指向移动?
不!如果您使用向量进行存储,并且超出了它的容量,它将重新定位,使指向它的所有指针无效。如果你的确信有足够的容量,你可以使用指针。

那么在这种情况下,我会考虑下面的结构,一个指针到内存只是只是一个索引到内存,为什么不使用索引本身?

struct Edge {
    // index into allNodes
    uint32_t from;
    uint32_t to;
}
std::vector<Edge> allEdges;
struct Node {
    Data data;
    // index into allEdges
    std::vector<uint32_t> outEdges;  // sort on to
    std::vector<uint32_t> inEdges;   // sort on from
}
std::vector<Node> allNodes; // contains all nodes
如果你不需要遍历所有边
struct Node {
    Data data;
    // index into allNodes
    std::vector<uint32_t> outEdges; // sort
    std::vector<uint32_t> inEdges;  // sort
}
std::vector<Node> allNodes; // contains all nodes

你不能做的是:
a)删除任何节点/边缘,并将其余的向上移动。B)移动他们中的任何一个,包括C)排序

如果要删除Edge,请记住在两个节点中擦除Edge。

如果你在每个向量中有很多边,那么对它们进行排序可能是相关的,这样你就可以使用二分搜索来找到一个特定的边。

如果你想使用指针,只要确保每个节点和边都有一个所有者。将它们放在一个向量中可以确保这一点,但要注意调整大小,因为它使所有向量无效。

在不需要指针时,应避免使用

这里,节点要么包含其他节点,要么包含对它们的引用。在容器中,维护n到n个节点副本之间的一致性将是一场噩梦。某种类型的引用要简单得多,也不太可能导致问题。

但是现在管理所有权成了一个问题。谁拥有节点并负责删除不再使用的节点?

对于无向图,当没有进一步的连接时,节点可以拥有自己并自毁(delete或从节点池中移除自己)。

在有向图中,节点不能自我确定,因为它不知道谁可能仍然指向它。它的同伴节点必须集体负责,并在没有节点接触该节点时释放该节点。跟踪这是一项有趣的管理任务,也是图表的重点。

对于Edge类使用普通指针是可以的,只要已经添加的节点不会再次被删除。你仍然可以删除整个图。如果你的图形经常变化,我建议使用std::weak_ptr作为边缘。

然而,Edge类已经完全过时了。

struct Node
{
    Data data;
    std::vector<Node*> edges;
}

这对于有向图和无向图都是足够的。在这两种情况下,您只需要记录向外的边。在无向图中,你必须记录两个节点的边(用指针指向另一个方向)。

关于指针不好的梗是完全错误的。指针是语言的重要组成部分,通常是从一个数据结构引用另一个数据结构的最佳(或唯一)方式。甚至美国宇航局喷气推进实验室也在航天器的性能和安全关键代码中自由地使用指针。参见他们的编程标准:http://lars-lab.jpl.nasa.gov/JPL_Coding_Standard_C.pdf

不小心使用指针会给你带来麻烦,但生活中的一切都是如此。