4

我的程序面临设计问题。我必须管理作为根 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

4 回答 4

3

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

因此,在您的情况下,我认为ChainDescriptor应该使用shared_ptr(它拥有节点)并且Node应该使用weak_ptrfor m_parent(它只引用它)和shared_ptrfor m_childs(它拥有它们)。

于 2013-11-14T10:38:08.117 回答
3

您可以使用shared_ptrform_childsweak_ptrfor m_parent

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

另一种选择是仅使用shared_ptrinChainDescriptor并将所有原始指针保留在Node. 这种方法避免了弱指针并且拥有清晰的所有权策略(父节点拥有他们的子节点)。

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

于 2013-11-14T10:38:15.323 回答
1

试图用某种智能指针替换原始指针通常是行不通的。智能指针与弱指针具有不同的语义,通常需要在更高级别考虑这些特殊语义。这里“最干净”的解决方案是添加对 copy in 的支持ChainDescriptor,实现深拷贝。(我在这里假设您可以 clone Node,并且所有这些Node都始终由 a 拥有ChainDescriptor。)此外,对于撤消,您可能无论如何都需要深层副本;您不希望活动实例中的修改修改为撤消而保存的数据。

话虽如此,您的节点似乎被用来形成一棵树。在这种情况下,std::shared_ptr只要 1) allNode 总是由 aChainDescriptor或 parent “拥有” Node,并且 2) 结构确实是森林,或者至少是 DAG 的集合(当然,你是'不在任何已保存的实例中进行更改)。如果结构是这样的,可能会发生循环,那么你不能shared_ptr在这个级别使用。您也许可以将节点列表和树抽象为一个单独的实现类,并 ChainDescriptor保留一个shared_ptr

(FWIW:我为多年前编写的解析树中的节点使用了引用计数指针,并且不同的实例可以共享子树。但我从一开始就将其设计为使用引用计数指针。而且由于树的方式建成后,我保证不会有循环。)

于 2013-11-14T10:53:06.420 回答
1

通常的实现是每个节点都对其子节点具有强引用(即保持它们活着),并且每个子节点对父节点具有弱引用。

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

我认为您的ChainDescriptor班级可以在这里使用强引用。

于 2013-11-14T10:44:51.060 回答