如何在c ++类中存储智能指针列表?

时间:2016-08-11 19:52:08

标签: c++ c++11 pointers smart-pointers

我有一个班级Node,必须有一个输入Edge的列表。但是,这些输入Edge不应由Node修改,只能被访问。我被建议使用智能指针,如下所示:

class Node
{
private:
    std::vector<std::unique_ptr<Edge>> inEdges;
//...
public:
    void inline AddEdge(std::unique_ptr<Edge>& edge) // could be const here too
    {
        this->inEdges.push_back(edge);
    }
//...
}

因此,在运行时,我可以创建节点和边的列表,并为每个节点分配边:

int main()
{
    std::vector<std::unique_ptr<Nodes>> nodes;
    std::vector<std::unique_ptr<Edges>> edges;
    nodes.push_back(std::make_unique<Node>(0, 0.5, 0.0));
    nodes.push_back(std::make_unique<Node>(1, 0.5, 0.0));
    edges.push_back(std::make_unique<Edge>(*nodes[0], *nodes[1], 1.0));
    nodes[1]->AddEdge(edges[0]);
}

编译器提供错误

  

错误1错误C2280:'std :: unique_ptr&gt; :: unique_ptr(const std :: unique_ptr&lt; _Ty,std :: default_delete&lt; _Ty&gt;&gt;&amp;)':尝试引用已删除的函数c:\ program文件(x86)\ microsoft visual studio 12.0 \ vc \ include \ xmemory0 593 1 sirs

由于Node的签名为std::vector<Edge*>,因此AddEdge void AddEdge(Edge& edge);内的原始指针处理&edge在矢量中。

我的代码出了什么问题?我该如何处理呢?鉴于std::vector无法存储引用,因为它们不可分配。

PS:我不想将指针的所有权转移到Node对象......

4 个答案:

答案 0 :(得分:4)

std::vector::push_back复制其参数,std::unique_ptr无法复制,只能移动。

您必须移动传递的std::unique_ptr

void inline AddEdge(std::unique_ptr<Edge> edge)
{
    this->inEdges.push_back(std::move(edge));
}

然后这样称呼它:

nodes[1]->AddEdge(std::move(edges[0]));

此外,inEdges并非必须是std::unique_ptr的向量,因为它们应该是非拥有者。 std::unique_ptr意味着所有权,但正如您所说,Node并不拥有Edge

使用std::vector<Edge*>代替(或std::observer_ptr在不久的将来实施时),因为原始指针(通常)意味着非所有权。您必须要小心std::vector<Edge*> 的生命周期超过std::unique_ptr的生命周期,否则指针将悬空。

答案 1 :(得分:4)

您应该移动 std::unique_ptr的实例,以便将它们放入std::vector。否则你需要std::shared_ptr

class Node
{
private:
    std::vector<std::unique_ptr<Edge>> inEdges;
//...
public:
    void AddEdge(std::unique_ptr<Edge>&& edge)
    {
        inEdges.push_back(std::move(edge));
    }
//...
}

我已更改AddEdge以获取r值参考,以便正确支持移动语义。

致电:

node.AddEdge(std::move(edge));

或者:

node.AddEdge(std::make_unique<Edge>(/*args*/));

暂且不说。如果您确实发现通过引用传递了std::unique_ptr,则可能是您应该使用std::shared_ptr的标志。

此外,inline对于在类中声明的方法是多余的。

答案 2 :(得分:2)

我根本不会使用智能指针。我认为最简单的方法是将所有节点 edge 存储为主向量中的,然后使用原始指针表达他们的关系。

您不需要智能指针,因为向量管理节点边缘的生命周期和破坏 >所以没有别的东西需要拥有它们。

使用此方案时要记住的最重要的事情是,在使用的数据结构之后,您必须从不更改向量的大小已创建指向其元素的指针。

原因是更改向量的大小会使所有指针无效(对象可能会移动到不同的内存位置)。

这样的事情:

class Edge
{
    class Node* a;
    class Node* b;
    double w;

public:
    Edge(Node* a, Node* b, double w): a(a), b(b), w(w) {}
};

class Node
{
private:
    double x, y, z;
    std::vector<Edge*> inEdges;

public:
    Node(double x, double y, double z): x(x), y(y), z(z) {}

    void AddEdge(Edge* edge)
    {
        this->inEdges.push_back(edge);
    }
};

int main()
{
    std::vector<Node> nodes; // values, not pointers
    std::vector<Edge> edges;

    // NOTE: You MUST create ALL of the Nodes BEFORE
    // adding their pointers to the Edges and you must
    // create ALL the Edges BEFORE adding their pointers
    // to the Nodes because resizing the vectors will
    // INVALIDATE all the pointers

    // FIRST create ALL the nodes
    nodes.emplace_back(0, 0.5, 0.0); // make nodes[0]
    nodes.emplace_back(1, 0.5, 0.0); // make nodes[1]

    // NEXT create ALL the edges
    edges.emplace_back(&nodes[0], &nodes[1], 1.0); // make edges[0]

    // Finally add the edges to the nodes
    nodes[1].AddEdge(&edges[0]);

}

答案 3 :(得分:1)

您正在尝试将 std :: unique_ptr 的副本推送到向量中。该向量试图创建unique_ptr的副本,这是不允许的,因为它会破坏独特所有权的目的。

您应首先考虑哪个对象拥有哪个对象,并且只有在确实存在唯一所有权的情况下才使用 std :: unique_ptr

您可以做几件事:

  1. std :: move()指向vector的指针,这将导致前一个副本无效(请参阅:move semantics)。
  2. 如果您希望多个对象保存指针的副本,请使用 std :: shared_ptr 。由于看起来你想拥有指针的多个副本,这可能是你想要的方向。
  3. 在你的情况下阅读 std :: weak_ptr 可能是值得的,因为我认为你可能希望对象相互引用,而不创建循环引用。弱指针在没有实际增加引用计数的情况下对shared_ptr进行处理,并且在某些情况下,您希望确保对象不会长时间挂在引用上并造成内存泄漏。

    希望这有帮助。