如何在 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
永远不会超过它。
但是,如果您希望 Node
比 Graph
长寿,这种方法 不会 起作用,或者如果您希望 Node
用于多个 Graph
对象。如果它位于不同的 Graph
之间,那么您可能 运行 有 Node
引用悬空指针的风险——这会很糟糕。
如果是这种情况,您可能需要考虑不同的所有权模式,例如 shared_ptr
和 weak_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 个所有者,因此逻辑简单易懂。
这是一个更普遍的问题,我正在努力解决 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
永远不会超过它。
但是,如果您希望 Node
比 Graph
长寿,这种方法 不会 起作用,或者如果您希望 Node
用于多个 Graph
对象。如果它位于不同的 Graph
之间,那么您可能 运行 有 Node
引用悬空指针的风险——这会很糟糕。
如果是这种情况,您可能需要考虑不同的所有权模式,例如 shared_ptr
和 weak_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 个所有者,因此逻辑简单易懂。