如何在 C++ 中存储对其他对象的引用?

How to store references to other objects in C++?

这是一个更普遍的问题,我正在努力解决 C++ 最佳实践问题。假设我想创建存储相互引用的对象,例如图形。所有的对象都归同一个对象所有,就像所有节点的Graph对象一样,也就是说所有权是固定的。

这是我的想法:一个 class 图有一个 std::vector 个节点,每个节点都有一个 std::vector 个节点代表它的连接列表。我想知道如何最好地根据智能指针来实现这一点?据我了解,所有权是唯一的,因此图形向量应该是 std::vector<std::unique_ptr<Node>> nodes,我可以根据需要填充它。但是 connections 向量,我怎样才能让每个节点存储对其连接的引用?这些将只是只读引用,也许最好命名所有节点并仅存储名称,或者将连接存储在 Graph 中。但是有没有一种很好的方法来存储对连接节点的引用,就好像它们是常量指针一样?

注意:这实际上是关于所有权和智能指针的,而不是关于数据结构的,图的例子只是一个例子。

I'm wondering how best to implement this in terms of smart pointers?

不使用它们。为图形使用节点向量:std::vector<Node>。这是一个合理的默认选择,除非您有充分的理由不这样做。

But is there a good way of storing references to the connection nodes as if they were const pointers?

是的。常量指针是一种很好的存储方式,就好像它们是常量指针一样。 (通过“const 指针”,我认为我们实际上是在谈论指向 const 的指针)。

引用包装器是另一种选择。虽然它的优点是没有 null 的表示,但它确实有语法笨拙的缺点。

在讨论“最佳实践”时,重要的是要考虑代码的质量属性和需求。

代码示例中没有“正确”或“错误”的答案,例如Graph;有不同的程度以不同的方式解决不同的问题——这在很大程度上取决于其预期的使用方式。

到目前为止,解决此类问题的最简单方法是让主容器 (Graph) 在 unique_ptr 中拥有 强所有权 ,并且只查看内部元素(Node)中的生命周期,使用原始指针,例如:

class Graph
{
    ...
private:
    std::vector<std::unique_ptr<Node>> m_nodes;
};

class Node
{
    ...
private:
    std::vector<const Node*> m_connected_nodes;
};

这会很有效,因为 Node 无法改变其连接的节点,并且因为 Graph 假设 Node 永远不会超过它。

但是,如果您希望 NodeGraph 长寿,这种方法 不会 起作用,或者如果您希望 Node 用于多个 Graph 对象。如果它位于不同的 Graph 之间,那么您可能 运行 有 Node 引用悬空指针的风险——这会很糟糕。

如果是这种情况,您可能需要考虑不同的所有权模式,例如 shared_ptrweak_ptr 所有权:


class Graph
{
    ...
private:
    std::vector<std::shared_ptr<Node>> m_nodes;
};

class Node
{
    ...
private:
    std::vector<std::weak_ptr<const Node*>> m_connected_nodes;
};

在这种情况下,Node 仅弱了解其他 Node 对象,而 Graph 是它们的强所有者。这可以防止悬空问题,但现在会为 shared_ptr 的控制节点带来额外的开销,并且在访问 weak_ptr 节点之前必须检查它是否处于活动状态。

所以正确答案是:这取决于。如果您可以摆脱前一种方法,那可能是最干净的方法;你总是有 1 个所有者,因此逻辑简单易懂。