【问题标题】:How to store a list of smart pointers inside a c++ class?如何在 C++ 类中存储智能指针列表?
【发布时间】:2016-08-11 19:52:08
【问题描述】:

我有一个类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>::unique_ptr(const std::unique_ptr<_ty>> &)' : 试图引用已删除的函数 c:\program files ( x86)\microsoft visual studio 12.0\vc\include\xmemory0 593 1 先生

它曾经使用Nodestd::vector&lt;Edge*&gt; 内的原始指针,因为AddEdge 的签名将是void AddEdge(Edge&amp; edge);,推回向量内的&amp;edge

我的代码有什么问题?我应该如何着手纠正它?鉴于 std::vector 不能存储引用,因为这些是不可分配的。

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

【问题讨论】:

  • std::unique_ptr 可以移动,但不能复制。您的 AddEdge 应该按值接受它,并使用 std::move 进一步传递它
  • Node 是否拥有边缘?如果不是,请使用原始指针(或std::reference_wrapper)。如果是:用普通的std::vector&lt;Edge&gt; 代替有什么问题?
  • 但我不想转让指针的所有权
  • Edge 有自己的动态...每个Node 只能获得每个Edge 的输出信号...所以Node 不拥有@ 987654344@的
  • @Girardi “应该是std::shared_ptr吗?” 不,智能指针意味着所有权。只是观察而不拥有使用原始指针和引用。使用后者(如果指针不能为 0,则可能通过 std::reference_wrapper

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


【解决方案1】:

您应该只移动 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】:

    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 不拥有 Edges。

    改用std::vector&lt;Edge*&gt;(或std::observer_ptr,当它在不久的将来实现时),因为原始指针(通常)意味着非所有权。您只需要注意std::vector&lt;Edge*&gt; 的生命周期不会超过std::unique_ptrs 的生命周期,否则指针将悬空。

    【讨论】:

    • "std::vector::push_back 复制它的参数",有一个重载采用不复制的右值引用
    • @PiotrSkotnicki 没错,我看错了 sn-p,对不起
    • @wfbarksdale 我想你想要std::move(vector[i])
    • 感谢@Rakete1111 的回答让我眼前一亮!
    • 你的方法原型不应该是void inline AddEdge(std::unique_ptr&lt;Edge&gt;&amp;&amp; edge) 来利用移动语义吗?
    【解决方案3】:

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

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

    你可以做几件事:

    1. std::move() 指向向量的指针,这将导致之前的副本失效(参见:移动语义)。
    2. 如果您希望多个对象保存指针的副本,请使用 std::shared_ptr。由于看起来您想要拥有多个指针副本,因此这可能是您想要的方向。

    在您的情况下,阅读 std::weak_ptr 可能是值得的,因为我认为您可能希望对象相互引用,而不创建循环引用。弱指针在 shared_ptr 上工作而不会实际增加引用计数,并且在某些情况下很重要,因为您希望确保对象不会长时间挂在引用上并造成内存泄漏。

    希望这会有所帮助。

    【讨论】:

      【解决方案4】:

      我根本不会使用 智能指针。我认为最简单的方法是将所有 nodesedges 作为 values 存储在主 vectors 中,然后使用原始指针来表达它们的关系。

      您不需要 智能指针,因为 向量 管理您的 节点 的生命周期和销毁> 所以没有其他东西需要拥有它们。

      使用此方案要记住的最重要的一点是,您必须永远在使用的数据结构之后更改 vector 的大小已创建指向其元素的指针。

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

      所以是这样的:

      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]);
      
      }
      

      【讨论】:

        猜你喜欢
        • 2017-12-03
        • 1970-01-01
        • 2011-08-12
        • 1970-01-01
        • 1970-01-01
        • 2019-01-27
        • 1970-01-01
        • 1970-01-01
        • 2011-04-22
        相关资源
        最近更新 更多