3

假设我有这样的课程:

class Node {
public:
    Node(Node* parent = 0) : mParent(parent) {}
    virtual ~Node() {
        for(auto p : mChildren) delete p;
    }

    // Takes ownership
    void addChild(Node* n);

    // Returns object with ownership
    Node* firstChild() const;

    // Does not take ownership
    void setParent(Node* n) { mParent = n; }

    // Returns parent, does not transfer ownership
    Node* parent() const { return mParent; }

private:
list<Node*> mChildren;
Node* mParent;
};

我现在想使用智能指针和/或右值引用来指示所有权在哪里转移和不转移。

我的第一个猜测是更改mChildren为包含unique_ptrs,如下调整函数签名。

    // Takes ownership
    void addChild(unique_ptr<Node> n);

    // Returns object with ownership
    unique_ptr<Node>& firstChild() const;

    // Does not take ownership
    void setParent(Node* n) { mParent = n; }

    // Returns parent, does not transfer ownership
    Node* parent() const { return mParent; }

现在,当我需要将结果传递Node::firstChild()给观察它但不获取所有权的函数时,这将是一种问题,因为我需要显式调用.get()unique_ptr据我所知,不建议这样做。

什么是正确和推荐的方式来指示所有权使用unique_ptr而不必求助于使用.get()和传递裸指针?

4

1 回答 1

8

起初,我会使用std::vector而不是std::list包含孩子。除非你有强烈的动机使用它,std::vector否则应该是默认容器。如果您担心性能,请不要担心,因为由 完成的连续分配std::vector可能会导致更高的缓存命中率,从而极大地加快访问速度std::list,这意味着分散的分配/访问模式。

其次,你有一个std::vector<std::unique_ptr<Node>>for 持有孩子是正确的,因为假设一个节点持有它的孩子节点的所有权是合理的。另一方面,除了 接受的指针之外的所有其他指针都addChild()应该是非拥有的原始指针。

这适用于mParent指针和由Node的成员函数返回的指针。事实上,firstChild()成员函数甚至可以返回一个引用,如果节点没有子节点则抛出异常。这样,您就不会混淆谁拥有返回的对象。

返回 aunique_ptr或对 a 的引用unique_ptr不是正确的习惯用法:唯一指针表示所有权,并且您不想将所有权授予Node.

这就是您的班级的样子:

#include <vector>
#include <memory>
#include <stdexcept>

class Node {
public:
    Node() : mParent(nullptr) { }

    void addChild(std::unique_ptr<Node>&& ptr) {
        mChildren.push_back(std::move(ptr));
        ptr->setParent(this);
    }

    Node& firstChild() const {
        if (mChildren.size() == 0) { throw std::logic_error("No children"); }
        else return *(mChildren[0].get());
    }

    Node& parent() const {
        if (mParent == nullptr) { throw std::logic_error("No parent"); }
        else return *mParent;
    }

private:

    void setParent(Node* n) { 
        mParent = n; 
    }

    std::vector<std::unique_ptr<Node>> mChildren;
    Node* mParent;
};

如果您想避免抛出异常,您当然可以决定返回非拥有的、可能为空的原始指针而不是引用。或者,您可以添加一对hasParent()getNumOfChildren()方法来检索有关 aNode状态的信息。如果他们不想处理异常,这将允许客户执行检查。

于 2013-05-04T10:25:12.940 回答