C ++:用共享和弱ptr替换原始指针

时间:2013-11-14 10:33:58

标签: c++ memory-management boost shared-ptr weak-ptr

我的程序中遇到了设计问题。 我必须管理Nodes对象,它是根ChainDescriptor的一部分。

基本上它看起来如下:

class ChainDescriptor
{
public:
    ~ChainDescriptor()
    {
        //delete the nodes in nodes...
    }

    void addNode(Node *);
    Node * getNode();

    const std::list<Node *>& getNodes() const;

    std::list<Node *> m_nodes;

};

class Node
{
public:
    Node(Node *parent);

    void addChild(Node *node);
    Node * getChild(const std::string& nodeName);

private:
    Node * m_parent;
    std::list<Node*> m_childs;
};

ChainDescriptor类拥有所有节点并负责删除它们。 但是现在需要在另一个程序中使用这些类,一个具有撤消/重做功能的GUI,以及“所有权”的问题。 在深入修改现有代码之前,我正在考虑不同的解决方案:

  • 使用shared_ptr和相应的list<shared_ptr<...> >
  • 使用weak_ptr和相应的list<weak_ptr<...> >

在上面的例子中,我真的不知道在哪里正确使用shared_ptrweak_ptr

有什么建议吗?

4 个答案:

答案 0 :(得分:3)

shared_ptr拥有智能指针,weak_ptr正在引用智能指针。

因此,在您的情况下,我认为ChainDescriptor应使用shared_ptr(它拥有节点)而Node应使用weak_ptr m_parent(仅限{}引用它)和shared_ptr m_childs(它拥有它们)。

答案 1 :(得分:3)

您可以shared_ptr使用m_childsweak_ptr使用m_parent

但是,保留指向父Node的原始指针并且根本不使用任何弱指针仍然是合理的。这背后的保护机制是永久存在非null父级的不变量。

另一个选项是仅在shared_ptr中使用ChainDescriptor并保留Node中的所有原始指针。这种方法避免了弱指针,并且具有干净的所有权策略(父节点拥有自己的子节点)。

弱指针将帮助您自动管理内存,但其背后是模糊的所有权逻辑和性能损失。

答案 2 :(得分:1)

通常的实现是每个节点都有对其子节点的强引用(即使它们保持活动状态),并且每个子节点都有一个弱引用返回父节点。

这样做的原因是避免循环引用。如果只使用强引用,那么你会遇到父引用计数永远不会降为零的情况(因为子引用),并且子引用计数永远不会降为零(因为父引用有引用)。

我认为你的ChainDescriptor类可以在这里使用强引用。

答案 3 :(得分:1)

尝试用某种智能替换原始指针 指针一般不起作用。智能指针有 不同的语义比弱指针,通常,这些 特殊语义需要在更高的位置考虑 水平。这里“最干净”的解决方案是添加对副本的支持 在ChainDescriptor中,实施深层复制。 (我想 在这里你可以克隆Node,并且所有Node都是 始终由ChainDescriptor拥有。)另外,对于撤消,您可以 反正需要一份深刻的副本;你不想修改 活动实例,用于修改为撤消保存的数据。

话虽如此,你的节点似乎被用来形成一棵树。在 在这种情况下,std::shared_ptr将起作用,只要1)所有Node 始终由ChainDescriptor或父母“拥有” Node,2)结构确​​实是森林,或者至少 DAG的集合(当然,您不会进行更改 在任何已保存的实例中)。如果结构是这样的 可能会出现循环,然后您无法使用shared_ptr 水平。您可以抽象出节点列表和 树成一个单独的实现类,并具有 ChainDescriptor保留shared_ptr

(FWIW:我使用了一个引用计数指针来表示节点 我多年前写过的一个解析树,以及不同的实例 可以共享子树。但我是从它设计的 开始使用引用计数指针。而且因为如何 树被建造,我保证可能没有 周期。)