使用智能指针建模所有权的含义

4

我目前手动管理项目中对象的生命周期。我正在考虑切换到智能指针,特别是tr1::shared_pointer和tr1::weak_ptr。然而,我遇到了一些问题,希望得到一些最佳实践的建议。

考虑以下类图:

Polyhedron Class Diagram

在这个图中,粗箭头表示具有所有权语义的关联(源负责删除目标或目标)。细箭头表示没有所有权的关联。

据我所知,实现具有所有权语义的关联的一种方法是使用tr1::shared_ptr(或其集合)。其他关联可以使用tr1::shared_ptr或tr1::weak_ptr来实现。如果可能导致循环引用,则不能使用前者,因为那会防止正确释放资源。

正如您所看到的,Edge和Side类之间存在一圈关联。我可以轻松地通过使用tr1::weak_ptrs从Edge到Side实现“left”和“right”关联来打破这个圈子。但是,我更喜欢使用智能指针来记录代码中关联的所有权语义。因此,我只想在图中由粗箭头表示的关联中使用shared_ptrs,并在其他所有情况下使用weak_ptrs。

现在我的第一个问题是:我应该像上面描述的那样慷慨地使用weak_ptrs,还是尽可能少地使用它们(仅避免循环引用)?

下一个问题是:假设我有一个计算一组顶点平均值的函数。进一步假设我已经实现了从Polyhedron到其顶点的关联,如下所示:

class Vertex;
class Polyhedron {
protected:
    std::vector<std::tr1::shared_ptr<Vertex> > m_vertices;
};

我已经实现了从一个侧面到其顶点的关联,方法如下:

class Vertex;
class Side {
protected:
    std::vector<std::tr1::weak_ptr<Vertex> > m_vertices;
};

注意,面的顶点集是多面体顶点集的子集。现在假设我有一个计算一组顶点平均值的函数。该函数当前声明如下:

const Vertex centerOfVertices(std::vector<Vertex*> vertices);

现在,如果我按照上述方式表示关联,如果我理解正确的话,我需要两个函数:
const Vertex centerOfVertices(std::vector<std::tr1::shared_ptr<Vertex> > vertices);
const Vertex centerOfVertices(std::vector<std::tr1::weak_ptr<Vertex> > vertices);

我无法将shared_ptr的向量转换为weak_ptr的向量,这让我感到有些奇怪。我想知道在这种情况下应该采取什么方法来避免这种情况。


2
我认为这将滥用 shared_ptr,我没有仔细阅读,但似乎没有任何东西实际上共享所有权。相反,请使用 unique_ptr 和原始指针,并向您提供(弱)原始指针的系统保证指向的对象在其范围内将有效。 - David
如果我知道 side 代表什么,我会更好地理解这个的。它是指 '面' 吗? - Rook
@JohnDibling 谢谢你指出这一点,我之前不知道。 - Kristian Duske
Dave:那里的主要实体都没有“所有者”或“父级”的严格定义。当多面体相遇时,它们可能共享顶点、边和顶点,或边、顶点和面。我认为这就是OP想要表示的内容。 - Rook
@Dave 这只是模型的一个子集,模型中可能还有其他共享所有权的地方,但并非全部,因此 unique_ptr 对我也很重要。 - Kristian Duske
显示剩余2条评论
3个回答

1
然而,我更喜欢使用智能指针来在代码中记录关联的所有权语义。
正是如此。毕竟这正是它们完美表达的。
因此,我想仅针对图表中表示为粗箭头的关联使用shared_ptrs,而对其他所有内容使用weak_ptrs。
去试试吧,但有一个警告:你的所有权看起来根本不像shared所有权,而是简单的唯一所有权。因此,在这里适当的智能指针不是shared_ptr,而是std::unique_ptr,而弱指针将变成裸指针。
现在,如果我按上述方式表示关联,我突然需要两个函数...
是的。或者你可以使用模板。
另外,由于该函数实际上并不关心共享所有权,因此您可以向其传递裸指针,但这当然意味着首先从结构中获取裸指针,这需要为客户端添加步骤,这可能不利于接口。

1
使用共享指针似乎很明智,特别是当您发现顶点等可以在多个多面体之间共享时。
要将弱指针向量转换为共享指针向量,请使用显式构造函数:
centerOfVertices(std::vector<std::tr1::shared_ptr<Vertex> >(vertices.begin(), vertices.end()));

0

你当然也可以使用shared_ptr来处理循环引用。在某些罕见的情况下,这实际上是有意义的。但是,你必须小心地在完成对象后打破循环。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接