进入智能指针,如何处理代表所有权?

问题描述 投票:4回答:2

我已经制作了一个动态图形结构,其中节点和弧都是类(我的意思是弧是内存中的实际实例,它们不是节点的邻接列表所暗示的)。每个节点都有一个指向它所连接的弧的指针列表。每个弧有2个指向它连接的2个节点的指针。

删除节点会为每个弧调用delete。每个弧删除都会从其连接的2个节点中的弧列表中删除其指针。简化:

~node()
    {
    while(arcs_list.size())
        {
        delete arcs_list[arcs_list.size()-1];
        }
    }

~arc()
    {
    node_from.remove_arc(this);
    node_to.remove_arc(this);
    }

如果我想在这里开始使用智能指针,我该如何进行?每个弧是否拥有2个节点,或者2个节点是否共享单个弧的所有权?我在想一个shared_ptr,但共享指针只会在删除两个节点时删除弧。如果我只删除一个节点,如果我使用shared_ptr,我仍然必须明确删除它的所有弧。这完全违背了不使用原始指针的观点。

节点可以单独存在;每个弧由两个节点拥有,只要这两个节点都存在,它就只能存在。

是否有其他类型的智能指针我应该用来处理这个?或者是原始指针只是简单的方式去?

c++ pointers smart-pointers raw-pointer
2个回答
4
投票

每个弧是否拥有2个节点,或者2个节点是否共享单个弧的所有权?

你自己回答了这个问题:

节点可以单独存在;每个弧由两个节点拥有,只要这两个节点都存在,它就只能存在。

当对象A拥有对象B时,对象A可以在销毁B后存在,但是销毁A意味着破坏B.应用于您的情况,这两个节点共享弧的所有权。

是否有其他类型的智能指针我应该用来处理这个?或者是原始指针只是简单的方式去?

没错。这是真正的问题。对于这种情况,没有预先制作的智能指针。但是,我不会在节点和/或弧类中使用原始指针。这意味着这些类需要在其主要目的之上实现内存管理。 (让每个班级做好一件事情更好,然后尝试做很多事情而失败。)我看到了一些可行的选择。

1:编写自己的智能指针

编写一个可以封装必要的破坏逻辑的类。节点和/或弧类将使用您的新类而不是标准智能指针(而不是原始指针)。花些时间确保您的设计决策是可靠的。我猜你的新类需要某种函数/可调用来告诉它如何从它所在的列表中删除它自己。或者可能将一些数据(如指向节点的指针)从arc类转移到new类。

我还没有弄清楚细节,但这是一个合理的方法,因为这种情况不适合任何标准的智能指针。关键是不要将此逻辑直接放在节点和弧类中。

2:标记无效弧

如果您的程序无法立即释放内存,则可以采用不同的方法来解决删除弧。不要立即从节点列表中删除弧,只需将弧标记为不再有效。当节点需要访问其弧时,它(或者更好的是,它的列表)将检查它访问的每个弧 - 如果弧无效,那么它可以在那时从列表中删除。从两个列表中删除节点后,正常的shared_ptr功能将启动以删除弧对象。

这种方法的有用性降低了节点在其弧上迭代的频率。所以有一个判断要求。

如何标记弧无效?天真的方法是给它一个布尔标志。在构造函数中将标志设置为false,并在将弧视为删除时将其设置为true。有效,但确实需要一个新的领域。这可以在没有膨胀弧级的情况下完成吗?嗯,据推测,每个弧都需要指向其节点的指针。由于弧不拥有其节点,因此这些可能是弱指针。因此,定义弧无效的一种方法是检查弱指针是否为expired()。 (注意,当直接删除弧时,弱指针可以手动reset(),而不是通过节点的删除。所以过期的弱指针不一定意味着关联的节点消失了,只是弧不再指向它。)

在弧类很大的情况下,您可能希望立即丢弃其大部分内存,只留下一个存根。您可以添加一个间接级别来完成此任务。本质上,节点将共享指向唯一指针的指针,唯一指针将指向您当前称为arc类的指针。删除弧时,唯一指针是reset(),释放弧的大部分内存。当此唯一指针为空时,弧无效。 (看起来Davis Herring的答案是另一种获得此效果的方法,如果你可以接受一个将shared_ptr存储到自身的对象,那么内存开销就会减少。)

3:使用Boost.Bimap

如果你可以使用Boost,他们有一个看起来像它可以解决你的问题的容器:Boost.Bimap。但是,你问,我不是已经使用邻接列表打折了吗?是的,但是这个Bimap不仅仅是一种将节点相互关联的方法。该容器支持与每个关系相关联的additional information。也就是说,Bimap中的每个关系都代表一个弧,它将具有一个与弧信息相关联的对象。似乎很适合你的情况,你可以让别人担心内存管理(总是一件好事,只要你相信某人的能力)。


2
投票

由于节点可以单独存在,因此它们由图形(可能是也可能不是单个对象)拥有,而不是弧(即使是共享所有权)。正如你所观察到的那样,它的节点对弧的所有权是对任何一个所有者足以保持对象存活的通常的shared_ptr情况的双重性。你可以在这里使用shared_ptrweak_ptr(以及指向节点的原始,非拥有指针):

struct Node;
struct Arc {
  Node *a,*b;
private:
  std::shared_ptr<Arc> skyhook{this};
public:
  void free() {skyhook.reset();}
};
struct Node {
  std::vector<std::weak_ptr<Arc>> arcs;
  ~Node() {
    for(const auto &w : arcs)
      if(const auto a=w.lock()) a->free();
  }
};

显然,其他Node操作必须检查空的弱指针,并可能定期清理它们。

请注意,异常安全(包括与bad_alloc构建shared_ptr相比)需要more care in constructing an Arc

© www.soinside.com 2019 - 2024. All rights reserved.